summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/usb
diff options
context:
space:
mode:
authorChristian Mauderer <Christian.Mauderer@embedded-brains.de>2016-11-14 13:30:48 +0100
committerChristian Mauderer <Christian.Mauderer@embedded-brains.de>2017-01-17 12:50:57 +0100
commitd145449b7426cf070c5b8a526785cc00ea45c4c6 (patch)
treefdd21cc557b90a908c89de2e1b5565486ad4439c /freebsd/sys/dev/usb
parentfirmware: Port to RTEMS. (diff)
downloadrtems-libbsd-d145449b7426cf070c5b8a526785cc00ea45c4c6.tar.bz2
Import USB and USB WLAN from FreeBSD.
Diffstat (limited to 'freebsd/sys/dev/usb')
-rw-r--r--freebsd/sys/dev/usb/net/if_aue.c1068
-rw-r--r--freebsd/sys/dev/usb/net/if_auereg.h220
-rw-r--r--freebsd/sys/dev/usb/net/if_axe.c1507
-rw-r--r--freebsd/sys/dev/usb/net/if_axereg.h363
-rw-r--r--freebsd/sys/dev/usb/net/if_axge.c1056
-rw-r--r--freebsd/sys/dev/usb/net/if_axgereg.h209
-rw-r--r--freebsd/sys/dev/usb/net/if_cdce.c1583
-rw-r--r--freebsd/sys/dev/usb/net/if_cdcereg.h105
-rw-r--r--freebsd/sys/dev/usb/net/if_cue.c657
-rw-r--r--freebsd/sys/dev/usb/net/if_cuereg.h132
-rw-r--r--freebsd/sys/dev/usb/net/if_ipheth.c548
-rw-r--r--freebsd/sys/dev/usb/net/if_iphethvar.h84
-rw-r--r--freebsd/sys/dev/usb/net/if_kue.c714
-rw-r--r--freebsd/sys/dev/usb/net/if_kuefw.h685
-rw-r--r--freebsd/sys/dev/usb/net/if_kuereg.h141
-rw-r--r--freebsd/sys/dev/usb/net/if_mos.c1032
-rw-r--r--freebsd/sys/dev/usb/net/if_mosreg.h176
-rw-r--r--freebsd/sys/dev/usb/net/if_rue.c924
-rw-r--r--freebsd/sys/dev/usb/net/if_ruereg.h178
-rw-r--r--freebsd/sys/dev/usb/net/if_smsc.c1931
-rw-r--r--freebsd/sys/dev/usb/net/if_smscreg.h277
-rw-r--r--freebsd/sys/dev/usb/net/if_udav.c883
-rw-r--r--freebsd/sys/dev/usb/net/if_udavreg.h167
-rw-r--r--freebsd/sys/dev/usb/net/if_ure.c1259
-rw-r--r--freebsd/sys/dev/usb/net/if_urereg.h440
-rw-r--r--freebsd/sys/dev/usb/net/ruephy.c222
-rw-r--r--freebsd/sys/dev/usb/net/ruephyreg.h38
-rw-r--r--freebsd/sys/dev/usb/net/usb_ethernet.c651
-rw-r--r--freebsd/sys/dev/usb/net/usb_ethernet.h127
-rw-r--r--freebsd/sys/dev/usb/wlan/if_rsu.c3796
-rw-r--r--freebsd/sys/dev/usb/wlan/if_rsureg.h895
-rw-r--r--freebsd/sys/dev/usb/wlan/if_rum.c3321
-rw-r--r--freebsd/sys/dev/usb/wlan/if_rumfw.h213
-rw-r--r--freebsd/sys/dev/usb/wlan/if_rumreg.h307
-rw-r--r--freebsd/sys/dev/usb/wlan/if_rumvar.h186
-rw-r--r--freebsd/sys/dev/usb/wlan/if_run.c6339
-rw-r--r--freebsd/sys/dev/usb/wlan/if_runreg.h1669
-rw-r--r--freebsd/sys/dev/usb/wlan/if_runvar.h269
-rw-r--r--freebsd/sys/dev/usb/wlan/if_uath.c2800
-rw-r--r--freebsd/sys/dev/usb/wlan/if_uathreg.h601
-rw-r--r--freebsd/sys/dev/usb/wlan/if_uathvar.h245
-rw-r--r--freebsd/sys/dev/usb/wlan/if_upgt.c2353
-rw-r--r--freebsd/sys/dev/usb/wlan/if_upgtvar.h480
-rw-r--r--freebsd/sys/dev/usb/wlan/if_ural.c2246
-rw-r--r--freebsd/sys/dev/usb/wlan/if_uralreg.h211
-rw-r--r--freebsd/sys/dev/usb/wlan/if_uralvar.h136
-rw-r--r--freebsd/sys/dev/usb/wlan/if_urtw.c4406
-rw-r--r--freebsd/sys/dev/usb/wlan/if_urtwreg.h433
-rw-r--r--freebsd/sys/dev/usb/wlan/if_urtwvar.h186
-rw-r--r--freebsd/sys/dev/usb/wlan/if_zyd.c2924
-rw-r--r--freebsd/sys/dev/usb/wlan/if_zydfw.h1144
-rw-r--r--freebsd/sys/dev/usb/wlan/if_zydreg.h1319
52 files changed, 53856 insertions, 0 deletions
diff --git a/freebsd/sys/dev/usb/net/if_aue.c b/freebsd/sys/dev/usb/net/if_aue.c
new file mode 100644
index 00000000..b79959f9
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_aue.c
@@ -0,0 +1,1068 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * 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 uether_do_request(). Packet
+ * transfers are done using usbd_transfer() and friends.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR aue_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_auereg.h>
+
+#ifdef USB_DEBUG
+static int aue_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue");
+SYSCTL_INT(_hw_usb_aue, OID_AUTO, debug, CTLFLAG_RWTUN, &aue_debug, 0,
+ "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const STRUCT_USB_HOST_ID aue_devs[] = {
+#define AUE_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+ AUE_DEV(3COM, 3C460B, AUE_FLAG_PII),
+ AUE_DEV(ABOCOM, DSB650TX_PNA, 0),
+ AUE_DEV(ABOCOM, UFE1000, AUE_FLAG_LSYS),
+ AUE_DEV(ABOCOM, XX10, 0),
+ AUE_DEV(ABOCOM, XX1, AUE_FLAG_PNA | AUE_FLAG_PII),
+ AUE_DEV(ABOCOM, XX2, AUE_FLAG_PII),
+ AUE_DEV(ABOCOM, XX4, AUE_FLAG_PNA),
+ AUE_DEV(ABOCOM, XX5, AUE_FLAG_PNA),
+ AUE_DEV(ABOCOM, XX6, AUE_FLAG_PII),
+ AUE_DEV(ABOCOM, XX7, AUE_FLAG_PII),
+ AUE_DEV(ABOCOM, XX8, AUE_FLAG_PII),
+ AUE_DEV(ABOCOM, XX9, AUE_FLAG_PNA),
+ AUE_DEV(ACCTON, SS1001, AUE_FLAG_PII),
+ AUE_DEV(ACCTON, USB320_EC, 0),
+ AUE_DEV(ADMTEK, PEGASUSII_2, AUE_FLAG_PII),
+ AUE_DEV(ADMTEK, PEGASUSII_3, AUE_FLAG_PII),
+ AUE_DEV(ADMTEK, PEGASUSII_4, AUE_FLAG_PII),
+ AUE_DEV(ADMTEK, PEGASUSII, AUE_FLAG_PII),
+ AUE_DEV(ADMTEK, PEGASUS, AUE_FLAG_PNA | AUE_FLAG_DUAL_PHY),
+ AUE_DEV(AEI, FASTETHERNET, AUE_FLAG_PII),
+ AUE_DEV(ALLIEDTELESYN, ATUSB100, AUE_FLAG_PII),
+ AUE_DEV(ATEN, UC110T, AUE_FLAG_PII),
+ AUE_DEV(BELKIN, USB2LAN, AUE_FLAG_PII),
+ AUE_DEV(BILLIONTON, USB100, 0),
+ AUE_DEV(BILLIONTON, USBE100, AUE_FLAG_PII),
+ AUE_DEV(BILLIONTON, USBEL100, 0),
+ AUE_DEV(BILLIONTON, USBLP100, AUE_FLAG_PNA),
+ AUE_DEV(COREGA, FETHER_USB_TXS, AUE_FLAG_PII),
+ AUE_DEV(COREGA, FETHER_USB_TX, 0),
+ AUE_DEV(DLINK, DSB650TX1, AUE_FLAG_LSYS),
+ AUE_DEV(DLINK, DSB650TX2, AUE_FLAG_LSYS | AUE_FLAG_PII),
+ AUE_DEV(DLINK, DSB650TX3, AUE_FLAG_LSYS | AUE_FLAG_PII),
+ AUE_DEV(DLINK, DSB650TX4, AUE_FLAG_LSYS | AUE_FLAG_PII),
+ AUE_DEV(DLINK, DSB650TX_PNA, AUE_FLAG_PNA),
+ AUE_DEV(DLINK, DSB650TX, AUE_FLAG_LSYS),
+ AUE_DEV(DLINK, DSB650, AUE_FLAG_LSYS),
+ AUE_DEV(ELCON, PLAN, AUE_FLAG_PNA | AUE_FLAG_PII),
+ AUE_DEV(ELECOM, LDUSB20, AUE_FLAG_PII),
+ AUE_DEV(ELECOM, LDUSBLTX, AUE_FLAG_PII),
+ AUE_DEV(ELECOM, LDUSBTX0, 0),
+ AUE_DEV(ELECOM, LDUSBTX1, AUE_FLAG_LSYS),
+ AUE_DEV(ELECOM, LDUSBTX2, 0),
+ AUE_DEV(ELECOM, LDUSBTX3, AUE_FLAG_LSYS),
+ AUE_DEV(ELSA, USB2ETHERNET, 0),
+ AUE_DEV(GIGABYTE, GNBR402W, 0),
+ AUE_DEV(HAWKING, UF100, AUE_FLAG_PII),
+ AUE_DEV(HP, HN210E, AUE_FLAG_PII),
+ AUE_DEV(IODATA, USBETTXS, AUE_FLAG_PII),
+ AUE_DEV(IODATA, USBETTX, 0),
+ AUE_DEV(KINGSTON, KNU101TX, 0),
+ AUE_DEV(LINKSYS, USB100H1, AUE_FLAG_LSYS | AUE_FLAG_PNA),
+ AUE_DEV(LINKSYS, USB100TX, AUE_FLAG_LSYS),
+ AUE_DEV(LINKSYS, USB10TA, AUE_FLAG_LSYS),
+ AUE_DEV(LINKSYS, USB10TX1, AUE_FLAG_LSYS | AUE_FLAG_PII),
+ AUE_DEV(LINKSYS, USB10TX2, AUE_FLAG_LSYS | AUE_FLAG_PII),
+ AUE_DEV(LINKSYS, USB10T, AUE_FLAG_LSYS),
+ AUE_DEV(MELCO, LUA2TX5, AUE_FLAG_PII),
+ AUE_DEV(MELCO, LUATX1, 0),
+ AUE_DEV(MELCO, LUATX5, 0),
+ AUE_DEV(MICROSOFT, MN110, AUE_FLAG_PII),
+ AUE_DEV(NETGEAR, FA101, AUE_FLAG_PII),
+ AUE_DEV(SIEMENS, SPEEDSTREAM, AUE_FLAG_PII),
+ AUE_DEV(SIIG2, USBTOETHER, AUE_FLAG_PII),
+ AUE_DEV(SMARTBRIDGES, SMARTNIC, AUE_FLAG_PII),
+ AUE_DEV(SMC, 2202USB, 0),
+ AUE_DEV(SMC, 2206USB, AUE_FLAG_PII),
+ AUE_DEV(SOHOWARE, NUB100, 0),
+ AUE_DEV(SOHOWARE, NUB110, AUE_FLAG_PII),
+#undef AUE_DEV
+};
+
+/* prototypes */
+
+static device_probe_t aue_probe;
+static device_attach_t aue_attach;
+static device_detach_t aue_detach;
+static miibus_readreg_t aue_miibus_readreg;
+static miibus_writereg_t aue_miibus_writereg;
+static miibus_statchg_t aue_miibus_statchg;
+
+static usb_callback_t aue_intr_callback;
+static usb_callback_t aue_bulk_read_callback;
+static usb_callback_t aue_bulk_write_callback;
+
+static uether_fn_t aue_attach_post;
+static uether_fn_t aue_init;
+static uether_fn_t aue_stop;
+static uether_fn_t aue_start;
+static uether_fn_t aue_tick;
+static uether_fn_t aue_setmulti;
+static uether_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 uint16_t aue_eeprom_getword(struct aue_softc *, int);
+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 usb_config aue_config[AUE_N_TRANSFER] = {
+
+ [AUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = (MCLBYTES + 2),
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = aue_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+
+ [AUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = aue_bulk_read_callback,
+ },
+
+ [AUE_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = 0, /* use wMaxPacketSize */
+ .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),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, aue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, aue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, aue_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t aue_driver = {
+ .name = "aue",
+ .methods = aue_methods,
+ .size = sizeof(struct aue_softc)
+};
+
+static devclass_t aue_devclass;
+
+DRIVER_MODULE(aue, uhub, 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);
+MODULE_VERSION(aue, 1);
+USB_PNP_HOST_INFO(aue_devs);
+
+static const struct usb_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 usb_device_request req;
+ usb_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 = uether_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 usb_device_request req;
+ usb_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 = uether_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 usb_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 (uether_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 usb_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 (uether_do_request(&sc->sc_ue, &req, &val, 1000)) {
+ /* error ignored */
+ }
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+static uint16_t
+aue_eeprom_getword(struct aue_softc *sc, int addr)
+{
+ int i;
+
+ 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 (uether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "EEPROM read timed out\n");
+
+ return (aue_csr_read_2(sc, AUE_EE_DATA));
+}
+
+/*
+ * Read station address(offset 0) from the EEPROM.
+ */
+static void
+aue_read_mac(struct aue_softc *sc, uint8_t *eaddr)
+{
+ int i, offset;
+ uint16_t word;
+
+ for (i = 0, offset = 0; i < ETHER_ADDR_LEN / 2; i++) {
+ word = aue_eeprom_getword(sc, offset + i);
+ eaddr[i * 2] = (uint8_t)word;
+ eaddr[i * 2 + 1] = (uint8_t)(word >> 8);
+ }
+}
+
+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 (uether_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 (uether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "MII write 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 usb_ether *ue)
+{
+ struct aue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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_maddr_rlock(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_maddr_runlock(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 (uether_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 used to force all of the GPIO pins low first and then
+ * enable the ones we want. This has been changed to better
+ * match the ADMtek's reference design to avoid setting the
+ * power-down configuration line of the PHY at the same time
+ * it is reset.
+ */
+ 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_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: */
+ uether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+aue_attach_post(struct usb_ether *ue)
+{
+ struct aue_softc *sc = uether_getsc(ue);
+
+ /* reset the adapter */
+ aue_reset(sc);
+
+ /* get station address from the EEPROM */
+ aue_read_mac(sc, ue->ue_eaddr);
+}
+
+/*
+ * Probe for a Pegasus chip.
+ */
+static int
+aue_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_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 (usbd_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 usb_attach_arg *uaa = device_get_ivars(dev);
+ struct aue_softc *sc = device_get_softc(dev);
+ struct usb_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_usb_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = AUE_IFACE_IDX;
+ error = usbd_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 = uether_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 usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, AUE_N_TRANSFER);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+aue_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct aue_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct aue_intrpkt pkt;
+ struct usb_page_cache *pc;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) &&
+ actlen >= (int)sizeof(pkt)) {
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, &pkt, sizeof(pkt));
+
+ if (pkt.aue_txstat0)
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ if (pkt.aue_txstat0 & (AUE_TXSTAT0_LATECOLL |
+ AUE_TXSTAT0_EXCESSCOLL))
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ return;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct aue_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = uether_getifp(ue);
+ struct aue_rxpkt stat;
+ struct usb_page_cache *pc;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+ pc = usbd_xfer_get_frame(xfer, 0);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "received %d bytes\n", actlen);
+
+ if (sc->sc_flags & AUE_FLAG_VER_2) {
+
+ if (actlen == 0) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ } else {
+
+ if (actlen <= (int)(sizeof(stat) + ETHER_CRC_LEN)) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ usbd_copy_out(pc, 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) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ /* No errors; receive the packet. */
+ actlen -= (sizeof(stat) + ETHER_CRC_LEN);
+ }
+ uether_rxbuf(ue, pc, 0, actlen);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct aue_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ uint8_t buf[2];
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+ pc = usbd_xfer_get_frame(xfer, 0);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer of %d bytes complete\n", actlen);
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /* 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) {
+
+ usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);
+
+ usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
+
+ } else {
+
+ usbd_xfer_set_frame_len(xfer, 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);
+
+ usbd_copy_in(pc, 0, buf, 2);
+ usbd_m_copy_in(pc, 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);
+
+ usbd_transfer_submit(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_tick(struct usb_ether *ue)
+{
+ struct aue_softc *sc = uether_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 usb_ether *ue)
+{
+ struct aue_softc *sc = uether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[AUE_INTR_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[AUE_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[AUE_BULK_DT_WR]);
+}
+
+static void
+aue_init(struct usb_ether *ue)
+{
+ struct aue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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);
+
+ usbd_xfer_set_stall(sc->sc_xfer[AUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ aue_start(ue);
+}
+
+static void
+aue_setpromisc(struct usb_ether *ue)
+{
+ struct aue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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);
+ struct mii_softc *miisc;
+ int error;
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~AUE_FLAG_LINK;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ error = mii_mediachg(mii);
+ return (error);
+}
+
+/*
+ * 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);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ AUE_UNLOCK(sc);
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+aue_stop(struct usb_ether *ue)
+{
+ struct aue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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:
+ */
+ usbd_transfer_stop(sc->sc_xfer[AUE_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[AUE_BULK_DT_RD]);
+ usbd_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);
+}
diff --git a/freebsd/sys/dev/usb/net/if_auereg.h b/freebsd/sys/dev/usb/net/if_auereg.h
new file mode 100644
index 00000000..4d0843eb
--- /dev/null
+++ b/freebsd/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 usbd_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) uether_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 usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb_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/freebsd/sys/dev/usb/net/if_axe.c b/freebsd/sys/dev/usb/net/if_axe.c
new file mode 100644
index 00000000..35b9ca2e
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_axe.c
@@ -0,0 +1,1507 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * 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 <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/if_vlan_var.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR axe_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.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
+
+#define AXE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
+
+#ifdef USB_DEBUG
+static int axe_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe");
+SYSCTL_INT(_hw_usb_axe, OID_AUTO, debug, CTLFLAG_RWTUN, &axe_debug, 0,
+ "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const STRUCT_USB_HOST_ID axe_devs[] = {
+#define AXE_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+ AXE_DEV(ABOCOM, UF200, 0),
+ AXE_DEV(ACERCM, EP1427X2, 0),
+ AXE_DEV(APPLE, ETHERNET, AXE_FLAG_772),
+ AXE_DEV(ASIX, AX88172, 0),
+ AXE_DEV(ASIX, AX88178, AXE_FLAG_178),
+ AXE_DEV(ASIX, AX88772, AXE_FLAG_772),
+ AXE_DEV(ASIX, AX88772A, AXE_FLAG_772A),
+ AXE_DEV(ASIX, AX88772B, AXE_FLAG_772B),
+ AXE_DEV(ASIX, AX88772B_1, AXE_FLAG_772B),
+ AXE_DEV(ATEN, UC210T, 0),
+ AXE_DEV(BELKIN, F5D5055, AXE_FLAG_178),
+ AXE_DEV(BILLIONTON, USB2AR, 0),
+ AXE_DEV(CISCOLINKSYS, USB200MV2, AXE_FLAG_772A),
+ AXE_DEV(COREGA, FETHER_USB2_TX, 0),
+ AXE_DEV(DLINK, DUBE100, 0),
+ AXE_DEV(DLINK, DUBE100B1, AXE_FLAG_772),
+ AXE_DEV(DLINK, DUBE100C1, AXE_FLAG_772B),
+ AXE_DEV(GOODWAY, GWUSB2E, 0),
+ AXE_DEV(IODATA, ETGUS2, AXE_FLAG_178),
+ AXE_DEV(JVC, MP_PRX1, 0),
+ AXE_DEV(LENOVO, ETHERNET, AXE_FLAG_772B),
+ AXE_DEV(LINKSYS2, USB200M, 0),
+ AXE_DEV(LINKSYS4, USB1000, AXE_FLAG_178),
+ AXE_DEV(LOGITEC, LAN_GTJU2A, AXE_FLAG_178),
+ AXE_DEV(MELCO, LUAU2KTX, 0),
+ AXE_DEV(MELCO, LUA3U2AGT, AXE_FLAG_178),
+ AXE_DEV(NETGEAR, FA120, 0),
+ AXE_DEV(OQO, ETHER01PLUS, AXE_FLAG_772),
+ AXE_DEV(PLANEX3, GU1000T, AXE_FLAG_178),
+ AXE_DEV(SITECOM, LN029, 0),
+ AXE_DEV(SITECOMEU, LN028, AXE_FLAG_178),
+ AXE_DEV(SITECOMEU, LN031, AXE_FLAG_178),
+ AXE_DEV(SYSTEMTALKS, SGCX2UL, 0),
+#undef AXE_DEV
+};
+
+static device_probe_t axe_probe;
+static device_attach_t axe_attach;
+static device_detach_t axe_detach;
+
+static usb_callback_t axe_bulk_read_callback;
+static usb_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 uether_fn_t axe_attach_post;
+static uether_fn_t axe_init;
+static uether_fn_t axe_stop;
+static uether_fn_t axe_start;
+static uether_fn_t axe_tick;
+static uether_fn_t axe_setmulti;
+static uether_fn_t axe_setpromisc;
+
+static int axe_attach_post_sub(struct usb_ether *);
+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 void axe_ax88772_phywake(struct axe_softc *);
+static void axe_ax88772a_init(struct axe_softc *);
+static void axe_ax88772b_init(struct axe_softc *);
+static int axe_get_phyno(struct axe_softc *, int);
+static int axe_ioctl(struct ifnet *, u_long, caddr_t);
+static int axe_rx_frame(struct usb_ether *, struct usb_page_cache *, int);
+static int axe_rxeof(struct usb_ether *, struct usb_page_cache *,
+ unsigned int offset, unsigned int, struct axe_csum_hdr *);
+static void axe_csum_cfg(struct usb_ether *);
+
+static const struct usb_config axe_config[AXE_N_TRANSFER] = {
+
+ [AXE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .frames = 16,
+ .bufsize = 16 * MCLBYTES,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = axe_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+
+ [AXE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = 16384, /* bytes */
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = axe_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ },
+};
+
+static const struct ax88772b_mfb ax88772b_mfb_table[] = {
+ { 0x8000, 0x8001, 2048 },
+ { 0x8100, 0x8147, 4096},
+ { 0x8200, 0x81EB, 6144},
+ { 0x8300, 0x83D7, 8192},
+ { 0x8400, 0x851E, 16384},
+ { 0x8500, 0x8666, 20480},
+ { 0x8600, 0x87AE, 24576},
+ { 0x8700, 0x8A3D, 32768}
+};
+
+static device_method_t axe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, axe_probe),
+ DEVMETHOD(device_attach, axe_attach),
+ DEVMETHOD(device_detach, axe_detach),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, axe_miibus_readreg),
+ DEVMETHOD(miibus_writereg, axe_miibus_writereg),
+ DEVMETHOD(miibus_statchg, axe_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t axe_driver = {
+ .name = "axe",
+ .methods = axe_methods,
+ .size = sizeof(struct axe_softc),
+};
+
+static devclass_t axe_devclass;
+
+DRIVER_MODULE(axe, uhub, 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);
+MODULE_VERSION(axe, 1);
+USB_PNP_HOST_INFO(axe_devs);
+
+static const struct usb_ether_methods axe_ue_methods = {
+ .ue_attach_post = axe_attach_post,
+ .ue_attach_post_sub = axe_attach_post_sub,
+ .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 usb_device_request req;
+ usb_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 = uether_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;
+
+ 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 (AXE_IS_772(sc) && 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 = htole32(val);
+ 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 = uether_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 (AXE_IS_178_FAMILY(sc)) {
+ if ((IFM_OPTIONS(mii->mii_media_active) &
+ IFM_ETH_TXPAUSE) != 0)
+ val |= AXE_178_MEDIA_TXFLOW_CONTROL_EN;
+ if ((IFM_OPTIONS(mii->mii_media_active) &
+ IFM_ETH_RXPAUSE) != 0)
+ val |= AXE_178_MEDIA_RXFLOW_CONTROL_EN;
+ }
+ }
+ if (AXE_IS_178_FAMILY(sc)) {
+ 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);
+ struct mii_softc *miisc;
+ int error;
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ 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);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ AXE_UNLOCK(sc);
+}
+
+static void
+axe_setmulti(struct usb_ether *ue)
+{
+ struct axe_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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_maddr_rlock(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_maddr_runlock(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);
+}
+
+#define AXE_GPIO_WRITE(x, y) do { \
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, (x), NULL); \
+ uether_pause(ue, (y)); \
+} while (0)
+
+static void
+axe_ax88178_init(struct axe_softc *sc)
+{
+ struct usb_ether *ue;
+ int gpio0, ledmode, phymode;
+ uint16_t eeprom, val;
+
+ ue = &sc->sc_ue;
+ 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 = AXE_PHY_MODE_MARVELL;
+ gpio0 = 1;
+ ledmode = 0;
+ } else {
+ phymode = eeprom & 0x7f;
+ gpio0 = (eeprom & 0x80) ? 0 : 1;
+ ledmode = eeprom >> 8;
+ }
+
+ if (bootverbose)
+ device_printf(sc->sc_ue.ue_dev,
+ "EEPROM data : 0x%04x, phymode : 0x%02x\n", eeprom,
+ phymode);
+ /* Program GPIOs depending on PHY hardware. */
+ switch (phymode) {
+ case AXE_PHY_MODE_MARVELL:
+ if (gpio0 == 1) {
+ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0_EN,
+ hz / 32);
+ AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN,
+ hz / 32);
+ AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2_EN, hz / 4);
+ AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN,
+ hz / 32);
+ } else {
+ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 |
+ AXE_GPIO1_EN, hz / 3);
+ if (ledmode == 1) {
+ AXE_GPIO_WRITE(AXE_GPIO1_EN, hz / 3);
+ AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN,
+ hz / 3);
+ } else {
+ AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN |
+ AXE_GPIO2 | AXE_GPIO2_EN, hz / 32);
+ AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN |
+ AXE_GPIO2_EN, hz / 4);
+ AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN |
+ AXE_GPIO2 | AXE_GPIO2_EN, hz / 32);
+ }
+ }
+ break;
+ case AXE_PHY_MODE_CICADA:
+ case AXE_PHY_MODE_CICADA_V2:
+ case AXE_PHY_MODE_CICADA_V2_ASIX:
+ if (gpio0 == 1)
+ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0 |
+ AXE_GPIO0_EN, hz / 32);
+ else
+ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 |
+ AXE_GPIO1_EN, hz / 32);
+ break;
+ case AXE_PHY_MODE_AGERE:
+ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 |
+ AXE_GPIO1_EN, hz / 32);
+ AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 |
+ AXE_GPIO2_EN, hz / 32);
+ AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2_EN, hz / 4);
+ AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 |
+ AXE_GPIO2_EN, hz / 32);
+ break;
+ case AXE_PHY_MODE_REALTEK_8211CL:
+ case AXE_PHY_MODE_REALTEK_8211BN:
+ case AXE_PHY_MODE_REALTEK_8251CL:
+ val = gpio0 == 1 ? AXE_GPIO0 | AXE_GPIO0_EN :
+ AXE_GPIO1 | AXE_GPIO1_EN;
+ AXE_GPIO_WRITE(val, hz / 32);
+ AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32);
+ AXE_GPIO_WRITE(val | AXE_GPIO2_EN, hz / 4);
+ AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32);
+ if (phymode == AXE_PHY_MODE_REALTEK_8211CL) {
+ axe_miibus_writereg(ue->ue_dev, sc->sc_phyno,
+ 0x1F, 0x0005);
+ axe_miibus_writereg(ue->ue_dev, sc->sc_phyno,
+ 0x0C, 0x0000);
+ val = axe_miibus_readreg(ue->ue_dev, sc->sc_phyno,
+ 0x0001);
+ axe_miibus_writereg(ue->ue_dev, sc->sc_phyno,
+ 0x01, val | 0x0080);
+ axe_miibus_writereg(ue->ue_dev, sc->sc_phyno,
+ 0x1F, 0x0000);
+ }
+ break;
+ default:
+ /* Unknown PHY model or no need to program GPIOs. */
+ break;
+ }
+
+ /* soft reset */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
+ uether_pause(ue, hz / 4);
+
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL);
+ uether_pause(ue, hz / 4);
+ /* Enable MII/GMII/RGMII interface to work with external PHY. */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL);
+ uether_pause(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);
+ uether_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);
+ uether_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);
+ uether_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);
+ uether_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);
+ uether_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);
+ }
+
+ uether_pause(&sc->sc_ue, hz / 4);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_ax88772_phywake(struct axe_softc *sc)
+{
+ struct usb_ether *ue;
+
+ ue = &sc->sc_ue;
+ if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) {
+ /* Manually select internal(embedded) PHY - MAC mode. */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB |
+ AXE_SW_PHY_SELECT_EMBEDDED | AXE_SW_PHY_SELECT_SS_MII,
+ NULL);
+ uether_pause(&sc->sc_ue, hz / 32);
+ } else {
+ /*
+ * Manually select external PHY - MAC mode.
+ * Reverse MII/RMII is for AX88772A PHY mode.
+ */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB |
+ AXE_SW_PHY_SELECT_EXT | AXE_SW_PHY_SELECT_SS_MII, NULL);
+ uether_pause(&sc->sc_ue, hz / 32);
+ }
+ /* Take PHY out of power down. */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD |
+ AXE_SW_RESET_IPRL, NULL);
+ uether_pause(&sc->sc_ue, hz / 4);
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL);
+ uether_pause(&sc->sc_ue, hz);
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
+ uether_pause(&sc->sc_ue, hz / 32);
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL);
+ uether_pause(&sc->sc_ue, hz / 32);
+}
+
+static void
+axe_ax88772a_init(struct axe_softc *sc)
+{
+ struct usb_ether *ue;
+
+ ue = &sc->sc_ue;
+ /* Reload EEPROM. */
+ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32);
+ axe_ax88772_phywake(sc);
+ /* Stop MAC. */
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_ax88772b_init(struct axe_softc *sc)
+{
+ struct usb_ether *ue;
+ uint16_t eeprom;
+ uint8_t *eaddr;
+ int i;
+
+ ue = &sc->sc_ue;
+ /* Reload EEPROM. */
+ AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32);
+ /*
+ * Save PHY power saving configuration(high byte) and
+ * clear EEPROM checksum value(low byte).
+ */
+ axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_PHY_PWRCFG, &eeprom);
+ sc->sc_pwrcfg = le16toh(eeprom) & 0xFF00;
+
+ /*
+ * Auto-loaded default station address from internal ROM is
+ * 00:00:00:00:00:00 such that an explicit access to EEPROM
+ * is required to get real station address.
+ */
+ eaddr = ue->ue_eaddr;
+ for (i = 0; i < ETHER_ADDR_LEN / 2; i++) {
+ axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_NODE_ID + i,
+ &eeprom);
+ eeprom = le16toh(eeprom);
+ *eaddr++ = (uint8_t)(eeprom & 0xFF);
+ *eaddr++ = (uint8_t)((eeprom >> 8) & 0xFF);
+ }
+ /* Wakeup PHY. */
+ axe_ax88772_phywake(sc);
+ /* Stop MAC. */
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+#undef AXE_GPIO_WRITE
+
+static void
+axe_reset(struct axe_softc *sc)
+{
+ struct usb_config_descriptor *cd;
+ usb_error_t err;
+
+ cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev);
+
+ err = usbd_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. */
+ uether_pause(&sc->sc_ue, hz / 100);
+
+ /* Reinitialize controller to achieve full reset. */
+ if (sc->sc_flags & AXE_FLAG_178)
+ axe_ax88178_init(sc);
+ else if (sc->sc_flags & AXE_FLAG_772)
+ axe_ax88772_init(sc);
+ else if (sc->sc_flags & AXE_FLAG_772A)
+ axe_ax88772a_init(sc);
+ else if (sc->sc_flags & AXE_FLAG_772B)
+ axe_ax88772b_init(sc);
+}
+
+static void
+axe_attach_post(struct usb_ether *ue)
+{
+ struct axe_softc *sc = uether_getsc(ue);
+
+ /*
+ * Load PHY indexes first. Needed by axe_xxx_init().
+ */
+ axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs);
+ if (bootverbose)
+ device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n",
+ sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]);
+ 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;
+ }
+
+ /* Initialize controller and get station address. */
+ if (sc->sc_flags & AXE_FLAG_178) {
+ axe_ax88178_init(sc);
+ axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
+ } else if (sc->sc_flags & AXE_FLAG_772) {
+ axe_ax88772_init(sc);
+ axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
+ } else if (sc->sc_flags & AXE_FLAG_772A) {
+ axe_ax88772a_init(sc);
+ axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
+ } else if (sc->sc_flags & AXE_FLAG_772B) {
+ axe_ax88772b_init(sc);
+ } else
+ axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
+
+ /*
+ * Fetch IPG values.
+ */
+ if (sc->sc_flags & (AXE_FLAG_772A | AXE_FLAG_772B)) {
+ /* Set IPG values. */
+ sc->sc_ipgs[0] = 0x15;
+ sc->sc_ipgs[1] = 0x16;
+ sc->sc_ipgs[2] = 0x1A;
+ } else
+ axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs);
+}
+
+static int
+axe_attach_post_sub(struct usb_ether *ue)
+{
+ struct axe_softc *sc;
+ struct ifnet *ifp;
+ u_int adv_pause;
+ int error;
+
+ sc = uether_getsc(ue);
+ ifp = ue->ue_ifp;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_start = uether_start;
+ ifp->if_ioctl = axe_ioctl;
+ ifp->if_init = uether_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+ ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ if (AXE_IS_178_FAMILY(sc))
+ ifp->if_capabilities |= IFCAP_VLAN_MTU;
+ if (sc->sc_flags & AXE_FLAG_772B) {
+ ifp->if_capabilities |= IFCAP_TXCSUM | IFCAP_RXCSUM;
+ ifp->if_hwassist = AXE_CSUM_FEATURES;
+ /*
+ * Checksum offloading of AX88772B also works with VLAN
+ * tagged frames but there is no way to take advantage
+ * of the feature because vlan(4) assumes
+ * IFCAP_VLAN_HWTAGGING is prerequisite condition to
+ * support checksum offloading with VLAN. VLAN hardware
+ * tagging support of AX88772B is very limited so it's
+ * not possible to announce IFCAP_VLAN_HWTAGGING.
+ */
+ }
+ ifp->if_capenable = ifp->if_capabilities;
+ if (sc->sc_flags & (AXE_FLAG_772A | AXE_FLAG_772B | AXE_FLAG_178))
+ adv_pause = MIIF_DOPAUSE;
+ else
+ adv_pause = 0;
+ mtx_lock(&Giant);
+ error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
+ uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
+ BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, adv_pause);
+ mtx_unlock(&Giant);
+
+ return (error);
+}
+
+/*
+ * Probe for a AX88172 chip.
+ */
+static int
+axe_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_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 (usbd_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 usb_attach_arg *uaa = device_get_ivars(dev);
+ struct axe_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = AXE_IFACE_IDX;
+ error = usbd_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 = uether_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 usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+#if (AXE_BULK_BUF_SIZE >= 0x10000)
+#error "Please update axe_bulk_read_callback()!"
+#endif
+
+static void
+axe_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct axe_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct usb_page_cache *pc;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ pc = usbd_xfer_get_frame(xfer, 0);
+ axe_rx_frame(ue, pc, actlen);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n", usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static int
+axe_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen)
+{
+ struct axe_softc *sc;
+ struct axe_sframe_hdr hdr;
+ struct axe_csum_hdr csum_hdr;
+ int error, len, pos;
+
+ sc = uether_getsc(ue);
+ pos = 0;
+ len = 0;
+ error = 0;
+ if ((sc->sc_flags & AXE_FLAG_STD_FRAME) != 0) {
+ while (pos < actlen) {
+ if ((int)(pos + sizeof(hdr)) > actlen) {
+ /* too little data */
+ error = EINVAL;
+ break;
+ }
+ usbd_copy_out(pc, pos, &hdr, sizeof(hdr));
+
+ if ((hdr.len ^ hdr.ilen) != sc->sc_lenmask) {
+ /* we lost sync */
+ error = EINVAL;
+ break;
+ }
+ pos += sizeof(hdr);
+ len = le16toh(hdr.len);
+ if (pos + len > actlen) {
+ /* invalid length */
+ error = EINVAL;
+ break;
+ }
+ axe_rxeof(ue, pc, pos, len, NULL);
+ pos += len + (len % 2);
+ }
+ } else if ((sc->sc_flags & AXE_FLAG_CSUM_FRAME) != 0) {
+ while (pos < actlen) {
+ if ((int)(pos + sizeof(csum_hdr)) > actlen) {
+ /* too little data */
+ error = EINVAL;
+ break;
+ }
+ usbd_copy_out(pc, pos, &csum_hdr, sizeof(csum_hdr));
+
+ csum_hdr.len = le16toh(csum_hdr.len);
+ csum_hdr.ilen = le16toh(csum_hdr.ilen);
+ csum_hdr.cstatus = le16toh(csum_hdr.cstatus);
+ if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^
+ AXE_CSUM_RXBYTES(csum_hdr.ilen)) !=
+ sc->sc_lenmask) {
+ /* we lost sync */
+ error = EINVAL;
+ break;
+ }
+ /*
+ * Get total transferred frame length including
+ * checksum header. The length should be multiple
+ * of 4.
+ */
+ len = sizeof(csum_hdr) + AXE_CSUM_RXBYTES(csum_hdr.len);
+ len = (len + 3) & ~3;
+ if (pos + len > actlen) {
+ /* invalid length */
+ error = EINVAL;
+ break;
+ }
+ axe_rxeof(ue, pc, pos + sizeof(csum_hdr),
+ AXE_CSUM_RXBYTES(csum_hdr.len), &csum_hdr);
+ pos += len;
+ }
+ } else
+ axe_rxeof(ue, pc, 0, actlen, NULL);
+
+ if (error != 0)
+ if_inc_counter(ue->ue_ifp, IFCOUNTER_IERRORS, 1);
+ return (error);
+}
+
+static int
+axe_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned int offset,
+ unsigned int len, struct axe_csum_hdr *csum_hdr)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+ struct mbuf *m;
+
+ if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ return (EINVAL);
+ }
+
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ return (ENOMEM);
+ }
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+ m_adj(m, ETHER_ALIGN);
+
+ usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
+
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len;
+
+ if (csum_hdr != NULL && csum_hdr->cstatus & AXE_CSUM_HDR_L3_TYPE_IPV4) {
+ if ((csum_hdr->cstatus & (AXE_CSUM_HDR_L4_CSUM_ERR |
+ AXE_CSUM_HDR_L3_CSUM_ERR)) == 0) {
+ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED |
+ CSUM_IP_VALID;
+ if ((csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) ==
+ AXE_CSUM_HDR_L4_TYPE_TCP ||
+ (csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) ==
+ AXE_CSUM_HDR_L4_TYPE_UDP) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+ }
+
+ _IF_ENQUEUE(&ue->ue_rxq, m);
+ return (0);
+}
+
+#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 usb_xfer *xfer, usb_error_t error)
+{
+ struct axe_softc *sc = usbd_xfer_softc(xfer);
+ struct axe_sframe_hdr hdr;
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ int nframes, pos;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & AXE_FLAG_LINK) == 0 ||
+ (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) {
+ /*
+ * Don't send anything if there is no link or
+ * controller is busy.
+ */
+ return;
+ }
+
+ for (nframes = 0; nframes < 16 &&
+ !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES,
+ nframes);
+ pos = 0;
+ pc = usbd_xfer_get_frame(xfer, nframes);
+ if (AXE_IS_178_FAMILY(sc)) {
+ hdr.len = htole16(m->m_pkthdr.len);
+ hdr.ilen = ~hdr.len;
+ /*
+ * If upper stack computed checksum, driver
+ * should tell controller not to insert
+ * computed checksum for checksum offloading
+ * enabled controller.
+ */
+ if (ifp->if_capabilities & IFCAP_TXCSUM) {
+ if ((m->m_pkthdr.csum_flags &
+ AXE_CSUM_FEATURES) != 0)
+ hdr.len |= htole16(
+ AXE_TX_CSUM_PSEUDO_HDR);
+ else
+ hdr.len |= htole16(
+ AXE_TX_CSUM_DIS);
+ }
+ usbd_copy_in(pc, pos, &hdr, sizeof(hdr));
+ pos += sizeof(hdr);
+ usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len);
+ pos += m->m_pkthdr.len;
+ if ((pos % 512) == 0) {
+ hdr.len = 0;
+ hdr.ilen = 0xffff;
+ usbd_copy_in(pc, pos, &hdr,
+ sizeof(hdr));
+ pos += sizeof(hdr);
+ }
+ } else {
+ usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len);
+ pos += m->m_pkthdr.len;
+ }
+
+ /*
+ * XXX
+ * Update TX packet counter here. This is not
+ * correct way but it seems that there is no way
+ * to know how many packets are sent at the end
+ * of transfer because controller combines
+ * multiple writes into single one if there is
+ * room in TX buffer of controller.
+ */
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ /* Set frame length. */
+ usbd_xfer_set_frame_len(xfer, nframes, pos);
+ }
+ if (nframes != 0) {
+ usbd_xfer_set_frames(xfer, nframes);
+ usbd_transfer_submit(xfer);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ }
+ return;
+ /* NOTREACHED */
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+axe_tick(struct usb_ether *ue)
+{
+ struct axe_softc *sc = uether_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 usb_ether *ue)
+{
+ struct axe_softc *sc = uether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[AXE_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[AXE_BULK_DT_WR]);
+}
+
+static void
+axe_csum_cfg(struct usb_ether *ue)
+{
+ struct axe_softc *sc;
+ struct ifnet *ifp;
+ uint16_t csum1, csum2;
+
+ sc = uether_getsc(ue);
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if ((sc->sc_flags & AXE_FLAG_772B) != 0) {
+ ifp = uether_getifp(ue);
+ csum1 = 0;
+ csum2 = 0;
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+ csum1 |= AXE_TXCSUM_IP | AXE_TXCSUM_TCP |
+ AXE_TXCSUM_UDP;
+ axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL);
+ csum1 = 0;
+ csum2 = 0;
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ csum1 |= AXE_RXCSUM_IP | AXE_RXCSUM_IPVE |
+ AXE_RXCSUM_TCP | AXE_RXCSUM_UDP | AXE_RXCSUM_ICMP |
+ AXE_RXCSUM_IGMP;
+ axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL);
+ }
+}
+
+static void
+axe_init(struct usb_ether *ue)
+{
+ struct axe_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+ uint16_t rxmode;
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ return;
+
+ /* Cancel pending I/O */
+ axe_stop(ue);
+
+ axe_reset(sc);
+
+ /* Set MAC address and transmitter IPG values. */
+ if (AXE_IS_178_FAMILY(sc)) {
+ axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp));
+ 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_NODEID, 0, 0, IF_LLADDR(ifp));
+ 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);
+ }
+
+ if (AXE_IS_178_FAMILY(sc)) {
+ sc->sc_flags &= ~(AXE_FLAG_STD_FRAME | AXE_FLAG_CSUM_FRAME);
+ if ((sc->sc_flags & AXE_FLAG_772B) != 0 &&
+ (ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+ sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK;
+ sc->sc_flags |= AXE_FLAG_CSUM_FRAME;
+ } else {
+ sc->sc_lenmask = AXE_HDR_LEN_MASK;
+ sc->sc_flags |= AXE_FLAG_STD_FRAME;
+ }
+ }
+
+ /* Configure TX/RX checksum offloading. */
+ axe_csum_cfg(ue);
+
+ if (sc->sc_flags & AXE_FLAG_772B) {
+ /* AX88772B uses different maximum frame burst configuration. */
+ axe_cmd(sc, AXE_772B_CMD_RXCTL_WRITE_CFG,
+ ax88772b_mfb_table[AX88772B_MFB_16K].threshold,
+ ax88772b_mfb_table[AX88772B_MFB_16K].byte_cnt, NULL);
+ }
+
+ /* Enable receiver, set RX mode. */
+ rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE);
+ if (AXE_IS_178_FAMILY(sc)) {
+ if (sc->sc_flags & AXE_FLAG_772B) {
+ /*
+ * Select RX header format type 1. Aligning IP
+ * header on 4 byte boundary is not needed when
+ * checksum offloading feature is not used
+ * because we always copy the received frame in
+ * RX handler. When RX checksum offloading is
+ * active, aligning IP header is required to
+ * reflect actual frame length including RX
+ * header size.
+ */
+ rxmode |= AXE_772B_RXCMD_HDR_TYPE_1;
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN;
+ } else {
+ /*
+ * Default Rx buffer size is too small to get
+ * maximum performance.
+ */
+ rxmode |= AXE_178_RXCMD_MFB_16384;
+ }
+ } 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);
+
+ usbd_xfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ /* Switch to selected media. */
+ axe_ifmedia_upd(ifp);
+}
+
+static void
+axe_setpromisc(struct usb_ether *ue)
+{
+ struct axe_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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 usb_ether *ue)
+{
+ struct axe_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ sc->sc_flags &= ~AXE_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]);
+}
+
+static int
+axe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct usb_ether *ue = ifp->if_softc;
+ struct axe_softc *sc;
+ struct ifreq *ifr;
+ int error, mask, reinit;
+
+ sc = uether_getsc(ue);
+ ifr = (struct ifreq *)data;
+ error = 0;
+ reinit = 0;
+ if (cmd == SIOCSIFCAP) {
+ AXE_LOCK(sc);
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+ if ((mask & IFCAP_TXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_TXCSUM;
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+ ifp->if_hwassist |= AXE_CSUM_FEATURES;
+ else
+ ifp->if_hwassist &= ~AXE_CSUM_FEATURES;
+ reinit++;
+ }
+ if ((mask & IFCAP_RXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_RXCSUM;
+ reinit++;
+ }
+ if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ else
+ reinit = 0;
+ AXE_UNLOCK(sc);
+ if (reinit > 0)
+ uether_init(ue);
+ } else
+ error = uether_ioctl(ifp, cmd, data);
+
+ return (error);
+}
diff --git a/freebsd/sys/dev/usb/net/if_axereg.h b/freebsd/sys/dev/usb/net/if_axereg.h
new file mode 100644
index 00000000..64cb2352
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_axereg.h
@@ -0,0 +1,363 @@
+/*-
+ * 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
+
+/* AX88772A and AX88772B only. */
+#define AXE_CMD_READ_VLAN_CTRL 0x4027
+#define AXE_CMD_WRITE_VLAN_CTRL 0x4028
+
+#define AXE_772B_CMD_RXCTL_WRITE_CFG 0x012A
+#define AXE_772B_CMD_READ_RXCSUM 0x002B
+#define AXE_772B_CMD_WRITE_RXCSUM 0x012C
+#define AXE_772B_CMD_READ_TXCSUM 0x002D
+#define AXE_772B_CMD_WRITE_TXCSUM 0x012E
+
+#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_ACCEPT_RUNT 0x0040 /* AX88772B */
+#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_772B_RXCMD_HDR_TYPE_0 0x0000
+#define AXE_772B_RXCMD_HDR_TYPE_1 0x0100
+#define AXE_772B_RXCMD_IPHDR_ALIGN 0x0200
+#define AXE_772B_RXCMD_ADD_CHKSUM 0x0400
+#define AXE_RXCMD_LOOPBACK 0x1000 /* AX88772A/AX88772B */
+
+#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_GPIO0_EN 0x01
+#define AXE_GPIO0 0x02
+#define AXE_GPIO1_EN 0x04
+#define AXE_GPIO1 0x08
+#define AXE_GPIO2_EN 0x10
+#define AXE_GPIO2 0x20
+#define AXE_GPIO_RELOAD_EEPROM 0x80
+
+#define AXE_PHY_MODE_MARVELL 0x00
+#define AXE_PHY_MODE_CICADA 0x01
+#define AXE_PHY_MODE_AGERE 0x02
+#define AXE_PHY_MODE_CICADA_V2 0x05
+#define AXE_PHY_MODE_AGERE_GMII 0x06
+#define AXE_PHY_MODE_CICADA_V2_ASIX 0x09
+#define AXE_PHY_MODE_REALTEK_8211CL 0x0C
+#define AXE_PHY_MODE_REALTEK_8211BN 0x0D
+#define AXE_PHY_MODE_REALTEK_8251CL 0x0E
+#define AXE_PHY_MODE_ATTANSIC 0x40
+
+/* AX88772A/AX88772B only. */
+#define AXE_SW_PHY_SELECT_EXT 0x0000
+#define AXE_SW_PHY_SELECT_EMBEDDED 0x0001
+#define AXE_SW_PHY_SELECT_AUTO 0x0002
+#define AXE_SW_PHY_SELECT_SS_MII 0x0004
+#define AXE_SW_PHY_SELECT_SS_RVRS_MII 0x0008
+#define AXE_SW_PHY_SELECT_SS_RVRS_RMII 0x000C
+#define AXE_SW_PHY_SELECT_SS_ENB 0x0010
+
+/* AX88772A/AX88772B VLAN control. */
+#define AXE_VLAN_CTRL_ENB 0x00001000
+#define AXE_VLAN_CTRL_STRIP 0x00002000
+#define AXE_VLAN_CTRL_VID1_MASK 0x00000FFF
+#define AXE_VLAN_CTRL_VID2_MASK 0x0FFF0000
+
+#define AXE_RXCSUM_IP 0x0001
+#define AXE_RXCSUM_IPVE 0x0002
+#define AXE_RXCSUM_IPV6E 0x0004
+#define AXE_RXCSUM_TCP 0x0008
+#define AXE_RXCSUM_UDP 0x0010
+#define AXE_RXCSUM_ICMP 0x0020
+#define AXE_RXCSUM_IGMP 0x0040
+#define AXE_RXCSUM_ICMP6 0x0080
+#define AXE_RXCSUM_TCPV6 0x0100
+#define AXE_RXCSUM_UDPV6 0x0200
+#define AXE_RXCSUM_ICMPV6 0x0400
+#define AXE_RXCSUM_IGMPV6 0x0800
+#define AXE_RXCSUM_ICMP6V6 0x1000
+#define AXE_RXCSUM_FOPC 0x8000
+
+#define AXE_RXCSUM_64TE 0x0100
+#define AXE_RXCSUM_PPPOE 0x0200
+#define AXE_RXCSUM_RPCE 0x8000
+
+#define AXE_TXCSUM_IP 0x0001
+#define AXE_TXCSUM_TCP 0x0002
+#define AXE_TXCSUM_UDP 0x0004
+#define AXE_TXCSUM_ICMP 0x0008
+#define AXE_TXCSUM_IGMP 0x0010
+#define AXE_TXCSUM_ICMP6 0x0020
+#define AXE_TXCSUM_TCPV6 0x0100
+#define AXE_TXCSUM_UDPV6 0x0200
+#define AXE_TXCSUM_ICMPV6 0x0400
+#define AXE_TXCSUM_IGMPV6 0x0800
+#define AXE_TXCSUM_ICMP6V6 0x1000
+
+#define AXE_TXCSUM_64TE 0x0001
+#define AXE_TXCSUM_PPPOE 0x0002
+
+#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
+
+/* EEPROM Map. */
+#define AXE_EEPROM_772B_NODE_ID 0x04
+#define AXE_EEPROM_772B_PHY_PWRCFG 0x18
+
+struct ax88772b_mfb {
+ int byte_cnt;
+ int threshold;
+ int size;
+};
+#define AX88772B_MFB_2K 0
+#define AX88772B_MFB_4K 1
+#define AX88772B_MFB_6K 2
+#define AX88772B_MFB_8K 3
+#define AX88772B_MFB_16K 4
+#define AX88772B_MFB_20K 5
+#define AX88772B_MFB_24K 6
+#define AX88772B_MFB_32K 7
+
+struct axe_sframe_hdr {
+ uint16_t len;
+#define AXE_HDR_LEN_MASK 0xFFFF
+ uint16_t ilen;
+} __packed;
+
+#define AXE_TX_CSUM_PSEUDO_HDR 0x4000
+#define AXE_TX_CSUM_DIS 0x8000
+
+/*
+ * When RX checksum offloading is enabled, AX88772B uses new RX header
+ * format and it's not compatible with previous RX header format. In
+ * addition, IP header align option should be enabled to get correct
+ * frame size including RX header. Total transferred size including
+ * the RX header is multiple of 4 and controller will pad necessary
+ * bytes if the length is not multiple of 4.
+ * This driver does not enable partial checksum feature which will
+ * compute 16bit checksum from 14th byte to the end of the frame. If
+ * this feature is enabled, computed checksum value is embedded into
+ * RX header which in turn means it uses different RX header format.
+ */
+struct axe_csum_hdr {
+ uint16_t len;
+#define AXE_CSUM_HDR_LEN_MASK 0x07FF
+#define AXE_CSUM_HDR_CRC_ERR 0x1000
+#define AXE_CSUM_HDR_MII_ERR 0x2000
+#define AXE_CSUM_HDR_RUNT 0x4000
+#define AXE_CSUM_HDR_BMCAST 0x8000
+ uint16_t ilen;
+ uint16_t cstatus;
+#define AXE_CSUM_HDR_VLAN_MASK 0x0007
+#define AXE_CSUM_HDR_VLAN_STRIP 0x0008
+#define AXE_CSUM_HDR_VLAN_PRI_MASK 0x0070
+#define AXE_CSUM_HDR_L4_CSUM_ERR 0x0100
+#define AXE_CSUM_HDR_L3_CSUM_ERR 0x0200
+#define AXE_CSUM_HDR_L4_TYPE_UDP 0x0400
+#define AXE_CSUM_HDR_L4_TYPE_ICMP 0x0800
+#define AXE_CSUM_HDR_L4_TYPE_IGMP 0x0C00
+#define AXE_CSUM_HDR_L4_TYPE_TCP 0x1000
+#define AXE_CSUM_HDR_L4_TYPE_TCPV6 0x1400
+#define AXE_CSUM_HDR_L4_TYPE_MASK 0x1C00
+#define AXE_CSUM_HDR_L3_TYPE_IPV4 0x2000
+#define AXE_CSUM_HDR_L3_TYPE_IPV6 0x4000
+
+#ifdef AXE_APPEND_PARTIAL_CSUM
+ /*
+ * These members present only when partial checksum
+ * offloading is enabled. The checksum value is simple
+ * 16bit sum of received frame starting at offset 14 of
+ * the frame to the end of the frame excluding FCS bytes.
+ */
+ uint16_t csum_value;
+ uint16_t dummy;
+#endif
+} __packed;
+
+#define AXE_CSUM_RXBYTES(x) ((x) & AXE_CSUM_HDR_LEN_MASK)
+
+#define GET_MII(sc) uether_getmii(&(sc)->sc_ue)
+
+/* The interrupt endpoint is currently unused by the ASIX part. */
+enum {
+ AXE_BULK_DT_WR,
+ AXE_BULK_DT_RD,
+ AXE_N_TRANSFER,
+};
+
+struct axe_softc {
+ struct usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb_xfer *sc_xfer[AXE_N_TRANSFER];
+ int sc_phyno;
+
+ int sc_flags;
+#define AXE_FLAG_LINK 0x0001
+#define AXE_FLAG_STD_FRAME 0x0010
+#define AXE_FLAG_CSUM_FRAME 0x0020
+#define AXE_FLAG_772 0x1000 /* AX88772 */
+#define AXE_FLAG_772A 0x2000 /* AX88772A */
+#define AXE_FLAG_772B 0x4000 /* AX88772B */
+#define AXE_FLAG_178 0x8000 /* AX88178 */
+
+ uint8_t sc_ipgs[3];
+ uint8_t sc_phyaddrs[2];
+ uint16_t sc_pwrcfg;
+ uint16_t sc_lenmask;
+};
+
+#define AXE_IS_178_FAMILY(sc) \
+ ((sc)->sc_flags & (AXE_FLAG_772 | AXE_FLAG_772A | AXE_FLAG_772B | \
+ AXE_FLAG_178))
+
+#define AXE_IS_772(sc) \
+ ((sc)->sc_flags & (AXE_FLAG_772 | AXE_FLAG_772A | AXE_FLAG_772B))
+
+#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/freebsd/sys/dev/usb/net/if_axge.c b/freebsd/sys/dev/usb/net/if_axge.c
new file mode 100644
index 00000000..fd50dcdf
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_axge.c
@@ -0,0 +1,1056 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2013-2014 Kevin Lo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ASIX Electronics AX88178A/AX88179 USB 2.0/3.0 gigabit ethernet driver.
+ */
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <rtems/bsd/sys/unistd.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR axge_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_axgereg.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+
+static const STRUCT_USB_HOST_ID axge_devs[] = {
+#define AXGE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+ AXGE_DEV(ASIX, AX88178A),
+ AXGE_DEV(ASIX, AX88179),
+ AXGE_DEV(DLINK, DUB1312),
+ AXGE_DEV(LENOVO, GIGALAN),
+ AXGE_DEV(SITECOMEU, LN032),
+#undef AXGE_DEV
+};
+
+static const struct {
+ uint8_t ctrl;
+ uint8_t timer_l;
+ uint8_t timer_h;
+ uint8_t size;
+ uint8_t ifg;
+} __packed axge_bulk_size[] = {
+ { 7, 0x4f, 0x00, 0x12, 0xff },
+ { 7, 0x20, 0x03, 0x16, 0xff },
+ { 7, 0xae, 0x07, 0x18, 0xff },
+ { 7, 0xcc, 0x4c, 0x18, 0x08 }
+};
+
+/* prototypes */
+
+static device_probe_t axge_probe;
+static device_attach_t axge_attach;
+static device_detach_t axge_detach;
+
+static usb_callback_t axge_bulk_read_callback;
+static usb_callback_t axge_bulk_write_callback;
+
+static miibus_readreg_t axge_miibus_readreg;
+static miibus_writereg_t axge_miibus_writereg;
+static miibus_statchg_t axge_miibus_statchg;
+
+static uether_fn_t axge_attach_post;
+static uether_fn_t axge_init;
+static uether_fn_t axge_stop;
+static uether_fn_t axge_start;
+static uether_fn_t axge_tick;
+static uether_fn_t axge_rxfilter;
+
+static int axge_read_mem(struct axge_softc *, uint8_t, uint16_t,
+ uint16_t, void *, int);
+static void axge_write_mem(struct axge_softc *, uint8_t, uint16_t,
+ uint16_t, void *, int);
+static uint8_t axge_read_cmd_1(struct axge_softc *, uint8_t, uint16_t);
+static uint16_t axge_read_cmd_2(struct axge_softc *, uint8_t, uint16_t,
+ uint16_t);
+static void axge_write_cmd_1(struct axge_softc *, uint8_t, uint16_t,
+ uint8_t);
+static void axge_write_cmd_2(struct axge_softc *, uint8_t, uint16_t,
+ uint16_t, uint16_t);
+static void axge_chip_init(struct axge_softc *);
+static void axge_reset(struct axge_softc *);
+
+static int axge_attach_post_sub(struct usb_ether *);
+static int axge_ifmedia_upd(struct ifnet *);
+static void axge_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static int axge_ioctl(struct ifnet *, u_long, caddr_t);
+static void axge_rx_frame(struct usb_ether *, struct usb_page_cache *, int);
+static void axge_rxeof(struct usb_ether *, struct usb_page_cache *,
+ unsigned int, unsigned int, uint32_t);
+static void axge_csum_cfg(struct usb_ether *);
+
+#define AXGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
+
+#ifdef USB_DEBUG
+static int axge_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, axge, CTLFLAG_RW, 0, "USB axge");
+SYSCTL_INT(_hw_usb_axge, OID_AUTO, debug, CTLFLAG_RWTUN, &axge_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb_config axge_config[AXGE_N_TRANSFER] = {
+ [AXGE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .frames = AXGE_N_FRAMES,
+ .bufsize = AXGE_N_FRAMES * MCLBYTES,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = axge_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+ [AXGE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = 65536,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = axge_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ },
+};
+
+static device_method_t axge_methods[] = {
+ /* Device interface. */
+ DEVMETHOD(device_probe, axge_probe),
+ DEVMETHOD(device_attach, axge_attach),
+ DEVMETHOD(device_detach, axge_detach),
+
+ /* MII interface. */
+ DEVMETHOD(miibus_readreg, axge_miibus_readreg),
+ DEVMETHOD(miibus_writereg, axge_miibus_writereg),
+ DEVMETHOD(miibus_statchg, axge_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t axge_driver = {
+ .name = "axge",
+ .methods = axge_methods,
+ .size = sizeof(struct axge_softc),
+};
+
+static devclass_t axge_devclass;
+
+DRIVER_MODULE(axge, uhub, axge_driver, axge_devclass, NULL, NULL);
+DRIVER_MODULE(miibus, axge, miibus_driver, miibus_devclass, NULL, NULL);
+MODULE_DEPEND(axge, uether, 1, 1, 1);
+MODULE_DEPEND(axge, usb, 1, 1, 1);
+MODULE_DEPEND(axge, ether, 1, 1, 1);
+MODULE_DEPEND(axge, miibus, 1, 1, 1);
+MODULE_VERSION(axge, 1);
+USB_PNP_HOST_INFO(axge_devs);
+
+static const struct usb_ether_methods axge_ue_methods = {
+ .ue_attach_post = axge_attach_post,
+ .ue_attach_post_sub = axge_attach_post_sub,
+ .ue_start = axge_start,
+ .ue_init = axge_init,
+ .ue_stop = axge_stop,
+ .ue_tick = axge_tick,
+ .ue_setmulti = axge_rxfilter,
+ .ue_setpromisc = axge_rxfilter,
+ .ue_mii_upd = axge_ifmedia_upd,
+ .ue_mii_sts = axge_ifmedia_sts,
+};
+
+static int
+axge_read_mem(struct axge_softc *sc, uint8_t cmd, uint16_t index,
+ uint16_t val, void *buf, int len)
+{
+ struct usb_device_request req;
+
+ AXGE_LOCK_ASSERT(sc, MA_OWNED);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = cmd;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, len);
+
+ return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static void
+axge_write_mem(struct axge_softc *sc, uint8_t cmd, uint16_t index,
+ uint16_t val, void *buf, int len)
+{
+ struct usb_device_request req;
+
+ AXGE_LOCK_ASSERT(sc, MA_OWNED);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = cmd;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, len);
+
+ if (uether_do_request(&sc->sc_ue, &req, buf, 1000)) {
+ /* Error ignored. */
+ }
+}
+
+static uint8_t
+axge_read_cmd_1(struct axge_softc *sc, uint8_t cmd, uint16_t reg)
+{
+ uint8_t val;
+
+ axge_read_mem(sc, cmd, 1, reg, &val, 1);
+ return (val);
+}
+
+static uint16_t
+axge_read_cmd_2(struct axge_softc *sc, uint8_t cmd, uint16_t index,
+ uint16_t reg)
+{
+ uint8_t val[2];
+
+ axge_read_mem(sc, cmd, index, reg, &val, 2);
+ return (UGETW(val));
+}
+
+static void
+axge_write_cmd_1(struct axge_softc *sc, uint8_t cmd, uint16_t reg, uint8_t val)
+{
+ axge_write_mem(sc, cmd, 1, reg, &val, 1);
+}
+
+static void
+axge_write_cmd_2(struct axge_softc *sc, uint8_t cmd, uint16_t index,
+ uint16_t reg, uint16_t val)
+{
+ uint8_t temp[2];
+
+ USETW(temp, val);
+ axge_write_mem(sc, cmd, index, reg, &temp, 2);
+}
+
+static int
+axge_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct axge_softc *sc;
+ uint16_t val;
+ int locked;
+
+ sc = device_get_softc(dev);
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXGE_LOCK(sc);
+
+ val = axge_read_cmd_2(sc, AXGE_ACCESS_PHY, reg, phy);
+
+ if (!locked)
+ AXGE_UNLOCK(sc);
+
+ return (val);
+}
+
+static int
+axge_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct axge_softc *sc;
+ int locked;
+
+ sc = device_get_softc(dev);
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXGE_LOCK(sc);
+
+ axge_write_cmd_2(sc, AXGE_ACCESS_PHY, reg, phy, val);
+
+ if (!locked)
+ AXGE_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+axge_miibus_statchg(device_t dev)
+{
+ struct axge_softc *sc;
+ struct mii_data *mii;
+ struct ifnet *ifp;
+ uint8_t link_status, tmp[5];
+ uint16_t val;
+ int locked;
+
+ sc = device_get_softc(dev);
+ mii = GET_MII(sc);
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXGE_LOCK(sc);
+
+ ifp = uether_getifp(&sc->sc_ue);
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ goto done;
+
+ sc->sc_flags &= ~AXGE_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:
+ case IFM_1000_T:
+ sc->sc_flags |= AXGE_FLAG_LINK;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Lost link, do nothing. */
+ if ((sc->sc_flags & AXGE_FLAG_LINK) == 0)
+ goto done;
+
+ link_status = axge_read_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PLSR);
+
+ val = 0;
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
+ val |= MSR_FD;
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
+ val |= MSR_TFC;
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
+ val |= MSR_RFC;
+ }
+ val |= MSR_RE;
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_1000_T:
+ val |= MSR_GM | MSR_EN_125MHZ;
+ if (link_status & PLSR_USB_SS)
+ memcpy(tmp, &axge_bulk_size[0], 5);
+ else if (link_status & PLSR_USB_HS)
+ memcpy(tmp, &axge_bulk_size[1], 5);
+ else
+ memcpy(tmp, &axge_bulk_size[3], 5);
+ break;
+ case IFM_100_TX:
+ val |= MSR_PS;
+ if (link_status & (PLSR_USB_SS | PLSR_USB_HS))
+ memcpy(tmp, &axge_bulk_size[2], 5);
+ else
+ memcpy(tmp, &axge_bulk_size[3], 5);
+ break;
+ case IFM_10_T:
+ memcpy(tmp, &axge_bulk_size[3], 5);
+ break;
+ }
+ /* Rx bulk configuration. */
+ axge_write_mem(sc, AXGE_ACCESS_MAC, 5, AXGE_RX_BULKIN_QCTRL, tmp, 5);
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, val);
+done:
+ if (!locked)
+ AXGE_UNLOCK(sc);
+}
+
+static void
+axge_chip_init(struct axge_softc *sc)
+{
+ /* Power up ethernet PHY. */
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, 0);
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, EPPRCR_IPRL);
+ uether_pause(&sc->sc_ue, hz / 4);
+ axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CLK_SELECT,
+ AXGE_CLK_SELECT_ACS | AXGE_CLK_SELECT_BCS);
+ uether_pause(&sc->sc_ue, hz / 10);
+}
+
+static void
+axge_reset(struct axge_softc *sc)
+{
+ struct usb_config_descriptor *cd;
+ usb_error_t err;
+
+ cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev);
+
+ err = usbd_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. */
+ uether_pause(&sc->sc_ue, hz / 100);
+
+ /* Reinitialize controller to achieve full reset. */
+ axge_chip_init(sc);
+}
+
+static void
+axge_attach_post(struct usb_ether *ue)
+{
+ struct axge_softc *sc;
+
+ sc = uether_getsc(ue);
+
+ /* Initialize controller and get station address. */
+ axge_chip_init(sc);
+ axge_read_mem(sc, AXGE_ACCESS_MAC, ETHER_ADDR_LEN, AXGE_NIDR,
+ ue->ue_eaddr, ETHER_ADDR_LEN);
+}
+
+static int
+axge_attach_post_sub(struct usb_ether *ue)
+{
+ struct axge_softc *sc;
+ struct ifnet *ifp;
+ int error;
+
+ sc = uether_getsc(ue);
+ ifp = ue->ue_ifp;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_start = uether_start;
+ ifp->if_ioctl = axge_ioctl;
+ ifp->if_init = uether_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+ ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_TXCSUM | IFCAP_RXCSUM;
+ ifp->if_hwassist = AXGE_CSUM_FEATURES;
+ ifp->if_capenable = ifp->if_capabilities;
+
+ mtx_lock(&Giant);
+ error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
+ uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
+ BMSR_DEFCAPMASK, AXGE_PHY_ADDR, MII_OFFSET_ANY, MIIF_DOPAUSE);
+ mtx_unlock(&Giant);
+
+ return (error);
+}
+
+/*
+ * Set media options.
+ */
+static int
+axge_ifmedia_upd(struct ifnet *ifp)
+{
+ struct axge_softc *sc;
+ struct mii_data *mii;
+ struct mii_softc *miisc;
+ int error;
+
+ sc = ifp->if_softc;
+ mii = GET_MII(sc);
+ AXGE_LOCK_ASSERT(sc, MA_OWNED);
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ error = mii_mediachg(mii);
+
+ return (error);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+axge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct axge_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ mii = GET_MII(sc);
+ AXGE_LOCK(sc);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ AXGE_UNLOCK(sc);
+}
+
+/*
+ * Probe for a AX88179 chip.
+ */
+static int
+axge_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa;
+
+ uaa = device_get_ivars(dev);
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != AXGE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != AXGE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(axge_devs, sizeof(axge_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+axge_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa;
+ struct axge_softc *sc;
+ struct usb_ether *ue;
+ uint8_t iface_index;
+ int error;
+
+ uaa = device_get_ivars(dev);
+ sc = device_get_softc(dev);
+ ue = &sc->sc_ue;
+
+ device_set_usb_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = AXGE_IFACE_IDX;
+ error = usbd_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, axge_config, AXGE_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed\n");
+ mtx_destroy(&sc->sc_mtx);
+ return (ENXIO);
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &axge_ue_methods;
+
+ error = uether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ axge_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+axge_detach(device_t dev)
+{
+ struct axge_softc *sc;
+ struct usb_ether *ue;
+ uint16_t val;
+
+ sc = device_get_softc(dev);
+ ue = &sc->sc_ue;
+ if (device_is_attached(dev)) {
+ AXGE_LOCK(sc);
+ /*
+ * XXX
+ * ether_ifdetach(9) should be called first.
+ */
+ axge_stop(ue);
+ /* Force bulk-in to return a zero-length USB packet. */
+ val = axge_read_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR);
+ val |= EPPRCR_BZ | EPPRCR_IPRL;
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, val);
+ /* Change clock. */
+ axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CLK_SELECT, 0);
+ /* Disable MAC. */
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, 0);
+ AXGE_UNLOCK(sc);
+ }
+ usbd_transfer_unsetup(sc->sc_xfer, AXGE_N_TRANSFER);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+axge_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct axge_softc *sc;
+ struct usb_ether *ue;
+ struct usb_page_cache *pc;
+ int actlen;
+
+ sc = usbd_xfer_softc(xfer);
+ ue = &sc->sc_ue;
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ pc = usbd_xfer_get_frame(xfer, 0);
+ axge_rx_frame(ue, pc, actlen);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ break;
+
+ default:
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+axge_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct axge_softc *sc;
+ struct ifnet *ifp;
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ struct axge_frame_txhdr txhdr;
+ int nframes, pos;
+
+ sc = usbd_xfer_softc(xfer);
+ ifp = uether_getifp(&sc->sc_ue);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & AXGE_FLAG_LINK) == 0 ||
+ (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) {
+ /*
+ * Don't send anything if there is no link or
+ * controller is busy.
+ */
+ return;
+ }
+
+ for (nframes = 0; nframes < AXGE_N_FRAMES &&
+ !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES,
+ nframes);
+ pc = usbd_xfer_get_frame(xfer, nframes);
+ txhdr.mss = 0;
+ txhdr.len = htole32(AXGE_TXBYTES(m->m_pkthdr.len));
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0 &&
+ (m->m_pkthdr.csum_flags & AXGE_CSUM_FEATURES) == 0)
+ txhdr.len |= htole32(AXGE_CSUM_DISABLE);
+
+ pos = 0;
+ usbd_copy_in(pc, pos, &txhdr, sizeof(txhdr));
+ pos += sizeof(txhdr);
+ usbd_m_copy_in(pc, 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);
+
+ /* Set frame length. */
+ usbd_xfer_set_frame_len(xfer, nframes, pos);
+ }
+ if (nframes != 0) {
+ /*
+ * XXX
+ * Update TX packet counter here. This is not
+ * correct way but it seems that there is no way
+ * to know how many packets are sent at the end
+ * of transfer because controller combines
+ * multiple writes into single one if there is
+ * room in TX buffer of controller.
+ */
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, nframes);
+ usbd_xfer_set_frames(xfer, nframes);
+ usbd_transfer_submit(xfer);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ }
+ return;
+ /* NOTREACHED */
+ default:
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+axge_tick(struct usb_ether *ue)
+{
+ struct axge_softc *sc;
+ struct mii_data *mii;
+
+ sc = uether_getsc(ue);
+ mii = GET_MII(sc);
+ AXGE_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+}
+
+static void
+axge_rxfilter(struct usb_ether *ue)
+{
+ struct axge_softc *sc;
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ uint32_t h;
+ uint16_t rxmode;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ sc = uether_getsc(ue);
+ ifp = uether_getifp(ue);
+ h = 0;
+ AXGE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Configure RX settings.
+ * Don't set RCR_IPE(IP header alignment on 32bit boundary) to disable
+ * inserting extra padding bytes. This wastes ethernet to USB host
+ * bandwidth as well as complicating RX handling logic. Current USB
+ * framework requires copying RX frames to mbufs so there is no need
+ * to worry about alignment.
+ */
+ rxmode = RCR_DROP_CRCERR | RCR_START;
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxmode |= RCR_ACPT_BCAST;
+ if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+ if (ifp->if_flags & IFF_PROMISC)
+ rxmode |= RCR_PROMISC;
+ rxmode |= RCR_ACPT_ALL_MCAST;
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, rxmode);
+ return;
+ }
+
+ rxmode |= RCR_ACPT_MCAST;
+ if_maddr_rlock(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_maddr_runlock(ifp);
+
+ axge_write_mem(sc, AXGE_ACCESS_MAC, 8, AXGE_MFA, (void *)&hashtbl, 8);
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, rxmode);
+}
+
+static void
+axge_start(struct usb_ether *ue)
+{
+ struct axge_softc *sc;
+
+ sc = uether_getsc(ue);
+ /*
+ * Start the USB transfers, if not already started.
+ */
+ usbd_transfer_start(sc->sc_xfer[AXGE_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[AXGE_BULK_DT_WR]);
+}
+
+static void
+axge_init(struct usb_ether *ue)
+{
+ struct axge_softc *sc;
+ struct ifnet *ifp;
+
+ sc = uether_getsc(ue);
+ ifp = uether_getifp(ue);
+ AXGE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ return;
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ axge_stop(ue);
+
+ axge_reset(sc);
+
+ /* Set MAC address. */
+ axge_write_mem(sc, AXGE_ACCESS_MAC, ETHER_ADDR_LEN, AXGE_NIDR,
+ IF_LLADDR(ifp), ETHER_ADDR_LEN);
+
+ axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PWLLR, 0x34);
+ axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PWLHR, 0x52);
+
+ /* Configure TX/RX checksum offloading. */
+ axge_csum_cfg(ue);
+
+ /* Configure RX filters. */
+ axge_rxfilter(ue);
+
+ /*
+ * XXX
+ * Controller supports wakeup on link change detection,
+ * magic packet and wakeup frame recpetion. But it seems
+ * there is no framework for USB ethernet suspend/wakeup.
+ * Disable all wakeup functions.
+ */
+ axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_MMSR, 0);
+ (void)axge_read_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_MMSR);
+
+ /* Configure default medium type. */
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, MSR_GM | MSR_FD |
+ MSR_RFC | MSR_TFC | MSR_RE);
+
+ usbd_xfer_set_stall(sc->sc_xfer[AXGE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ /* Switch to selected media. */
+ axge_ifmedia_upd(ifp);
+}
+
+static void
+axge_stop(struct usb_ether *ue)
+{
+ struct axge_softc *sc;
+ struct ifnet *ifp;
+ uint16_t val;
+
+ sc = uether_getsc(ue);
+ ifp = uether_getifp(ue);
+
+ AXGE_LOCK_ASSERT(sc, MA_OWNED);
+
+ val = axge_read_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR);
+ val &= ~MSR_RE;
+ axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, val);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ sc->sc_flags &= ~AXGE_FLAG_LINK;
+
+ /*
+ * Stop all the transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[AXGE_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[AXGE_BULK_DT_RD]);
+}
+
+static int
+axge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct usb_ether *ue;
+ struct axge_softc *sc;
+ struct ifreq *ifr;
+ int error, mask, reinit;
+
+ ue = ifp->if_softc;
+ sc = uether_getsc(ue);
+ ifr = (struct ifreq *)data;
+ error = 0;
+ reinit = 0;
+ if (cmd == SIOCSIFCAP) {
+ AXGE_LOCK(sc);
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+ if ((mask & IFCAP_TXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_TXCSUM;
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+ ifp->if_hwassist |= AXGE_CSUM_FEATURES;
+ else
+ ifp->if_hwassist &= ~AXGE_CSUM_FEATURES;
+ reinit++;
+ }
+ if ((mask & IFCAP_RXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_RXCSUM;
+ reinit++;
+ }
+ if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ else
+ reinit = 0;
+ AXGE_UNLOCK(sc);
+ if (reinit > 0)
+ uether_init(ue);
+ } else
+ error = uether_ioctl(ifp, cmd, data);
+
+ return (error);
+}
+
+static void
+axge_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen)
+{
+ struct axge_frame_rxhdr pkt_hdr;
+ uint32_t rxhdr;
+ uint32_t pos;
+ uint32_t pkt_cnt, pkt_end;
+ uint32_t hdr_off;
+ uint32_t pktlen;
+
+ /* verify we have enough data */
+ if (actlen < (int)sizeof(rxhdr))
+ return;
+
+ pos = 0;
+
+ usbd_copy_out(pc, actlen - sizeof(rxhdr), &rxhdr, sizeof(rxhdr));
+ rxhdr = le32toh(rxhdr);
+
+ pkt_cnt = rxhdr & 0xFFFF;
+ hdr_off = pkt_end = (rxhdr >> 16) & 0xFFFF;
+
+ /*
+ * <----------------------- actlen ------------------------>
+ * [frame #0]...[frame #N][pkt_hdr #0]...[pkt_hdr #N][rxhdr]
+ * Each RX frame would be aligned on 8 bytes boundary. If
+ * RCR_IPE bit is set in AXGE_RCR register, there would be 2
+ * padding bytes and 6 dummy bytes(as the padding also should
+ * be aligned on 8 bytes boundary) for each RX frame to align
+ * IP header on 32bits boundary. Driver don't set RCR_IPE bit
+ * of AXGE_RCR register, so there should be no padding bytes
+ * which simplifies RX logic a lot.
+ */
+ while (pkt_cnt--) {
+ /* verify the header offset */
+ if ((int)(hdr_off + sizeof(pkt_hdr)) > actlen) {
+ DPRINTF("End of packet headers\n");
+ break;
+ }
+ usbd_copy_out(pc, hdr_off, &pkt_hdr, sizeof(pkt_hdr));
+ pkt_hdr.status = le32toh(pkt_hdr.status);
+ pktlen = AXGE_RXBYTES(pkt_hdr.status);
+ if (pos + pktlen > pkt_end) {
+ DPRINTF("Data position reached end\n");
+ break;
+ }
+
+ if (AXGE_RX_ERR(pkt_hdr.status) != 0) {
+ DPRINTF("Dropped a packet\n");
+ if_inc_counter(ue->ue_ifp, IFCOUNTER_IERRORS, 1);
+ } else
+ axge_rxeof(ue, pc, pos, pktlen, pkt_hdr.status);
+ pos += (pktlen + 7) & ~7;
+ hdr_off += sizeof(pkt_hdr);
+ }
+}
+
+static void
+axge_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned int offset,
+ unsigned int len, uint32_t status)
+{
+ struct ifnet *ifp;
+ struct mbuf *m;
+
+ ifp = ue->ue_ifp;
+ if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ return;
+ }
+
+ if (len > MHLEN - ETHER_ALIGN)
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ else
+ m = m_gethdr(M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ return;
+ }
+ m->m_pkthdr.rcvif = ifp;
+ m->m_len = m->m_pkthdr.len = len;
+ m->m_data += ETHER_ALIGN;
+
+ usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
+
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+ if ((status & AXGE_RX_L3_CSUM_ERR) == 0 &&
+ (status & AXGE_RX_L3_TYPE_MASK) == AXGE_RX_L3_TYPE_IPV4)
+ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED |
+ CSUM_IP_VALID;
+ if ((status & AXGE_RX_L4_CSUM_ERR) == 0 &&
+ ((status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_UDP ||
+ (status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_TCP)) {
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID |
+ CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+
+ _IF_ENQUEUE(&ue->ue_rxq, m);
+}
+
+static void
+axge_csum_cfg(struct usb_ether *ue)
+{
+ struct axge_softc *sc;
+ struct ifnet *ifp;
+ uint8_t csum;
+
+ sc = uether_getsc(ue);
+ AXGE_LOCK_ASSERT(sc, MA_OWNED);
+ ifp = uether_getifp(ue);
+
+ csum = 0;
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+ csum |= CTCR_IP | CTCR_TCP | CTCR_UDP;
+ axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CTCR, csum);
+
+ csum = 0;
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ csum |= CRCR_IP | CRCR_TCP | CRCR_UDP;
+ axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CRCR, csum);
+}
diff --git a/freebsd/sys/dev/usb/net/if_axgereg.h b/freebsd/sys/dev/usb/net/if_axgereg.h
new file mode 100644
index 00000000..c073610f
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_axgereg.h
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 2013-2014 Kevin Lo
+ * 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 AXGE_ACCESS_MAC 0x01
+#define AXGE_ACCESS_PHY 0x02
+#define AXGE_ACCESS_WAKEUP 0x03
+#define AXGE_ACCESS_EEPROM 0x04
+#define AXGE_ACCESS_EFUSE 0x05
+#define AXGE_RELOAD_EEPROM_EFUSE 0x06
+#define AXGE_WRITE_EFUSE_EN 0x09
+#define AXGE_WRITE_EFUSE_DIS 0x0A
+#define AXGE_ACCESS_MFAB 0x10
+
+/* Physical link status register */
+#define AXGE_PLSR 0x02
+#define PLSR_USB_FS 0x01
+#define PLSR_USB_HS 0x02
+#define PLSR_USB_SS 0x04
+
+/* EEPROM address register */
+#define AXGE_EAR 0x07
+
+/* EEPROM data low register */
+#define AXGE_EDLR 0x08
+
+/* EEPROM data high register */
+#define AXGE_EDHR 0x09
+
+/* EEPROM command register */
+#define AXGE_ECR 0x0a
+
+/* Rx control register */
+#define AXGE_RCR 0x0b
+#define RCR_STOP 0x0000
+#define RCR_PROMISC 0x0001
+#define RCR_ACPT_ALL_MCAST 0x0002
+#define RCR_AUTOPAD_BNDRY 0x0004
+#define RCR_ACPT_BCAST 0x0008
+#define RCR_ACPT_MCAST 0x0010
+#define RCR_ACPT_PHY_MCAST 0x0020
+#define RCR_START 0x0080
+#define RCR_DROP_CRCERR 0x0100
+#define RCR_IPE 0x0200
+#define RCR_TX_CRC_PAD 0x0400
+
+/* Node id register */
+#define AXGE_NIDR 0x10
+
+/* Multicast filter array */
+#define AXGE_MFA 0x16
+
+/* Medium status register */
+#define AXGE_MSR 0x22
+#define MSR_GM 0x0001
+#define MSR_FD 0x0002
+#define MSR_EN_125MHZ 0x0008
+#define MSR_RFC 0x0010
+#define MSR_TFC 0x0020
+#define MSR_RE 0x0100
+#define MSR_PS 0x0200
+
+/* Monitor mode status register */
+#define AXGE_MMSR 0x24
+#define MMSR_RWLC 0x02
+#define MMSR_RWMP 0x04
+#define MMSR_RWWF 0x08
+#define MMSR_RW_FLAG 0x10
+#define MMSR_PME_POL 0x20
+#define MMSR_PME_TYPE 0x40
+#define MMSR_PME_IND 0x80
+
+/* GPIO control/status register */
+#define AXGE_GPIOCR 0x25
+
+/* Ethernet PHY power & reset control register */
+#define AXGE_EPPRCR 0x26
+#define EPPRCR_BZ 0x0010
+#define EPPRCR_IPRL 0x0020
+#define EPPRCR_AUTODETACH 0x1000
+
+#define AXGE_RX_BULKIN_QCTRL 0x2e
+
+#define AXGE_CLK_SELECT 0x33
+#define AXGE_CLK_SELECT_BCS 0x01
+#define AXGE_CLK_SELECT_ACS 0x02
+#define AXGE_CLK_SELECT_ACSREQ 0x10
+#define AXGE_CLK_SELECT_ULR 0x08
+
+/* COE Rx control register */
+#define AXGE_CRCR 0x34
+#define CRCR_IP 0x01
+#define CRCR_TCP 0x02
+#define CRCR_UDP 0x04
+#define CRCR_ICMP 0x08
+#define CRCR_IGMP 0x10
+#define CRCR_TCPV6 0x20
+#define CRCR_UDPV6 0x40
+#define CRCR_ICMPV6 0x80
+
+/* COE Tx control register */
+#define AXGE_CTCR 0x35
+#define CTCR_IP 0x01
+#define CTCR_TCP 0x02
+#define CTCR_UDP 0x04
+#define CTCR_ICMP 0x08
+#define CTCR_IGMP 0x10
+#define CTCR_TCPV6 0x20
+#define CTCR_UDPV6 0x40
+#define CTCR_ICMPV6 0x80
+
+/* Pause water level high register */
+#define AXGE_PWLHR 0x54
+
+/* Pause water level low register */
+#define AXGE_PWLLR 0x55
+
+#define AXGE_CONFIG_IDX 0 /* config number 1 */
+#define AXGE_IFACE_IDX 0
+
+#define GET_MII(sc) uether_getmii(&(sc)->sc_ue)
+
+/* The interrupt endpoint is currently unused by the ASIX part. */
+enum {
+ AXGE_BULK_DT_WR,
+ AXGE_BULK_DT_RD,
+ AXGE_N_TRANSFER,
+};
+
+#define AXGE_N_FRAMES 16
+
+struct axge_frame_txhdr {
+ uint32_t len;
+#define AXGE_TXLEN_MASK 0x0001FFFF
+#define AXGE_VLAN_INSERT 0x20000000
+#define AXGE_CSUM_DISABLE 0x80000000
+ uint32_t mss;
+#define AXGE_MSS_MASK 0x00003FFF
+#define AXGE_PADDING 0x80008000
+#define AXGE_VLAN_TAG_MASK 0xFFFF0000
+} __packed;
+
+#define AXGE_TXBYTES(x) ((x) & AXGE_TXLEN_MASK)
+
+#define AXGE_PHY_ADDR 3
+
+struct axge_frame_rxhdr {
+ uint32_t status;
+#define AXGE_RX_L4_CSUM_ERR 0x00000001
+#define AXGE_RX_L3_CSUM_ERR 0x00000002
+#define AXGE_RX_L4_TYPE_UDP 0x00000004
+#define AXGE_RX_L4_TYPE_ICMP 0x00000008
+#define AXGE_RX_L4_TYPE_IGMP 0x0000000C
+#define AXGE_RX_L4_TYPE_TCP 0x00000010
+#define AXGE_RX_L4_TYPE_MASK 0x0000001C
+#define AXGE_RX_L3_TYPE_IPV4 0x00000020
+#define AXGE_RX_L3_TYPE_IPV6 0x00000040
+#define AXGE_RX_L3_TYPE_MASK 0x00000060
+#define AXGE_RX_VLAN_IND_MASK 0x00000700
+#define AXGE_RX_GOOD_PKT 0x00000800
+#define AXGE_RX_VLAN_PRI_MASK 0x00007000
+#define AXGE_RX_MBCAST 0x00008000
+#define AXGE_RX_LEN_MASK 0x1FFF0000
+#define AXGE_RX_CRC_ERR 0x20000000
+#define AXGE_RX_MII_ERR 0x40000000
+#define AXGE_RX_DROP_PKT 0x80000000
+#define AXGE_RX_LEN_SHIFT 16
+} __packed;
+
+#define AXGE_RXBYTES(x) (((x) & AXGE_RX_LEN_MASK) >> AXGE_RX_LEN_SHIFT)
+#define AXGE_RX_ERR(x) \
+ ((x) & (AXGE_RX_CRC_ERR | AXGE_RX_MII_ERR | AXGE_RX_DROP_PKT))
+
+struct axge_softc {
+ struct usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb_xfer *sc_xfer[AXGE_N_TRANSFER];
+
+ int sc_flags;
+#define AXGE_FLAG_LINK 0x0001 /* got a link */
+};
+
+#define AXGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define AXGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define AXGE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/freebsd/sys/dev/usb/net/if_cdce.c b/freebsd/sys/dev/usb/net/if_cdce.c
new file mode 100644
index 00000000..ecab60ea
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_cdce.c
@@ -0,0 +1,1583 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $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
+ */
+
+/*
+ * USB Network Control Model (NCM)
+ * http://www.usb.org/developers/devclass_docs/NCM10.zip
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_cdc.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR cdce_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_msctest.h>
+#include <rtems/bsd/local/usb_if.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_suspend_t cdce_suspend;
+static device_resume_t cdce_resume;
+static usb_handle_request_t cdce_handle_request;
+
+static usb_callback_t cdce_bulk_write_callback;
+static usb_callback_t cdce_bulk_read_callback;
+static usb_callback_t cdce_intr_read_callback;
+static usb_callback_t cdce_intr_write_callback;
+
+#if CDCE_HAVE_NCM
+static usb_callback_t cdce_ncm_bulk_write_callback;
+static usb_callback_t cdce_ncm_bulk_read_callback;
+#endif
+
+static uether_fn_t cdce_attach_post;
+static uether_fn_t cdce_init;
+static uether_fn_t cdce_stop;
+static uether_fn_t cdce_start;
+static uether_fn_t cdce_setmulti;
+static uether_fn_t cdce_setpromisc;
+
+static uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t);
+
+#ifdef USB_DEBUG
+static int cdce_debug = 0;
+static int cdce_tx_interval = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet");
+SYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RWTUN, &cdce_debug, 0,
+ "Debug level");
+SYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RWTUN, &cdce_tx_interval, 0,
+ "NCM transmit interval in ms");
+#endif
+
+static const struct usb_config cdce_config[CDCE_N_TRANSFER] = {
+
+ [CDCE_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_RX,
+ .if_index = 0,
+ .frames = CDCE_FRAMES_MAX,
+ .bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
+ .callback = cdce_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ .usb_mode = USB_MODE_DUAL, /* both modes */
+ },
+
+ [CDCE_BULK_TX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_TX,
+ .if_index = 0,
+ .frames = CDCE_FRAMES_MAX,
+ .bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+ .callback = cdce_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ .usb_mode = USB_MODE_DUAL, /* both modes */
+ },
+
+ [CDCE_INTR_RX] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_RX,
+ .if_index = 1,
+ .bufsize = CDCE_IND_SIZE_MAX,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+ .callback = cdce_intr_read_callback,
+ .timeout = 0,
+ .usb_mode = USB_MODE_HOST,
+ },
+
+ [CDCE_INTR_TX] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_TX,
+ .if_index = 1,
+ .bufsize = CDCE_IND_SIZE_MAX,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
+ .callback = cdce_intr_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ .usb_mode = USB_MODE_DEVICE,
+ },
+};
+
+#if CDCE_HAVE_NCM
+static const struct usb_config cdce_ncm_config[CDCE_N_TRANSFER] = {
+
+ [CDCE_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_RX,
+ .if_index = 0,
+ .frames = CDCE_NCM_RX_FRAMES_MAX,
+ .bufsize = (CDCE_NCM_RX_FRAMES_MAX * CDCE_NCM_RX_MAXLEN),
+ .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,},
+ .callback = cdce_ncm_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ .usb_mode = USB_MODE_DUAL, /* both modes */
+ },
+
+ [CDCE_BULK_TX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_TX,
+ .if_index = 0,
+ .frames = CDCE_NCM_TX_FRAMES_MAX,
+ .bufsize = (CDCE_NCM_TX_FRAMES_MAX * CDCE_NCM_TX_MAXLEN),
+ .flags = {.pipe_bof = 1,},
+ .callback = cdce_ncm_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ .usb_mode = USB_MODE_DUAL, /* both modes */
+ },
+
+ [CDCE_INTR_RX] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_RX,
+ .if_index = 1,
+ .bufsize = CDCE_IND_SIZE_MAX,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+ .callback = cdce_intr_read_callback,
+ .timeout = 0,
+ .usb_mode = USB_MODE_HOST,
+ },
+
+ [CDCE_INTR_TX] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_TX,
+ .if_index = 1,
+ .bufsize = CDCE_IND_SIZE_MAX,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
+ .callback = cdce_intr_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ .usb_mode = USB_MODE_DEVICE,
+ },
+};
+#endif
+
+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_END
+};
+
+static driver_t cdce_driver = {
+ .name = "cdce",
+ .methods = cdce_methods,
+ .size = sizeof(struct cdce_softc),
+};
+
+static devclass_t cdce_devclass;
+static eventhandler_tag cdce_etag;
+
+static int cdce_driver_loaded(struct module *, int, void *);
+
+static const STRUCT_USB_HOST_ID cdce_switch_devs[] = {
+ {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E3272_INIT, MSC_EJECT_HUAWEI2)},
+};
+
+static const STRUCT_USB_HOST_ID cdce_host_devs[] = {
+ {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)},
+
+ {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
+ USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x16),
+ USB_DRIVER_INFO(0)},
+ {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
+ USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x46),
+ USB_DRIVER_INFO(0)},
+ {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
+ USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x76),
+ USB_DRIVER_INFO(0)},
+};
+
+static const STRUCT_USB_DUAL_ID cdce_dual_devs[] = {
+ {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)},
+ {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)},
+ {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_NETWORK_CONTROL_MODEL, 0)},
+};
+
+DRIVER_MODULE(cdce, uhub, cdce_driver, cdce_devclass, cdce_driver_loaded, 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);
+USB_PNP_DEVICE_INFO(cdce_switch_devs);
+USB_PNP_HOST_INFO(cdce_host_devs);
+USB_PNP_DUAL_INFO(cdce_dual_devs);
+
+static const struct usb_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,
+};
+
+#if CDCE_HAVE_NCM
+/*------------------------------------------------------------------------*
+ * cdce_ncm_init
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+cdce_ncm_init(struct cdce_softc *sc)
+{
+ struct usb_ncm_parameters temp;
+ struct usb_device_request req;
+ struct usb_ncm_func_descriptor *ufd;
+ uint8_t value[8];
+ int err;
+
+ ufd = usbd_find_descriptor(sc->sc_ue.ue_udev, NULL,
+ sc->sc_ifaces_index[1], UDESC_CS_INTERFACE, 0xFF,
+ UCDC_NCM_FUNC_DESC_SUBTYPE, 0xFF);
+
+ /* verify length of NCM functional descriptor */
+ if (ufd != NULL) {
+ if (ufd->bLength < sizeof(*ufd))
+ ufd = NULL;
+ else
+ DPRINTFN(1, "Found NCM functional descriptor.\n");
+ }
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UCDC_NCM_GET_NTB_PARAMETERS;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_ifaces_index[1];
+ req.wIndex[1] = 0;
+ USETW(req.wLength, sizeof(temp));
+
+ err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req,
+ &temp, 0, NULL, 1000 /* ms */);
+ if (err)
+ return (1);
+
+ /* Read correct set of parameters according to device mode */
+
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+ sc->sc_ncm.rx_max = UGETDW(temp.dwNtbInMaxSize);
+ sc->sc_ncm.tx_max = UGETDW(temp.dwNtbOutMaxSize);
+ sc->sc_ncm.tx_remainder = UGETW(temp.wNdpOutPayloadRemainder);
+ sc->sc_ncm.tx_modulus = UGETW(temp.wNdpOutDivisor);
+ sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpOutAlignment);
+ sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams);
+ } else {
+ sc->sc_ncm.rx_max = UGETDW(temp.dwNtbOutMaxSize);
+ sc->sc_ncm.tx_max = UGETDW(temp.dwNtbInMaxSize);
+ sc->sc_ncm.tx_remainder = UGETW(temp.wNdpInPayloadRemainder);
+ sc->sc_ncm.tx_modulus = UGETW(temp.wNdpInDivisor);
+ sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpInAlignment);
+ sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams);
+ }
+
+ /* Verify maximum receive length */
+
+ if ((sc->sc_ncm.rx_max < 32) ||
+ (sc->sc_ncm.rx_max > CDCE_NCM_RX_MAXLEN)) {
+ DPRINTFN(1, "Using default maximum receive length\n");
+ sc->sc_ncm.rx_max = CDCE_NCM_RX_MAXLEN;
+ }
+
+ /* Verify maximum transmit length */
+
+ if ((sc->sc_ncm.tx_max < 32) ||
+ (sc->sc_ncm.tx_max > CDCE_NCM_TX_MAXLEN)) {
+ DPRINTFN(1, "Using default maximum transmit length\n");
+ sc->sc_ncm.tx_max = CDCE_NCM_TX_MAXLEN;
+ }
+
+ /*
+ * Verify that the structure alignment is:
+ * - power of two
+ * - not greater than the maximum transmit length
+ * - not less than four bytes
+ */
+ if ((sc->sc_ncm.tx_struct_align < 4) ||
+ (sc->sc_ncm.tx_struct_align !=
+ ((-sc->sc_ncm.tx_struct_align) & sc->sc_ncm.tx_struct_align)) ||
+ (sc->sc_ncm.tx_struct_align >= sc->sc_ncm.tx_max)) {
+ DPRINTFN(1, "Using default other alignment: 4 bytes\n");
+ sc->sc_ncm.tx_struct_align = 4;
+ }
+
+ /*
+ * Verify that the payload alignment is:
+ * - power of two
+ * - not greater than the maximum transmit length
+ * - not less than four bytes
+ */
+ if ((sc->sc_ncm.tx_modulus < 4) ||
+ (sc->sc_ncm.tx_modulus !=
+ ((-sc->sc_ncm.tx_modulus) & sc->sc_ncm.tx_modulus)) ||
+ (sc->sc_ncm.tx_modulus >= sc->sc_ncm.tx_max)) {
+ DPRINTFN(1, "Using default transmit modulus: 4 bytes\n");
+ sc->sc_ncm.tx_modulus = 4;
+ }
+
+ /* Verify that the payload remainder */
+
+ if ((sc->sc_ncm.tx_remainder >= sc->sc_ncm.tx_modulus)) {
+ DPRINTFN(1, "Using default transmit remainder: 0 bytes\n");
+ sc->sc_ncm.tx_remainder = 0;
+ }
+
+ /*
+ * Offset the TX remainder so that IP packet payload starts at
+ * the tx_modulus. This is not too clear in the specification.
+ */
+
+ sc->sc_ncm.tx_remainder =
+ (sc->sc_ncm.tx_remainder - ETHER_HDR_LEN) &
+ (sc->sc_ncm.tx_modulus - 1);
+
+ /* Verify max datagrams */
+
+ if (sc->sc_ncm.tx_nframe == 0 ||
+ sc->sc_ncm.tx_nframe > (CDCE_NCM_SUBFRAMES_MAX - 1)) {
+ DPRINTFN(1, "Using default max "
+ "subframes: %u units\n", CDCE_NCM_SUBFRAMES_MAX - 1);
+ /* need to reserve one entry for zero padding */
+ sc->sc_ncm.tx_nframe = (CDCE_NCM_SUBFRAMES_MAX - 1);
+ }
+
+ /* Additional configuration, will fail in device side mode, which is OK. */
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_NCM_SET_NTB_INPUT_SIZE;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_ifaces_index[1];
+ req.wIndex[1] = 0;
+
+ if (ufd != NULL &&
+ (ufd->bmNetworkCapabilities & UCDC_NCM_CAP_MAX_DGRAM)) {
+ USETW(req.wLength, 8);
+ USETDW(value, sc->sc_ncm.rx_max);
+ USETW(value + 4, (CDCE_NCM_SUBFRAMES_MAX - 1));
+ USETW(value + 6, 0);
+ } else {
+ USETW(req.wLength, 4);
+ USETDW(value, sc->sc_ncm.rx_max);
+ }
+
+ err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req,
+ &value, 0, NULL, 1000 /* ms */);
+ if (err) {
+ DPRINTFN(1, "Setting input size "
+ "to %u failed.\n", sc->sc_ncm.rx_max);
+ }
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_NCM_SET_CRC_MODE;
+ USETW(req.wValue, 0); /* no CRC */
+ req.wIndex[0] = sc->sc_ifaces_index[1];
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req,
+ NULL, 0, NULL, 1000 /* ms */);
+ if (err) {
+ DPRINTFN(1, "Setting CRC mode to off failed.\n");
+ }
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_NCM_SET_NTB_FORMAT;
+ USETW(req.wValue, 0); /* NTB-16 */
+ req.wIndex[0] = sc->sc_ifaces_index[1];
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req,
+ NULL, 0, NULL, 1000 /* ms */);
+ if (err) {
+ DPRINTFN(1, "Setting NTB format to 16-bit failed.\n");
+ }
+
+ return (0); /* success */
+}
+#endif
+
+static void
+cdce_test_autoinst(void *arg, struct usb_device *udev,
+ struct usb_attach_arg *uaa)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *id;
+
+ if (uaa->dev_state != UAA_DEV_READY)
+ return;
+
+ iface = usbd_get_iface(udev, 0);
+ if (iface == NULL)
+ return;
+ id = iface->idesc;
+ if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
+ return;
+ if (usbd_lookup_id_by_uaa(cdce_switch_devs, sizeof(cdce_switch_devs), uaa))
+ return; /* no device match */
+
+ if (usb_msc_eject(udev, 0, USB_GET_DRIVER_INFO(uaa)) == 0) {
+ /* success, mark the udev as disappearing */
+ uaa->dev_state = UAA_DEV_EJECTING;
+ }
+}
+
+static int
+cdce_driver_loaded(struct module *mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ /* register our autoinstall handler */
+ cdce_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
+ cdce_test_autoinst, NULL, EVENTHANDLER_PRI_ANY);
+ return (0);
+ case MOD_UNLOAD:
+ EVENTHANDLER_DEREGISTER(usb_dev_configured, cdce_etag);
+ return (0);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+static int
+cdce_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ int error;
+
+ error = usbd_lookup_id_by_uaa(cdce_host_devs, sizeof(cdce_host_devs), uaa);
+ if (error)
+ error = usbd_lookup_id_by_uaa(cdce_dual_devs, sizeof(cdce_dual_devs), uaa);
+ return (error);
+}
+
+static void
+cdce_attach_post(struct usb_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+static int
+cdce_attach(device_t dev)
+{
+ struct cdce_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct usb_interface *iface;
+ const struct usb_cdc_union_descriptor *ud;
+ const struct usb_interface_descriptor *id;
+ const struct usb_cdc_ethernet_descriptor *ued;
+ const struct usb_config *pcfg;
+ uint32_t seed;
+ int error;
+ uint8_t i;
+ uint8_t data_iface_no;
+ char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+ sc->sc_ue.ue_udev = uaa->device;
+
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ ud = usbd_find_descriptor
+ (uaa->device, NULL, uaa->info.bIfaceIndex,
+ UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_UNION, 0xFF);
+
+ if ((ud == NULL) || (ud->bLength < sizeof(*ud)) ||
+ (sc->sc_flags & CDCE_FLAG_NO_UNION)) {
+ DPRINTFN(1, "No union descriptor!\n");
+ sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex;
+ sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
+ goto alloc_transfers;
+ }
+ data_iface_no = ud->bSlaveInterface[0];
+
+ for (i = 0;; i++) {
+
+ iface = usbd_get_iface(uaa->device, i);
+
+ if (iface) {
+
+ id = usbd_get_interface_descriptor(iface);
+
+ if (id && (id->bInterfaceNumber == data_iface_no)) {
+ sc->sc_ifaces_index[0] = i;
+ sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
+ usbd_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:
+
+ pcfg = cdce_config; /* Default Configuration */
+
+ for (i = 0; i != 32; i++) {
+
+ error = usbd_set_alt_interface_index(uaa->device,
+ sc->sc_ifaces_index[0], i);
+ if (error)
+ break;
+#if CDCE_HAVE_NCM
+ if ((i == 0) && (cdce_ncm_init(sc) == 0))
+ pcfg = cdce_ncm_config;
+#endif
+ error = usbd_transfer_setup(uaa->device,
+ sc->sc_ifaces_index, sc->sc_xfer,
+ pcfg, CDCE_N_TRANSFER, sc, &sc->sc_mtx);
+
+ if (error == 0)
+ break;
+ }
+
+ if (error || (i == 32)) {
+ device_printf(dev, "No valid alternate "
+ "setting found\n");
+ goto detach;
+ }
+
+ ued = usbd_find_descriptor
+ (uaa->device, NULL, uaa->info.bIfaceIndex,
+ UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_ENF, 0xFF);
+
+ if ((ued == NULL) || (ued->bLength < sizeof(*ued))) {
+ error = USB_ERR_INVAL;
+ } else {
+ error = usbd_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");
+ seed = ticks;
+ sc->sc_ue.ue_eaddr[0] = 0x2a;
+ memcpy(&sc->sc_ue.ue_eaddr[1], &seed, sizeof(uint32_t));
+ sc->sc_ue.ue_eaddr[5] = device_get_unit(dev);
+
+ } else {
+
+ memset(sc->sc_ue.ue_eaddr, 0, 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->usb_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 = uether_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 usb_ether *ue = &sc->sc_ue;
+
+ /* stop all USB transfers first */
+ usbd_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+cdce_start(struct usb_ether *ue)
+{
+ struct cdce_softc *sc = uether_getsc(ue);
+
+ /*
+ * Start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[CDCE_BULK_TX]);
+ usbd_transfer_start(sc->sc_xfer[CDCE_BULK_RX]);
+}
+
+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 usb_xfer *xfer, usb_error_t error)
+{
+ struct cdce_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ struct mbuf *mt;
+ uint32_t crc;
+ uint8_t x;
+ int actlen, aframes;
+
+ usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
+
+ DPRINTFN(1, "\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
+ actlen, aframes);
+
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /* 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);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ continue;
+ }
+ }
+ if (m->m_len != m->m_pkthdr.len) {
+ mt = m_defrag(m, M_NOWAIT);
+ if (mt == NULL) {
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ continue;
+ }
+ m = mt;
+ }
+ if (m->m_pkthdr.len > MCLBYTES) {
+ m->m_pkthdr.len = MCLBYTES;
+ }
+ sc->sc_tx_buf[x] = m;
+ usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
+
+ /*
+ * If there's a BPF listener, bounce a copy of
+ * this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+ }
+ if (x != 0) {
+ usbd_xfer_set_frames(xfer, x);
+
+ usbd_transfer_submit(xfer);
+ }
+ break;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ /* free all previous TX buffers */
+ cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
+
+ /* count output errors */
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ }
+ 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 usb_ether *ue)
+{
+ struct cdce_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ CDCE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ /* start interrupt transfer */
+ usbd_transfer_start(sc->sc_xfer[CDCE_INTR_RX]);
+ usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]);
+
+ /*
+ * Stall data write direction, which depends on USB mode.
+ *
+ * Some USB host stacks (e.g. Mac OS X) don't clears stall
+ * bit as it should, so set it in our host mode only.
+ */
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
+ usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]);
+
+ /* start data transfers */
+ cdce_start(ue);
+}
+
+static void
+cdce_stop(struct usb_ether *ue)
+{
+ struct cdce_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ CDCE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_RX]);
+ usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_TX]);
+ usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_RX]);
+ usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_TX]);
+}
+
+static void
+cdce_setmulti(struct usb_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+static void
+cdce_setpromisc(struct usb_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+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 usb_xfer *xfer, usb_error_t error)
+{
+ struct cdce_softc *sc = usbd_xfer_softc(xfer);
+ struct mbuf *m;
+ uint8_t x;
+ int actlen;
+ int aframes;
+ int len;
+
+ usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
+
+ for (x = 0; x != aframes; x++) {
+
+ m = sc->sc_rx_buf[x];
+ sc->sc_rx_buf[x] = NULL;
+ len = usbd_xfer_frame_len(xfer, x);
+
+ /* Strip off CRC added by Zaurus, if any */
+ if ((sc->sc_flags & CDCE_FLAG_ZAURUS) && len >= 14)
+ len -= 4;
+
+ if (len < (int)sizeof(struct ether_header)) {
+ m_freem(m);
+ continue;
+ }
+ /* queue up mbuf */
+ uether_rxmbuf(&sc->sc_ue, m, len);
+ }
+
+ /* 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 = uether_newbuf();
+ if (m == NULL)
+ goto tr_stall;
+ sc->sc_rx_buf[x] = m;
+ } else {
+ m = sc->sc_rx_buf[x];
+ }
+
+ usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
+ }
+ /* set number of frames and start hardware */
+ usbd_xfer_set_frames(xfer, x);
+ usbd_transfer_submit(xfer);
+ /* flush any received frames */
+ uether_rxflush(&sc->sc_ue);
+ break;
+
+ default: /* Error */
+ DPRINTF("error = %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+tr_stall:
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ usbd_xfer_set_frames(xfer, 0);
+ usbd_transfer_submit(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 usb_xfer *xfer, usb_error_t error)
+{
+ struct cdce_softc *sc = usbd_xfer_softc(xfer);
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("Received %d bytes\n", actlen);
+
+ /* TODO: decode some indications */
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ break;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* start clear stall */
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+cdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct cdce_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_cdc_notification req;
+ struct usb_page_cache *pc;
+ uint32_t speed;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("Transferred %d bytes\n", actlen);
+
+ switch (sc->sc_notify_state) {
+ case CDCE_NOTIFY_NETWORK_CONNECTION:
+ sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE;
+ break;
+ case CDCE_NOTIFY_SPEED_CHANGE:
+ sc->sc_notify_state = CDCE_NOTIFY_DONE;
+ break;
+ default:
+ break;
+ }
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ /*
+ * Inform host about connection. Required according to USB CDC
+ * specification and communicating to Mac OS X USB host stack.
+ * Some of the values seems ignored by Mac OS X though.
+ */
+ if (sc->sc_notify_state == CDCE_NOTIFY_NETWORK_CONNECTION) {
+ req.bmRequestType = UCDC_NOTIFICATION;
+ req.bNotification = UCDC_N_NETWORK_CONNECTION;
+ req.wIndex[0] = sc->sc_ifaces_index[1];
+ req.wIndex[1] = 0;
+ USETW(req.wValue, 1); /* Connected */
+ USETW(req.wLength, 0);
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &req, sizeof(req));
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+ usbd_xfer_set_frames(xfer, 1);
+ usbd_transfer_submit(xfer);
+
+ } else if (sc->sc_notify_state == CDCE_NOTIFY_SPEED_CHANGE) {
+ req.bmRequestType = UCDC_NOTIFICATION;
+ req.bNotification = UCDC_N_CONNECTION_SPEED_CHANGE;
+ req.wIndex[0] = sc->sc_ifaces_index[1];
+ req.wIndex[1] = 0;
+ USETW(req.wValue, 0);
+ USETW(req.wLength, 8);
+
+ /* Peak theoretical bulk trasfer rate in bits/s */
+ if (usbd_get_speed(sc->sc_ue.ue_udev) != USB_SPEED_FULL)
+ speed = (13 * 512 * 8 * 1000 * 8);
+ else
+ speed = (19 * 64 * 1 * 1000 * 8);
+
+ USETDW(req.data + 0, speed); /* Upstream bit rate */
+ USETDW(req.data + 4, speed); /* Downstream bit rate */
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &req, sizeof(req));
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+ usbd_xfer_set_frames(xfer, 1);
+ usbd_transfer_submit(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+ /* start clear stall */
+ usbd_xfer_set_stall(xfer);
+ }
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static int
+cdce_handle_request(device_t dev,
+ const void *preq, void **pptr, uint16_t *plen,
+ uint16_t offset, uint8_t *pstate)
+{
+ struct cdce_softc *sc = device_get_softc(dev);
+ const struct usb_device_request *req = preq;
+ uint8_t is_complete = *pstate;
+
+ /*
+ * When Mac OS X resumes after suspending it expects
+ * to be notified again after this request.
+ */
+ if (req->bmRequestType == UT_WRITE_CLASS_INTERFACE && \
+ req->bRequest == UCDC_NCM_SET_ETHERNET_PACKET_FILTER) {
+
+ if (is_complete == 1) {
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE;
+ usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]);
+ mtx_unlock(&sc->sc_mtx);
+ }
+
+ return (0);
+ }
+
+ return (ENXIO); /* use builtin handler */
+}
+
+#if CDCE_HAVE_NCM
+static void
+cdce_ncm_tx_zero(struct usb_page_cache *pc,
+ uint32_t start, uint32_t end)
+{
+ if (start >= CDCE_NCM_TX_MAXLEN)
+ return;
+ if (end > CDCE_NCM_TX_MAXLEN)
+ end = CDCE_NCM_TX_MAXLEN;
+
+ usbd_frame_zero(pc, start, end - start);
+}
+
+static uint8_t
+cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
+{
+ struct cdce_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, index);
+ struct mbuf *m;
+ uint32_t rem;
+ uint32_t offset;
+ uint32_t last_offset;
+ uint16_t n;
+ uint8_t retval;
+
+ usbd_xfer_set_frame_offset(xfer, index * CDCE_NCM_TX_MAXLEN, index);
+
+ offset = sizeof(sc->sc_ncm.hdr) +
+ sizeof(sc->sc_ncm.dpt) + sizeof(sc->sc_ncm.dp);
+
+ /* Store last valid offset before alignment */
+ last_offset = offset;
+
+ /* Align offset */
+ offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder,
+ offset, sc->sc_ncm.tx_modulus);
+
+ /* Zero pad */
+ cdce_ncm_tx_zero(pc, last_offset, offset);
+
+ /* buffer full */
+ retval = 2;
+
+ for (n = 0; n != sc->sc_ncm.tx_nframe; n++) {
+
+ /* check if end of transmit buffer is reached */
+
+ if (offset >= sc->sc_ncm.tx_max)
+ break;
+
+ /* compute maximum buffer size */
+
+ rem = sc->sc_ncm.tx_max - offset;
+
+ IFQ_DRV_DEQUEUE(&(ifp->if_snd), m);
+
+ if (m == NULL) {
+ /* buffer not full */
+ retval = 1;
+ break;
+ }
+
+ if (m->m_pkthdr.len > (int)rem) {
+ if (n == 0) {
+ /* The frame won't fit in our buffer */
+ DPRINTFN(1, "Frame too big to be transmitted!\n");
+ m_freem(m);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ n--;
+ continue;
+ }
+ /* Wait till next buffer becomes ready */
+ IFQ_DRV_PREPEND(&(ifp->if_snd), m);
+ break;
+ }
+ usbd_m_copy_in(pc, offset, m, 0, m->m_pkthdr.len);
+
+ USETW(sc->sc_ncm.dp[n].wFrameLength, m->m_pkthdr.len);
+ USETW(sc->sc_ncm.dp[n].wFrameIndex, offset);
+
+ /* Update offset */
+ offset += m->m_pkthdr.len;
+
+ /* Store last valid offset before alignment */
+ last_offset = offset;
+
+ /* Align offset */
+ offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder,
+ offset, sc->sc_ncm.tx_modulus);
+
+ /* Zero pad */
+ cdce_ncm_tx_zero(pc, last_offset, offset);
+
+ /*
+ * If there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ /* Free mbuf */
+
+ m_freem(m);
+
+ /* Pre-increment interface counter */
+
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ }
+
+ if (n == 0)
+ return (0);
+
+ rem = (sizeof(sc->sc_ncm.dpt) + (4 * n) + 4);
+
+ USETW(sc->sc_ncm.dpt.wLength, rem);
+
+ /* zero the rest of the data pointer entries */
+ for (; n != CDCE_NCM_SUBFRAMES_MAX; n++) {
+ USETW(sc->sc_ncm.dp[n].wFrameLength, 0);
+ USETW(sc->sc_ncm.dp[n].wFrameIndex, 0);
+ }
+
+ offset = last_offset;
+
+ /* Align offset */
+ offset = CDCE_NCM_ALIGN(0, offset, CDCE_NCM_TX_MINLEN);
+
+ /* Optimise, save bandwidth and force short termination */
+ if (offset >= sc->sc_ncm.tx_max)
+ offset = sc->sc_ncm.tx_max;
+ else
+ offset ++;
+
+ /* Zero pad */
+ cdce_ncm_tx_zero(pc, last_offset, offset);
+
+ /* set frame length */
+ usbd_xfer_set_frame_len(xfer, index, offset);
+
+ /* Fill out 16-bit header */
+ sc->sc_ncm.hdr.dwSignature[0] = 'N';
+ sc->sc_ncm.hdr.dwSignature[1] = 'C';
+ sc->sc_ncm.hdr.dwSignature[2] = 'M';
+ sc->sc_ncm.hdr.dwSignature[3] = 'H';
+ USETW(sc->sc_ncm.hdr.wHeaderLength, sizeof(sc->sc_ncm.hdr));
+ USETW(sc->sc_ncm.hdr.wBlockLength, offset);
+ USETW(sc->sc_ncm.hdr.wSequence, sc->sc_ncm.tx_seq);
+ USETW(sc->sc_ncm.hdr.wDptIndex, sizeof(sc->sc_ncm.hdr));
+
+ sc->sc_ncm.tx_seq++;
+
+ /* Fill out 16-bit frame table header */
+ sc->sc_ncm.dpt.dwSignature[0] = 'N';
+ sc->sc_ncm.dpt.dwSignature[1] = 'C';
+ sc->sc_ncm.dpt.dwSignature[2] = 'M';
+ sc->sc_ncm.dpt.dwSignature[3] = '0';
+ USETW(sc->sc_ncm.dpt.wNextNdpIndex, 0); /* reserved */
+
+ usbd_copy_in(pc, 0, &(sc->sc_ncm.hdr), sizeof(sc->sc_ncm.hdr));
+ usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr), &(sc->sc_ncm.dpt),
+ sizeof(sc->sc_ncm.dpt));
+ usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr) + sizeof(sc->sc_ncm.dpt),
+ &(sc->sc_ncm.dp), sizeof(sc->sc_ncm.dp));
+ return (retval);
+}
+
+static void
+cdce_ncm_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct cdce_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ uint16_t x;
+ uint8_t temp;
+ int actlen;
+ int aframes;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
+
+ DPRINTFN(10, "transfer complete: "
+ "%u bytes in %u frames\n", actlen, aframes);
+
+ case USB_ST_SETUP:
+ for (x = 0; x != CDCE_NCM_TX_FRAMES_MAX; x++) {
+ temp = cdce_ncm_fill_tx_frames(xfer, x);
+ if (temp == 0)
+ break;
+ if (temp == 1) {
+ x++;
+ break;
+ }
+ }
+
+ if (x != 0) {
+#ifdef USB_DEBUG
+ usbd_xfer_set_interval(xfer, cdce_tx_interval);
+#endif
+ usbd_xfer_set_frames(xfer, x);
+ usbd_transfer_submit(xfer);
+ }
+ break;
+
+ default: /* Error */
+ DPRINTFN(10, "Transfer error: %s\n",
+ usbd_errstr(error));
+
+ /* update error counter */
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ usbd_xfer_set_frames(xfer, 0);
+ usbd_transfer_submit(xfer);
+ }
+ }
+ break;
+ }
+}
+
+static void
+cdce_ncm_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct cdce_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int sumdata;
+ int sumlen;
+ int actlen;
+ int aframes;
+ int temp;
+ int nframes;
+ int x;
+ int offset;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ usbd_xfer_status(xfer, &actlen, &sumlen, &aframes, NULL);
+
+ DPRINTFN(1, "received %u bytes in %u frames\n",
+ actlen, aframes);
+
+ if (actlen < (int)(sizeof(sc->sc_ncm.hdr) +
+ sizeof(sc->sc_ncm.dpt))) {
+ DPRINTFN(1, "frame too short\n");
+ goto tr_setup;
+ }
+ usbd_copy_out(pc, 0, &(sc->sc_ncm.hdr),
+ sizeof(sc->sc_ncm.hdr));
+
+ if ((sc->sc_ncm.hdr.dwSignature[0] != 'N') ||
+ (sc->sc_ncm.hdr.dwSignature[1] != 'C') ||
+ (sc->sc_ncm.hdr.dwSignature[2] != 'M') ||
+ (sc->sc_ncm.hdr.dwSignature[3] != 'H')) {
+ DPRINTFN(1, "invalid HDR signature: "
+ "0x%02x:0x%02x:0x%02x:0x%02x\n",
+ sc->sc_ncm.hdr.dwSignature[0],
+ sc->sc_ncm.hdr.dwSignature[1],
+ sc->sc_ncm.hdr.dwSignature[2],
+ sc->sc_ncm.hdr.dwSignature[3]);
+ goto tr_stall;
+ }
+ temp = UGETW(sc->sc_ncm.hdr.wBlockLength);
+ if (temp > sumlen) {
+ DPRINTFN(1, "unsupported block length %u/%u\n",
+ temp, sumlen);
+ goto tr_stall;
+ }
+ temp = UGETW(sc->sc_ncm.hdr.wDptIndex);
+ if ((int)(temp + sizeof(sc->sc_ncm.dpt)) > actlen) {
+ DPRINTFN(1, "invalid DPT index: 0x%04x\n", temp);
+ goto tr_stall;
+ }
+ usbd_copy_out(pc, temp, &(sc->sc_ncm.dpt),
+ sizeof(sc->sc_ncm.dpt));
+
+ if ((sc->sc_ncm.dpt.dwSignature[0] != 'N') ||
+ (sc->sc_ncm.dpt.dwSignature[1] != 'C') ||
+ (sc->sc_ncm.dpt.dwSignature[2] != 'M') ||
+ (sc->sc_ncm.dpt.dwSignature[3] != '0')) {
+ DPRINTFN(1, "invalid DPT signature"
+ "0x%02x:0x%02x:0x%02x:0x%02x\n",
+ sc->sc_ncm.dpt.dwSignature[0],
+ sc->sc_ncm.dpt.dwSignature[1],
+ sc->sc_ncm.dpt.dwSignature[2],
+ sc->sc_ncm.dpt.dwSignature[3]);
+ goto tr_stall;
+ }
+ nframes = UGETW(sc->sc_ncm.dpt.wLength) / 4;
+
+ /* Subtract size of header and last zero padded entry */
+ if (nframes >= (2 + 1))
+ nframes -= (2 + 1);
+ else
+ nframes = 0;
+
+ DPRINTFN(1, "nframes = %u\n", nframes);
+
+ temp += sizeof(sc->sc_ncm.dpt);
+
+ if ((temp + (4 * nframes)) > actlen)
+ goto tr_stall;
+
+ if (nframes > CDCE_NCM_SUBFRAMES_MAX) {
+ DPRINTFN(1, "Truncating number of frames from %u to %u\n",
+ nframes, CDCE_NCM_SUBFRAMES_MAX);
+ nframes = CDCE_NCM_SUBFRAMES_MAX;
+ }
+ usbd_copy_out(pc, temp, &(sc->sc_ncm.dp), (4 * nframes));
+
+ sumdata = 0;
+
+ for (x = 0; x != nframes; x++) {
+
+ offset = UGETW(sc->sc_ncm.dp[x].wFrameIndex);
+ temp = UGETW(sc->sc_ncm.dp[x].wFrameLength);
+
+ if ((offset == 0) ||
+ (temp < (int)sizeof(struct ether_header)) ||
+ (temp > (MCLBYTES - ETHER_ALIGN))) {
+ DPRINTFN(1, "NULL frame detected at %d\n", x);
+ m = NULL;
+ /* silently ignore this frame */
+ continue;
+ } else if ((offset + temp) > actlen) {
+ DPRINTFN(1, "invalid frame "
+ "detected at %d\n", x);
+ m = NULL;
+ /* silently ignore this frame */
+ continue;
+ } else if (temp > (int)(MHLEN - ETHER_ALIGN)) {
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ } else {
+ m = m_gethdr(M_NOWAIT, MT_DATA);
+ }
+
+ DPRINTFN(16, "frame %u, offset = %u, length = %u \n",
+ x, offset, temp);
+
+ /* check if we have a buffer */
+ if (m) {
+ m->m_len = m->m_pkthdr.len = temp + ETHER_ALIGN;
+ m_adj(m, ETHER_ALIGN);
+
+ usbd_copy_out(pc, offset, m->m_data, temp);
+
+ /* enqueue */
+ uether_rxmbuf(&sc->sc_ue, m, temp);
+
+ sumdata += temp;
+ } else {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ }
+ }
+
+ DPRINTFN(1, "Efficiency: %u/%u bytes\n", sumdata, actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, sc->sc_ncm.rx_max);
+ usbd_xfer_set_frames(xfer, 1);
+ usbd_transfer_submit(xfer);
+ uether_rxflush(&sc->sc_ue); /* must be last */
+ break;
+
+ default: /* Error */
+ DPRINTFN(1, "error = %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+tr_stall:
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ usbd_xfer_set_frames(xfer, 0);
+ usbd_transfer_submit(xfer);
+ }
+ }
+ break;
+ }
+}
+#endif
diff --git a/freebsd/sys/dev/usb/net/if_cdcereg.h b/freebsd/sys/dev/usb/net/if_cdcereg.h
new file mode 100644
index 00000000..1bc2d603
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_cdcereg.h
@@ -0,0 +1,105 @@
+/*-
+ * 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 */
+
+#define CDCE_NCM_TX_MINLEN 512 /* bytes, must be power of two */
+#define CDCE_NCM_TX_MAXLEN (16384 + 4) /* bytes, must be short terminated */
+#define CDCE_NCM_TX_FRAMES_MAX 8 /* units */
+
+#define CDCE_NCM_RX_MAXLEN (1UL << 14) /* bytes */
+#define CDCE_NCM_RX_FRAMES_MAX 1 /* units */
+
+#define CDCE_NCM_SUBFRAMES_MAX 32 /* units */
+
+#define CDCE_NCM_ALIGN(rem,off,mod) \
+ ((uint32_t)(((uint32_t)(rem)) - \
+ ((uint32_t)((-(uint32_t)(off)) & (-(uint32_t)(mod))))))
+
+#ifndef CDCE_HAVE_NCM
+#define CDCE_HAVE_NCM 1
+#endif
+
+enum {
+ CDCE_BULK_RX,
+ CDCE_BULK_TX,
+ CDCE_INTR_RX,
+ CDCE_INTR_TX,
+ CDCE_N_TRANSFER,
+};
+
+struct cdce_ncm {
+ struct usb_ncm16_hdr hdr;
+ struct usb_ncm16_dpt dpt;
+ struct usb_ncm16_dp dp[CDCE_NCM_SUBFRAMES_MAX];
+ uint32_t rx_max;
+ uint32_t tx_max;
+ uint16_t tx_remainder;
+ uint16_t tx_modulus;
+ uint16_t tx_struct_align;
+ uint16_t tx_seq;
+ uint16_t tx_nframe;
+};
+
+struct cdce_softc {
+ struct usb_ether sc_ue;
+ struct mtx sc_mtx;
+#if CDCE_HAVE_NCM
+ struct cdce_ncm sc_ncm;
+#endif
+ struct usb_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_ifaces_index[2];
+ uint8_t sc_notify_state;
+#define CDCE_NOTIFY_NETWORK_CONNECTION 0
+#define CDCE_NOTIFY_SPEED_CHANGE 1
+#define CDCE_NOTIFY_DONE 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/freebsd/sys/dev/usb/net/if_cue.c b/freebsd/sys/dev/usb/net/if_cue.c
new file mode 100644
index 00000000..7110f78e
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_cue.c
@@ -0,0 +1,657 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * 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 transferred using a single bulk
+ * transaction, which helps performance a great deal.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR cue_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.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_USB_HOST_ID cue_devs[] = {
+#define CUE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+ CUE_DEV(CATC, NETMATE),
+ CUE_DEV(CATC, NETMATE2),
+ CUE_DEV(SMARTBRIDGES, SMARTLINK),
+#undef CUE_DEV
+};
+
+/* prototypes */
+
+static device_probe_t cue_probe;
+static device_attach_t cue_attach;
+static device_detach_t cue_detach;
+
+static usb_callback_t cue_bulk_read_callback;
+static usb_callback_t cue_bulk_write_callback;
+
+static uether_fn_t cue_attach_post;
+static uether_fn_t cue_init;
+static uether_fn_t cue_stop;
+static uether_fn_t cue_start;
+static uether_fn_t cue_tick;
+static uether_fn_t cue_setmulti;
+static uether_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 *);
+
+#ifdef USB_DEBUG
+static int cue_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, cue, CTLFLAG_RW, 0, "USB cue");
+SYSCTL_INT(_hw_usb_cue, OID_AUTO, debug, CTLFLAG_RWTUN, &cue_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb_config cue_config[CUE_N_TRANSFER] = {
+
+ [CUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = (MCLBYTES + 2),
+ .flags = {.pipe_bof = 1,},
+ .callback = cue_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+
+ [CUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = (MCLBYTES + 2),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .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_END
+};
+
+static driver_t cue_driver = {
+ .name = "cue",
+ .methods = cue_methods,
+ .size = sizeof(struct cue_softc),
+};
+
+static devclass_t cue_devclass;
+
+DRIVER_MODULE(cue, uhub, 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);
+MODULE_VERSION(cue, 1);
+USB_PNP_HOST_INFO(cue_devs);
+
+static const struct usb_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 usb_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 (uether_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 usb_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)uether_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 usb_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 (uether_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 usb_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 (uether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+cue_getmac(struct cue_softc *sc, void *buf)
+{
+ struct usb_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 (uether_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 usb_ether *ue)
+{
+ struct cue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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 usb_ether *ue)
+{
+ struct cue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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_maddr_rlock(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_maddr_runlock(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 usb_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 (uether_do_request(&sc->sc_ue, &req, NULL, 1000)) {
+ /* ignore any errors */
+ }
+
+ /*
+ * wait a little while for the chip to get its brains in order:
+ */
+ uether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+cue_attach_post(struct usb_ether *ue)
+{
+ struct cue_softc *sc = uether_getsc(ue);
+
+ cue_getmac(sc, ue->ue_eaddr);
+}
+
+static int
+cue_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_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 (usbd_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 usb_attach_arg *uaa = device_get_ivars(dev);
+ struct cue_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = CUE_IFACE_IDX;
+ error = usbd_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 = uether_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 usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+cue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct cue_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = uether_getifp(ue);
+ struct usb_page_cache *pc;
+ uint8_t buf[2];
+ int len;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (actlen <= (int)(2 + sizeof(struct ether_header))) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, buf, 2);
+ actlen -= 2;
+ len = buf[0] | (buf[1] << 8);
+ len = min(actlen, len);
+
+ uether_rxbuf(ue, pc, 2, len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+cue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct cue_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /* 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;
+ usbd_xfer_set_frame_len(xfer, 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);
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, buf, 2);
+ usbd_m_copy_in(pc, 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);
+
+ usbd_transfer_submit(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+cue_tick(struct usb_ether *ue)
+{
+ struct cue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_SINGLECOLL));
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_MULTICOLL));
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_EXCESSCOLL));
+
+ if (cue_csr_read_2(sc, CUE_RX_FRAMEERR))
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+}
+
+static void
+cue_start(struct usb_ether *ue)
+{
+ struct cue_softc *sc = uether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[CUE_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[CUE_BULK_DT_WR]);
+}
+
+static void
+cue_init(struct usb_ether *ue)
+{
+ struct cue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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);
+
+ usbd_xfer_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 usb_ether *ue)
+{
+ struct cue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[CUE_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[CUE_BULK_DT_RD]);
+
+ cue_csr_write_1(sc, CUE_ETHCTL, 0);
+ cue_reset(sc);
+}
diff --git a/freebsd/sys/dev/usb/net/if_cuereg.h b/freebsd/sys/dev/usb/net/if_cuereg.h
new file mode 100644
index 00000000..ff245c05
--- /dev/null
+++ b/freebsd/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 CATC part. */
+enum {
+ CUE_BULK_DT_WR,
+ CUE_BULK_DT_RD,
+ CUE_N_TRANSFER,
+};
+
+struct cue_softc {
+ struct usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb_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/freebsd/sys/dev/usb/net/if_ipheth.c b/freebsd/sys/dev/usb/net/if_ipheth.c
new file mode 100644
index 00000000..025d5208
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_ipheth.c
@@ -0,0 +1,548 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2009 Diego Giagio. 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.
+ */
+
+/*
+ * Thanks to Diego Giagio for figuring out the programming details for
+ * the Apple iPhone Ethernet driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR ipheth_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_iphethvar.h>
+
+static device_probe_t ipheth_probe;
+static device_attach_t ipheth_attach;
+static device_detach_t ipheth_detach;
+
+static usb_callback_t ipheth_bulk_write_callback;
+static usb_callback_t ipheth_bulk_read_callback;
+
+static uether_fn_t ipheth_attach_post;
+static uether_fn_t ipheth_tick;
+static uether_fn_t ipheth_init;
+static uether_fn_t ipheth_stop;
+static uether_fn_t ipheth_start;
+static uether_fn_t ipheth_setmulti;
+static uether_fn_t ipheth_setpromisc;
+
+#ifdef USB_DEBUG
+static int ipheth_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet");
+SYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RWTUN, &ipheth_debug, 0, "Debug level");
+#endif
+
+static const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = {
+
+ [IPHETH_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_RX,
+ .frames = IPHETH_RX_FRAMES_MAX,
+ .bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES),
+ .flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
+ .callback = ipheth_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ },
+
+ [IPHETH_BULK_TX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_TX,
+ .frames = IPHETH_TX_FRAMES_MAX,
+ .bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE),
+ .flags = {.force_short_xfer = 1,},
+ .callback = ipheth_bulk_write_callback,
+ .timeout = IPHETH_TX_TIMEOUT,
+ },
+};
+
+static device_method_t ipheth_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ipheth_probe),
+ DEVMETHOD(device_attach, ipheth_attach),
+ DEVMETHOD(device_detach, ipheth_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t ipheth_driver = {
+ .name = "ipheth",
+ .methods = ipheth_methods,
+ .size = sizeof(struct ipheth_softc),
+};
+
+static devclass_t ipheth_devclass;
+
+static const STRUCT_USB_HOST_ID ipheth_devs[] = {
+#if 0
+ {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+ IPHETH_USBINTF_PROTO)},
+ {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+ IPHETH_USBINTF_PROTO)},
+ {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS,
+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+ IPHETH_USBINTF_PROTO)},
+ {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4,
+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+ IPHETH_USBINTF_PROTO)},
+ {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S,
+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+ IPHETH_USBINTF_PROTO)},
+ {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5,
+ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+ IPHETH_USBINTF_PROTO)},
+#else
+ /* product agnostic interface match */
+ {USB_VENDOR(USB_VENDOR_APPLE),
+ USB_IFACE_CLASS(IPHETH_USBINTF_CLASS),
+ USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS),
+ USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)},
+#endif
+};
+
+DRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0);
+MODULE_VERSION(ipheth, 1);
+MODULE_DEPEND(ipheth, uether, 1, 1, 1);
+MODULE_DEPEND(ipheth, usb, 1, 1, 1);
+MODULE_DEPEND(ipheth, ether, 1, 1, 1);
+USB_PNP_HOST_INFO(ipheth_devs);
+
+static const struct usb_ether_methods ipheth_ue_methods = {
+ .ue_attach_post = ipheth_attach_post,
+ .ue_start = ipheth_start,
+ .ue_init = ipheth_init,
+ .ue_tick = ipheth_tick,
+ .ue_stop = ipheth_stop,
+ .ue_setmulti = ipheth_setmulti,
+ .ue_setpromisc = ipheth_setpromisc,
+};
+
+#define IPHETH_ID(v,p,c,sc,pt) \
+ USB_VENDOR(v), USB_PRODUCT(p), \
+ USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \
+ USB_IFACE_PROTOCOL(pt)
+
+static int
+ipheth_get_mac_addr(struct ipheth_softc *sc)
+{
+ struct usb_device_request req;
+ int error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = IPHETH_CMD_GET_MACADDR;
+ req.wValue[0] = 0;
+ req.wValue[1] = 0;
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ req.wLength[0] = ETHER_ADDR_LEN;
+ req.wLength[1] = 0;
+
+ error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data);
+
+ if (error)
+ return (error);
+
+ memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN);
+
+ return (0);
+}
+
+static int
+ipheth_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa));
+}
+
+static int
+ipheth_attach(device_t dev)
+{
+ struct ipheth_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ int error;
+
+ sc->sc_iface_no = uaa->info.bIfaceIndex;
+
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ error = usbd_set_alt_interface_index(uaa->device,
+ uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM);
+ if (error) {
+ device_printf(dev, "Cannot set alternate setting\n");
+ goto detach;
+ }
+ error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no,
+ sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "Cannot setup USB transfers\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 = &ipheth_ue_methods;
+
+ error = ipheth_get_mac_addr(sc);
+ if (error) {
+ device_printf(dev, "Cannot get MAC address\n");
+ goto detach;
+ }
+
+ error = uether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ ipheth_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+ipheth_detach(device_t dev)
+{
+ struct ipheth_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+
+ /* stop all USB transfers first */
+ usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER);
+
+ uether_ifdetach(ue);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+ipheth_start(struct usb_ether *ue)
+{
+ struct ipheth_softc *sc = uether_getsc(ue);
+
+ /*
+ * Start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]);
+ usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]);
+}
+
+static void
+ipheth_stop(struct usb_ether *ue)
+{
+ struct ipheth_softc *sc = uether_getsc(ue);
+
+ /*
+ * Stop the USB transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]);
+ usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]);
+}
+
+static void
+ipheth_tick(struct usb_ether *ue)
+{
+ struct ipheth_softc *sc = uether_getsc(ue);
+ struct usb_device_request req;
+ int error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = IPHETH_CMD_CARRIER_CHECK;
+ req.wValue[0] = 0;
+ req.wValue[1] = 0;
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ req.wLength[0] = IPHETH_CTRL_BUF_SIZE;
+ req.wLength[1] = 0;
+
+ error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT);
+
+ if (error)
+ return;
+
+ sc->sc_carrier_on =
+ (sc->sc_data[0] == IPHETH_CARRIER_ON);
+}
+
+static void
+ipheth_attach_post(struct usb_ether *ue)
+{
+
+}
+
+static void
+ipheth_init(struct usb_ether *ue)
+{
+ struct ipheth_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ IPHETH_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ /* stall data write direction, which depends on USB mode */
+ usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]);
+
+ /* start data transfers */
+ ipheth_start(ue);
+}
+
+static void
+ipheth_setmulti(struct usb_ether *ue)
+{
+
+}
+
+static void
+ipheth_setpromisc(struct usb_ether *ue)
+{
+
+}
+
+static void
+ipheth_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
+ipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ipheth_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ uint8_t x;
+ int actlen;
+ int aframes;
+
+ usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
+
+ DPRINTFN(1, "\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
+ actlen, aframes);
+
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /* free all previous TX buffers */
+ ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ break;
+
+ usbd_xfer_set_frame_offset(xfer,
+ x * IPHETH_BUF_SIZE, x);
+
+ pc = usbd_xfer_get_frame(xfer, x);
+
+ sc->sc_tx_buf[x] = m;
+
+ if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
+ m->m_pkthdr.len = IPHETH_BUF_SIZE;
+
+ usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
+
+ usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);
+
+ if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
+ usbd_frame_zero(pc, m->m_pkthdr.len,
+ IPHETH_BUF_SIZE - m->m_pkthdr.len);
+ }
+
+ /*
+ * If there's a BPF listener, bounce a copy of
+ * this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+ }
+ if (x != 0) {
+ usbd_xfer_set_frames(xfer, x);
+
+ usbd_transfer_submit(xfer);
+ }
+ break;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ /* free all previous TX buffers */
+ ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
+
+ /* count output errors */
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+ipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ipheth_softc *sc = usbd_xfer_softc(xfer);
+ struct mbuf *m;
+ uint8_t x;
+ int actlen;
+ int aframes;
+ int len;
+
+ usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
+
+ for (x = 0; x != aframes; x++) {
+
+ m = sc->sc_rx_buf[x];
+ sc->sc_rx_buf[x] = NULL;
+ len = usbd_xfer_frame_len(xfer, x);
+
+ if (len < (int)(sizeof(struct ether_header) +
+ IPHETH_RX_ADJ)) {
+ m_freem(m);
+ continue;
+ }
+
+ m_adj(m, IPHETH_RX_ADJ);
+
+ /* queue up mbuf */
+ uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
+ }
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+
+ for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
+ if (sc->sc_rx_buf[x] == NULL) {
+ m = uether_newbuf();
+ if (m == NULL)
+ goto tr_stall;
+
+ /* cancel alignment for ethernet */
+ m_adj(m, ETHER_ALIGN);
+
+ sc->sc_rx_buf[x] = m;
+ } else {
+ m = sc->sc_rx_buf[x];
+ }
+
+ usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
+ }
+ /* set number of frames and start hardware */
+ usbd_xfer_set_frames(xfer, x);
+ usbd_transfer_submit(xfer);
+ /* flush any received frames */
+ uether_rxflush(&sc->sc_ue);
+ break;
+
+ default: /* Error */
+ DPRINTF("error = %s\n", usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ tr_stall:
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ usbd_xfer_set_frames(xfer, 0);
+ usbd_transfer_submit(xfer);
+ break;
+ }
+ /* need to free the RX-mbufs when we are cancelled */
+ ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
+ break;
+ }
+}
diff --git a/freebsd/sys/dev/usb/net/if_iphethvar.h b/freebsd/sys/dev/usb/net/if_iphethvar.h
new file mode 100644
index 00000000..65b0c940
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_iphethvar.h
@@ -0,0 +1,84 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2009 Diego Giagio. 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.
+ */
+
+/*
+ * Thanks to Diego Giagio for figuring out the programming details for
+ * the Apple iPhone Ethernet driver.
+ */
+
+#ifndef _IF_IPHETHVAR_H_
+#define _IF_IPHETHVAR_H_
+
+#define IPHETH_USBINTF_CLASS 255
+#define IPHETH_USBINTF_SUBCLASS 253
+#define IPHETH_USBINTF_PROTO 1
+
+#define IPHETH_BUF_SIZE 1516
+#define IPHETH_TX_TIMEOUT 5000 /* ms */
+
+#define IPHETH_RX_FRAMES_MAX 1
+#define IPHETH_TX_FRAMES_MAX 8
+
+#define IPHETH_RX_ADJ 2
+
+#define IPHETH_CFG_INDEX 0
+#define IPHETH_IF_INDEX 2
+#define IPHETH_ALT_INTFNUM 1
+
+#define IPHETH_CTRL_ENDP 0x00
+#define IPHETH_CTRL_BUF_SIZE 0x40
+#define IPHETH_CTRL_TIMEOUT 5000 /* ms */
+
+#define IPHETH_CMD_GET_MACADDR 0x00
+#define IPHETH_CMD_CARRIER_CHECK 0x45
+
+#define IPHETH_CARRIER_ON 0x04
+
+enum {
+ IPHETH_BULK_TX,
+ IPHETH_BULK_RX,
+ IPHETH_N_TRANSFER,
+};
+
+struct ipheth_softc {
+ struct usb_ether sc_ue;
+ struct mtx sc_mtx;
+
+ struct usb_xfer *sc_xfer[IPHETH_N_TRANSFER];
+ struct mbuf *sc_rx_buf[IPHETH_RX_FRAMES_MAX];
+ struct mbuf *sc_tx_buf[IPHETH_TX_FRAMES_MAX];
+
+ uint8_t sc_data[IPHETH_CTRL_BUF_SIZE];
+ uint8_t sc_iface_no;
+ uint8_t sc_carrier_on;
+};
+
+#define IPHETH_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define IPHETH_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define IPHETH_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
+
+#endif /* _IF_IPHETHVAR_H_ */
diff --git a/freebsd/sys/dev/usb/net/if_kue.c b/freebsd/sys/dev/usb/net/if_kue.c
new file mode 100644
index 00000000..02d3322c
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_kue.c
@@ -0,0 +1,714 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * 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 <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR kue_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.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_USB_HOST_ID kue_devs[] = {
+#define KUE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+ KUE_DEV(3COM, 3C19250),
+ KUE_DEV(3COM, 3C460),
+ KUE_DEV(ABOCOM, URE450),
+ KUE_DEV(ADS, UBS10BT),
+ KUE_DEV(ADS, UBS10BTX),
+ KUE_DEV(AOX, USB101),
+ KUE_DEV(ASANTE, EA),
+ KUE_DEV(ATEN, DSB650C),
+ KUE_DEV(ATEN, UC10T),
+ KUE_DEV(COREGA, ETHER_USB_T),
+ KUE_DEV(DLINK, DSB650C),
+ KUE_DEV(ENTREGA, E45),
+ KUE_DEV(ENTREGA, XX1),
+ KUE_DEV(ENTREGA, XX2),
+ KUE_DEV(IODATA, USBETT),
+ KUE_DEV(JATON, EDA),
+ KUE_DEV(KINGSTON, XX1),
+ KUE_DEV(KLSI, DUH3E10BT),
+ KUE_DEV(KLSI, DUH3E10BTN),
+ KUE_DEV(LINKSYS, USB10T),
+ KUE_DEV(MOBILITY, EA),
+ KUE_DEV(NETGEAR, EA101),
+ KUE_DEV(NETGEAR, EA101X),
+ KUE_DEV(PERACOM, ENET),
+ KUE_DEV(PERACOM, ENET2),
+ KUE_DEV(PERACOM, ENET3),
+ KUE_DEV(PORTGEAR, EA8),
+ KUE_DEV(PORTGEAR, EA9),
+ KUE_DEV(PORTSMITH, EEA),
+ KUE_DEV(SHARK, PA),
+ KUE_DEV(SILICOM, GPE),
+ KUE_DEV(SILICOM, U2E),
+ KUE_DEV(SMC, 2102USB),
+#undef KUE_DEV
+};
+
+/* prototypes */
+
+static device_probe_t kue_probe;
+static device_attach_t kue_attach;
+static device_detach_t kue_detach;
+
+static usb_callback_t kue_bulk_read_callback;
+static usb_callback_t kue_bulk_write_callback;
+
+static uether_fn_t kue_attach_post;
+static uether_fn_t kue_init;
+static uether_fn_t kue_stop;
+static uether_fn_t kue_start;
+static uether_fn_t kue_setmulti;
+static uether_fn_t kue_setpromisc;
+
+static int kue_do_request(struct kue_softc *,
+ struct usb_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 *);
+
+#ifdef USB_DEBUG
+static int kue_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, kue, CTLFLAG_RW, 0, "USB kue");
+SYSCTL_INT(_hw_usb_kue, OID_AUTO, debug, CTLFLAG_RWTUN, &kue_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb_config kue_config[KUE_N_TRANSFER] = {
+
+ [KUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = (MCLBYTES + 2 + 64),
+ .flags = {.pipe_bof = 1,},
+ .callback = kue_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+
+ [KUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = (MCLBYTES + 2),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = kue_bulk_read_callback,
+ .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_END
+};
+
+static driver_t kue_driver = {
+ .name = "kue",
+ .methods = kue_methods,
+ .size = sizeof(struct kue_softc),
+};
+
+static devclass_t kue_devclass;
+
+DRIVER_MODULE(kue, uhub, 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);
+MODULE_VERSION(kue, 1);
+USB_PNP_HOST_INFO(kue_devs);
+
+static const struct usb_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 usb_device_request *req,
+ void *data)
+{
+ usb_error_t err;
+
+ err = uether_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 usb_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 usb_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 usb_device_descriptor *dd;
+ uint16_t hwrev;
+ usb_error_t err;
+
+ dd = usbd_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",
+ usbd_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",
+ usbd_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",
+ usbd_errstr(err));
+ return(ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+kue_setpromisc(struct usb_ether *ue)
+{
+ struct kue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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 usb_ether *ue)
+{
+ struct kue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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_maddr_rlock(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;
+ memcpy(KUE_MCFILT(sc, i),
+ LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ ETHER_ADDR_LEN);
+ i++;
+ }
+ if_maddr_runlock(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 usb_config_descriptor *cd;
+ usb_error_t err;
+
+ cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev);
+
+ err = usbd_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 */
+ uether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+kue_attach_post(struct usb_ether *ue)
+{
+ struct kue_softc *sc = uether_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 usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_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 (usbd_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 usb_attach_arg *uaa = device_get_ivars(dev);
+ struct kue_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = KUE_IFACE_IDX;
+ error = usbd_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 = uether_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 usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, KUE_N_TRANSFER);
+ uether_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 usb_xfer *xfer, usb_error_t error)
+{
+ struct kue_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = uether_getifp(ue);
+ struct usb_page_cache *pc;
+ uint8_t buf[2];
+ int len;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (actlen <= (int)(2 + sizeof(struct ether_header))) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, buf, 2);
+ actlen -= 2;
+ len = buf[0] | (buf[1] << 8);
+ len = min(actlen, len);
+
+ uether_rxbuf(ue, pc, 2, len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+kue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct kue_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ 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");
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /* 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);
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, buf, 2);
+ usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len);
+
+ usbd_frame_zero(pc, temp_len, total_len - temp_len);
+ usbd_xfer_set_frame_len(xfer, 0, total_len);
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usbd_transfer_submit(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+kue_start(struct usb_ether *ue)
+{
+ struct kue_softc *sc = uether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[KUE_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[KUE_BULK_DT_WR]);
+}
+
+static void
+kue_init(struct usb_ether *ue)
+{
+ struct kue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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);
+
+ usbd_xfer_set_stall(sc->sc_xfer[KUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ kue_start(ue);
+}
+
+static void
+kue_stop(struct usb_ether *ue)
+{
+ struct kue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[KUE_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[KUE_BULK_DT_RD]);
+}
diff --git a/freebsd/sys/dev/usb/net/if_kuefw.h b/freebsd/sys/dev/usb/net/if_kuefw.h
new file mode 100644
index 00000000..2b055a92
--- /dev/null
+++ b/freebsd/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/freebsd/sys/dev/usb/net/if_kuereg.h b/freebsd/sys/dev/usb/net/if_kuereg.h
new file mode 100644
index 00000000..16ad044d
--- /dev/null
+++ b/freebsd/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 usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct kue_ether_desc sc_desc;
+ struct usb_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/freebsd/sys/dev/usb/net/if_mos.c b/freebsd/sys/dev/usb/net/if_mos.c
new file mode 100644
index 00000000..4c997fa4
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_mos.c
@@ -0,0 +1,1032 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2011 Rick van der Zwet <info@rickvanderzwet.nl>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * Copyright (c) 2008 Johann Christian Rode <jcrode@gmx.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * Copyright (c) 2005, 2006, 2007 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * 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$");
+
+/*
+ * Moschip MCS7730/MCS7830/MCS7832 USB to Ethernet controller
+ * The datasheet is available at the following URL:
+ * http://www.moschip.com/data/products/MCS7830/Data%20Sheet_7830.pdf
+ */
+
+/*
+ * The FreeBSD if_mos.c driver is based on various different sources:
+ * The vendor provided driver at the following URL:
+ * http://www.moschip.com/data/products/MCS7830/Driver_FreeBSD_7830.tar.gz
+ *
+ * Mixed together with the OpenBSD if_mos.c driver for validation and checking
+ * and the FreeBSD if_reu.c as reference for the USB Ethernet framework.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR mos_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+
+//#include <dev/usb/net/if_mosreg.h>
+#include "if_mosreg.h"
+
+#ifdef USB_DEBUG
+static int mos_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, mos, CTLFLAG_RW, 0, "USB mos");
+SYSCTL_INT(_hw_usb_mos, OID_AUTO, debug, CTLFLAG_RWTUN, &mos_debug, 0,
+ "Debug level");
+#endif
+
+#define MOS_DPRINTFN(fmt,...) \
+ DPRINTF("mos: %s: " fmt "\n",__FUNCTION__,## __VA_ARGS__)
+
+#define USB_PRODUCT_MOSCHIP_MCS7730 0x7730
+#define USB_PRODUCT_SITECOMEU_LN030 0x0021
+
+
+
+/* Various supported device vendors/products. */
+static const STRUCT_USB_HOST_ID mos_devs[] = {
+ {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7730, MCS7730)},
+ {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7830, MCS7830)},
+ {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7832, MCS7832)},
+ {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN030, MCS7830)},
+};
+
+static int mos_probe(device_t dev);
+static int mos_attach(device_t dev);
+static void mos_attach_post(struct usb_ether *ue);
+static int mos_detach(device_t dev);
+
+static void mos_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error);
+static void mos_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error);
+static void mos_intr_callback(struct usb_xfer *xfer, usb_error_t error);
+static void mos_tick(struct usb_ether *);
+static void mos_start(struct usb_ether *);
+static void mos_init(struct usb_ether *);
+static void mos_chip_init(struct mos_softc *);
+static void mos_stop(struct usb_ether *);
+static int mos_miibus_readreg(device_t, int, int);
+static int mos_miibus_writereg(device_t, int, int, int);
+static void mos_miibus_statchg(device_t);
+static int mos_ifmedia_upd(struct ifnet *);
+static void mos_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static void mos_reset(struct mos_softc *sc);
+
+static int mos_reg_read_1(struct mos_softc *, int);
+static int mos_reg_read_2(struct mos_softc *, int);
+static int mos_reg_write_1(struct mos_softc *, int, int);
+static int mos_reg_write_2(struct mos_softc *, int, int);
+static int mos_readmac(struct mos_softc *, uint8_t *);
+static int mos_writemac(struct mos_softc *, uint8_t *);
+static int mos_write_mcast(struct mos_softc *, u_char *);
+
+static void mos_setmulti(struct usb_ether *);
+static void mos_setpromisc(struct usb_ether *);
+
+static const struct usb_config mos_config[MOS_ENDPT_MAX] = {
+
+ [MOS_ENDPT_TX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = (MCLBYTES + 2),
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = mos_bulk_write_callback,
+ .timeout = 10000,
+ },
+
+ [MOS_ENDPT_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = mos_bulk_read_callback,
+ },
+
+ [MOS_ENDPT_INTR] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = 0,
+ .callback = mos_intr_callback,
+ },
+};
+
+static device_method_t mos_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mos_probe),
+ DEVMETHOD(device_attach, mos_attach),
+ DEVMETHOD(device_detach, mos_detach),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, mos_miibus_readreg),
+ DEVMETHOD(miibus_writereg, mos_miibus_writereg),
+ DEVMETHOD(miibus_statchg, mos_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t mos_driver = {
+ .name = "mos",
+ .methods = mos_methods,
+ .size = sizeof(struct mos_softc)
+};
+
+static devclass_t mos_devclass;
+
+DRIVER_MODULE(mos, uhub, mos_driver, mos_devclass, NULL, 0);
+DRIVER_MODULE(miibus, mos, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(mos, uether, 1, 1, 1);
+MODULE_DEPEND(mos, usb, 1, 1, 1);
+MODULE_DEPEND(mos, ether, 1, 1, 1);
+MODULE_DEPEND(mos, miibus, 1, 1, 1);
+USB_PNP_HOST_INFO(mos_devs);
+
+static const struct usb_ether_methods mos_ue_methods = {
+ .ue_attach_post = mos_attach_post,
+ .ue_start = mos_start,
+ .ue_init = mos_init,
+ .ue_stop = mos_stop,
+ .ue_tick = mos_tick,
+ .ue_setmulti = mos_setmulti,
+ .ue_setpromisc = mos_setpromisc,
+ .ue_mii_upd = mos_ifmedia_upd,
+ .ue_mii_sts = mos_ifmedia_sts,
+};
+
+
+static int
+mos_reg_read_1(struct mos_softc *sc, int reg)
+{
+ struct usb_device_request req;
+ usb_error_t err;
+ uByte val = 0;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = MOS_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = uether_do_request(&sc->sc_ue, &req, &val, 1000);
+
+ if (err) {
+ MOS_DPRINTFN("mos_reg_read_1 error, reg: %d\n", reg);
+ return (-1);
+ }
+ return (val);
+}
+
+static int
+mos_reg_read_2(struct mos_softc *sc, int reg)
+{
+ struct usb_device_request req;
+ usb_error_t err;
+ uWord val;
+
+ USETW(val, 0);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = MOS_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = uether_do_request(&sc->sc_ue, &req, &val, 1000);
+
+ if (err) {
+ MOS_DPRINTFN("mos_reg_read_2 error, reg: %d", reg);
+ return (-1);
+ }
+ return (UGETW(val));
+}
+
+static int
+mos_reg_write_1(struct mos_softc *sc, int reg, int aval)
+{
+ struct usb_device_request req;
+ usb_error_t err;
+ uByte val;
+ val = aval;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MOS_UR_WRITEREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = uether_do_request(&sc->sc_ue, &req, &val, 1000);
+
+ if (err) {
+ MOS_DPRINTFN("mos_reg_write_1 error, reg: %d", reg);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+mos_reg_write_2(struct mos_softc *sc, int reg, int aval)
+{
+ struct usb_device_request req;
+ usb_error_t err;
+ uWord val;
+
+ USETW(val, aval);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MOS_UR_WRITEREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = uether_do_request(&sc->sc_ue, &req, &val, 1000);
+
+ if (err) {
+ MOS_DPRINTFN("mos_reg_write_2 error, reg: %d", reg);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+mos_readmac(struct mos_softc *sc, u_char *mac)
+{
+ struct usb_device_request req;
+ usb_error_t err;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = MOS_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, MOS_MAC);
+ USETW(req.wLength, ETHER_ADDR_LEN);
+
+ err = uether_do_request(&sc->sc_ue, &req, mac, 1000);
+
+ if (err) {
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+mos_writemac(struct mos_softc *sc, uint8_t *mac)
+{
+ struct usb_device_request req;
+ usb_error_t err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MOS_UR_WRITEREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, MOS_MAC);
+ USETW(req.wLength, ETHER_ADDR_LEN);
+
+ err = uether_do_request(&sc->sc_ue, &req, mac, 1000);
+
+ if (err) {
+ MOS_DPRINTFN("mos_writemac error");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+mos_write_mcast(struct mos_softc *sc, u_char *hashtbl)
+{
+ struct usb_device_request req;
+ usb_error_t err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MOS_UR_WRITEREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, MOS_MCAST_TABLE);
+ USETW(req.wLength, 8);
+
+ err = uether_do_request(&sc->sc_ue, &req, hashtbl, 1000);
+
+ if (err) {
+ MOS_DPRINTFN("mos_reg_mcast error");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+mos_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct mos_softc *sc = device_get_softc(dev);
+ uWord val;
+ int i, res, locked;
+
+ USETW(val, 0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ MOS_LOCK(sc);
+
+ mos_reg_write_2(sc, MOS_PHY_DATA, 0);
+ mos_reg_write_1(sc, MOS_PHY_CTL, (phy & MOS_PHYCTL_PHYADDR) |
+ MOS_PHYCTL_READ);
+ mos_reg_write_1(sc, MOS_PHY_STS, (reg & MOS_PHYSTS_PHYREG) |
+ MOS_PHYSTS_PENDING);
+
+ for (i = 0; i < MOS_TIMEOUT; i++) {
+ if (mos_reg_read_1(sc, MOS_PHY_STS) & MOS_PHYSTS_READY)
+ break;
+ }
+ if (i == MOS_TIMEOUT) {
+ MOS_DPRINTFN("MII read timeout");
+ }
+ res = mos_reg_read_2(sc, MOS_PHY_DATA);
+
+ if (!locked)
+ MOS_UNLOCK(sc);
+ return (res);
+}
+
+static int
+mos_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct mos_softc *sc = device_get_softc(dev);
+ int i, locked;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ MOS_LOCK(sc);
+
+ mos_reg_write_2(sc, MOS_PHY_DATA, val);
+ mos_reg_write_1(sc, MOS_PHY_CTL, (phy & MOS_PHYCTL_PHYADDR) |
+ MOS_PHYCTL_WRITE);
+ mos_reg_write_1(sc, MOS_PHY_STS, (reg & MOS_PHYSTS_PHYREG) |
+ MOS_PHYSTS_PENDING);
+
+ for (i = 0; i < MOS_TIMEOUT; i++) {
+ if (mos_reg_read_1(sc, MOS_PHY_STS) & MOS_PHYSTS_READY)
+ break;
+ }
+ if (i == MOS_TIMEOUT)
+ MOS_DPRINTFN("MII write timeout");
+
+ if (!locked)
+ MOS_UNLOCK(sc);
+ return 0;
+}
+
+static void
+mos_miibus_statchg(device_t dev)
+{
+ struct mos_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ int val, err, locked;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ MOS_LOCK(sc);
+
+ /* disable RX, TX prior to changing FDX, SPEEDSEL */
+ val = mos_reg_read_1(sc, MOS_CTL);
+ val &= ~(MOS_CTL_TX_ENB | MOS_CTL_RX_ENB);
+ mos_reg_write_1(sc, MOS_CTL, val);
+
+ /* reset register which counts dropped frames */
+ mos_reg_write_1(sc, MOS_FRAME_DROP_CNT, 0);
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ val |= MOS_CTL_FDX_ENB;
+ else
+ val &= ~(MOS_CTL_FDX_ENB);
+
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_100_TX:
+ val |= MOS_CTL_SPEEDSEL;
+ break;
+ case IFM_10_T:
+ val &= ~(MOS_CTL_SPEEDSEL);
+ break;
+ }
+
+ /* re-enable TX, RX */
+ val |= (MOS_CTL_TX_ENB | MOS_CTL_RX_ENB);
+ err = mos_reg_write_1(sc, MOS_CTL, val);
+
+ if (err)
+ MOS_DPRINTFN("media change failed");
+
+ if (!locked)
+ MOS_UNLOCK(sc);
+}
+
+/*
+ * Set media options.
+ */
+static int
+mos_ifmedia_upd(struct ifnet *ifp)
+{
+ struct mos_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+ struct mii_softc *miisc;
+ int error;
+
+ MOS_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->mos_link = 0;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ error = mii_mediachg(mii);
+ return (error);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+mos_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct mos_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ MOS_LOCK(sc);
+ mii_pollstat(mii);
+
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ MOS_UNLOCK(sc);
+}
+
+static void
+mos_setpromisc(struct usb_ether *ue)
+{
+ struct mos_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ uint8_t rxmode;
+
+ MOS_LOCK_ASSERT(sc, MA_OWNED);
+
+ rxmode = mos_reg_read_1(sc, MOS_CTL);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC) {
+ rxmode |= MOS_CTL_RX_PROMISC;
+ } else {
+ rxmode &= ~MOS_CTL_RX_PROMISC;
+ }
+
+ mos_reg_write_1(sc, MOS_CTL, rxmode);
+}
+
+
+
+static void
+mos_setmulti(struct usb_ether *ue)
+{
+ struct mos_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+ struct ifmultiaddr *ifma;
+
+ uint32_t h = 0;
+ uint8_t rxmode;
+ uint8_t hashtbl[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ int allmulti = 0;
+
+ MOS_LOCK_ASSERT(sc, MA_OWNED);
+
+ rxmode = mos_reg_read_1(sc, MOS_CTL);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC)
+ allmulti = 1;
+
+ /* get all new ones */
+ if_maddr_rlock(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK) {
+ allmulti = 1;
+ continue;
+ }
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ hashtbl[h / 8] |= 1 << (h % 8);
+ }
+ if_maddr_runlock(ifp);
+
+ /* now program new ones */
+ if (allmulti == 1) {
+ rxmode |= MOS_CTL_ALLMULTI;
+ mos_reg_write_1(sc, MOS_CTL, rxmode);
+ } else {
+ rxmode &= ~MOS_CTL_ALLMULTI;
+ mos_write_mcast(sc, (void *)&hashtbl);
+ mos_reg_write_1(sc, MOS_CTL, rxmode);
+ }
+}
+
+static void
+mos_reset(struct mos_softc *sc)
+{
+ uint8_t ctl;
+
+ ctl = mos_reg_read_1(sc, MOS_CTL);
+ ctl &= ~(MOS_CTL_RX_PROMISC | MOS_CTL_ALLMULTI | MOS_CTL_TX_ENB |
+ MOS_CTL_RX_ENB);
+ /* Disable RX, TX, promiscuous and allmulticast mode */
+ mos_reg_write_1(sc, MOS_CTL, ctl);
+
+ /* Reset frame drop counter register to zero */
+ mos_reg_write_1(sc, MOS_FRAME_DROP_CNT, 0);
+
+ /* Wait a little while for the chip to get its brains in order. */
+ usb_pause_mtx(&sc->sc_mtx, hz / 128);
+ return;
+}
+
+static void
+mos_chip_init(struct mos_softc *sc)
+{
+ int i;
+
+ /*
+ * Rev.C devices have a pause threshold register which needs to be set
+ * at startup.
+ */
+ if (mos_reg_read_1(sc, MOS_PAUSE_TRHD) != -1) {
+ for (i = 0; i < MOS_PAUSE_REWRITES; i++)
+ mos_reg_write_1(sc, MOS_PAUSE_TRHD, 0);
+ }
+ sc->mos_phyaddrs[0] = 1;
+ sc->mos_phyaddrs[1] = 0xFF;
+}
+
+/*
+ * Probe for a MCS7x30 chip.
+ */
+static int
+mos_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ int retval;
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != MOS_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != MOS_IFACE_IDX)
+ return (ENXIO);
+
+ retval = usbd_lookup_id_by_uaa(mos_devs, sizeof(mos_devs), uaa);
+ return (retval);
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+mos_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct mos_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->mos_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = MOS_IFACE_IDX;
+ error = usbd_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, mos_config, MOS_ENDPT_MAX,
+ 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 = &mos_ue_methods;
+
+
+ if (sc->mos_flags & MCS7730) {
+ MOS_DPRINTFN("model: MCS7730");
+ } else if (sc->mos_flags & MCS7830) {
+ MOS_DPRINTFN("model: MCS7830");
+ } else if (sc->mos_flags & MCS7832) {
+ MOS_DPRINTFN("model: MCS7832");
+ }
+ error = uether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0);
+
+
+detach:
+ mos_detach(dev);
+ return (ENXIO);
+}
+
+
+static void
+mos_attach_post(struct usb_ether *ue)
+{
+ struct mos_softc *sc = uether_getsc(ue);
+ int err;
+
+ /* Read MAC address, inform the world. */
+ err = mos_readmac(sc, ue->ue_eaddr);
+
+ if (err)
+ MOS_DPRINTFN("couldn't get MAC address");
+
+ MOS_DPRINTFN("address: %s", ether_sprintf(ue->ue_eaddr));
+
+ mos_chip_init(sc);
+}
+
+static int
+mos_detach(device_t dev)
+{
+ struct mos_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, MOS_ENDPT_MAX);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+
+
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void
+mos_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct mos_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = uether_getifp(ue);
+
+ uint8_t rxstat = 0;
+ uint32_t actlen;
+ uint16_t pktlen = 0;
+ struct usb_page_cache *pc;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+ pc = usbd_xfer_get_frame(xfer, 0);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ MOS_DPRINTFN("actlen : %d", actlen);
+ if (actlen <= 1) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ /* evaluate status byte at the end */
+ usbd_copy_out(pc, actlen - sizeof(rxstat), &rxstat,
+ sizeof(rxstat));
+
+ if (rxstat != MOS_RXSTS_VALID) {
+ MOS_DPRINTFN("erroneous frame received");
+ if (rxstat & MOS_RXSTS_SHORT_FRAME)
+ MOS_DPRINTFN("frame size less than 64 bytes");
+ if (rxstat & MOS_RXSTS_LARGE_FRAME) {
+ MOS_DPRINTFN("frame size larger than "
+ "1532 bytes");
+ }
+ if (rxstat & MOS_RXSTS_CRC_ERROR)
+ MOS_DPRINTFN("CRC error");
+ if (rxstat & MOS_RXSTS_ALIGN_ERROR)
+ MOS_DPRINTFN("alignment error");
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ /* Remember the last byte was used for the status fields */
+ pktlen = actlen - 1;
+ if (pktlen < sizeof(struct ether_header)) {
+ MOS_DPRINTFN("error: pktlen %d is smaller "
+ "than ether_header %zd", pktlen,
+ sizeof(struct ether_header));
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ uether_rxbuf(ue, pc, 0, actlen);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+ default:
+ MOS_DPRINTFN("bulk read error, %s", usbd_errstr(error));
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ MOS_DPRINTFN("start rx %i", usbd_xfer_max_len(xfer));
+ return;
+ }
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+static void
+mos_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct mos_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+
+
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ MOS_DPRINTFN("transfer of complete");
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ /*
+ * XXX: don't send anything if there is no link?
+ */
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ return;
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
+
+ usbd_xfer_set_frame_len(xfer, 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);
+
+ usbd_transfer_submit(xfer);
+
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ return;
+ default:
+ MOS_DPRINTFN("usb error on tx: %s\n", usbd_errstr(error));
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+mos_tick(struct usb_ether *ue)
+{
+ struct mos_softc *sc = uether_getsc(ue);
+ struct mii_data *mii = GET_MII(sc);
+
+ MOS_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if (!sc->mos_link && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ MOS_DPRINTFN("got link");
+ sc->mos_link++;
+ mos_start(ue);
+ }
+}
+
+
+static void
+mos_start(struct usb_ether *ue)
+{
+ struct mos_softc *sc = uether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[MOS_ENDPT_TX]);
+ usbd_transfer_start(sc->sc_xfer[MOS_ENDPT_RX]);
+ usbd_transfer_start(sc->sc_xfer[MOS_ENDPT_INTR]);
+}
+
+static void
+mos_init(struct usb_ether *ue)
+{
+ struct mos_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+ uint8_t rxmode;
+
+ MOS_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* Cancel pending I/O and free all RX/TX buffers. */
+ mos_reset(sc);
+
+ /* Write MAC address */
+ mos_writemac(sc, IF_LLADDR(ifp));
+
+ /* Read and set transmitter IPG values */
+ sc->mos_ipgs[0] = mos_reg_read_1(sc, MOS_IPG0);
+ sc->mos_ipgs[1] = mos_reg_read_1(sc, MOS_IPG1);
+ mos_reg_write_1(sc, MOS_IPG0, sc->mos_ipgs[0]);
+ mos_reg_write_1(sc, MOS_IPG1, sc->mos_ipgs[1]);
+
+ /*
+ * Enable receiver and transmitter, bridge controls speed/duplex
+ * mode
+ */
+ rxmode = mos_reg_read_1(sc, MOS_CTL);
+ rxmode |= MOS_CTL_RX_ENB | MOS_CTL_TX_ENB | MOS_CTL_BS_ENB;
+ rxmode &= ~(MOS_CTL_SLEEP);
+
+ mos_setpromisc(ue);
+
+ /* XXX: broadcast mode? */
+ mos_reg_write_1(sc, MOS_CTL, rxmode);
+
+ /* Load the multicast filter. */
+ mos_setmulti(ue);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ mos_start(ue);
+}
+
+
+static void
+mos_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct mos_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ uint32_t pkt;
+ int actlen;
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+ MOS_DPRINTFN("actlen %i", actlen);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, &pkt, sizeof(pkt));
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ return;
+ default:
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+mos_stop(struct usb_ether *ue)
+{
+ struct mos_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ mos_reset(sc);
+
+ MOS_LOCK_ASSERT(sc, MA_OWNED);
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /* stop all the transfers, if not already stopped */
+ usbd_transfer_stop(sc->sc_xfer[MOS_ENDPT_TX]);
+ usbd_transfer_stop(sc->sc_xfer[MOS_ENDPT_RX]);
+ usbd_transfer_stop(sc->sc_xfer[MOS_ENDPT_INTR]);
+
+ sc->mos_link = 0;
+}
diff --git a/freebsd/sys/dev/usb/net/if_mosreg.h b/freebsd/sys/dev/usb/net/if_mosreg.h
new file mode 100644
index 00000000..c811d552
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_mosreg.h
@@ -0,0 +1,176 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2010, 2011 Rick van der Zwet <info@rickvanderzwet.nl>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * Copyright (c) 2008 Johann Christian Rode <jcrode@gmx.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * 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 Ravikanth.
+ * 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.
+ *
+ */
+
+/*
+ * Register definitions for the Moschip MCS7x30 ethernet controller.
+ */
+#define MOS_MCAST_TABLE 0x00
+#define MOS_IPG0 0x08
+#define MOS_IPG1 0x09
+#define MOS_PHY_DATA0 0x0a
+#define MOS_PHY_DATA1 0x0b
+#define MOS_PHY_CTL 0x0c
+#define MOS_PHY_STS 0x0d
+#define MOS_PHY_DATA MOS_PHY_DATA0
+#define MOS_CTL 0x0e
+#define MOS_MAC0 0x0f
+#define MOS_MAC1 0x10
+#define MOS_MAC2 0x11
+#define MOS_MAC3 0x12
+#define MOS_MAC4 0x13
+#define MOS_MAC5 0x14
+#define MOS_MAC MOS_MAC0
+/* apparently only available on hardware rev. C */
+#define MOS_FRAME_DROP_CNT 0x15
+#define MOS_PAUSE_TRHD 0x16
+
+#define MOS_PHYCTL_PHYADDR 0x1f
+#define MOS_PHYCTL_WRITE 0x20
+#define MOS_PHYCTL_READ 0x40
+
+#define MOS_PHYSTS_PHYREG 0x1f
+#define MOS_PHYSTS_READY 0x40
+#define MOS_PHYSTS_PENDING 0x80
+
+#define MOS_CTL_RX_PROMISC 0x01
+#define MOS_CTL_ALLMULTI 0x02
+#define MOS_CTL_SLEEP 0x04
+#define MOS_CTL_TX_ENB 0x08
+/*
+ * The documentation calls this bit 'reserved', but in the FreeBSD driver
+ * provided by the vendor, this enables the receiver.
+ */
+#define MOS_CTL_RX_ENB 0x10
+#define MOS_CTL_FDX_ENB 0x20
+/* 0 = 10 Mbps, 1 = 100 Mbps */
+#define MOS_CTL_SPEEDSEL 0x40
+/* 0 = PHY controls speed/duplex mode, 1 = bridge controls speed/duplex mode */
+#define MOS_CTL_BS_ENB 0x80
+
+#define MOS_RXSTS_SHORT_FRAME 0x01
+#define MOS_RXSTS_LENGTH_ERROR 0x02
+#define MOS_RXSTS_ALIGN_ERROR 0x04
+#define MOS_RXSTS_CRC_ERROR 0x08
+#define MOS_RXSTS_LARGE_FRAME 0x10
+#define MOS_RXSTS_VALID 0x20
+/*
+ * The EtherType field of an Ethernet frame can contain values other than
+ * the frame length, hence length errors are ignored.
+ */
+#define MOS_RXSTS_MASK 0x3d
+
+#define MOS_PAUSE_TRHD_DEFAULT 0
+#define MOS_PAUSE_REWRITES 3
+
+#define MOS_TIMEOUT 1000
+
+#define MOS_RX_LIST_CNT 1
+#define MOS_TX_LIST_CNT 1
+
+/* Maximum size of a fast ethernet frame plus one byte for the status */
+#define MOS_BUFSZ (ETHER_MAX_LEN+1)
+
+/*
+ * USB endpoints.
+ */
+#define MOS_ENDPT_RX 0
+#define MOS_ENDPT_TX 1
+#define MOS_ENDPT_INTR 2
+#define MOS_ENDPT_MAX 3
+
+/*
+ * USB vendor requests.
+ */
+#define MOS_UR_READREG 0x0e
+#define MOS_UR_WRITEREG 0x0d
+
+#define MOS_CONFIG_IDX 0
+#define MOS_IFACE_IDX 0
+
+#define MCS7730 0x0001
+#define MCS7830 0x0002
+#define MCS7832 0x0004
+
+#define MOS_INC(x, y) (x) = (x + 1) % y
+
+struct mos_softc {
+ struct usb_ether sc_ue;
+ struct ifnet ifp;
+
+ struct mtx sc_mtx;
+ struct usb_xfer *sc_xfer[MOS_ENDPT_MAX];
+
+ uint16_t mos_flags;
+
+ int mos_link;
+ unsigned char mos_ipgs[2];
+ unsigned char mos_phyaddrs[2];
+};
+
+#define GET_MII(sc) uether_getmii(&(sc)->sc_ue)
+#define MOS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define MOS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define MOS_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/freebsd/sys/dev/usb/net/if_rue.c b/freebsd/sys/dev/usb/net/if_rue.c
new file mode 100644
index 00000000..d64e1e0a
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_rue.c
@@ -0,0 +1,924 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * 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 <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR rue_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_ruereg.h>
+
+#ifdef USB_DEBUG
+static int rue_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue");
+SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &rue_debug, 0, "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+
+static const STRUCT_USB_HOST_ID rue_devs[] = {
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX, 0)},
+ {USB_VPI(USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100, 0)},
+ {USB_VPI(USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01, 0)},
+};
+
+/* prototypes */
+
+static device_probe_t rue_probe;
+static device_attach_t rue_attach;
+static device_detach_t rue_detach;
+
+static miibus_readreg_t rue_miibus_readreg;
+static miibus_writereg_t rue_miibus_writereg;
+static miibus_statchg_t rue_miibus_statchg;
+
+static usb_callback_t rue_intr_callback;
+static usb_callback_t rue_bulk_read_callback;
+static usb_callback_t rue_bulk_write_callback;
+
+static uether_fn_t rue_attach_post;
+static uether_fn_t rue_init;
+static uether_fn_t rue_stop;
+static uether_fn_t rue_start;
+static uether_fn_t rue_tick;
+static uether_fn_t rue_setmulti;
+static uether_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 usb_config rue_config[RUE_N_TRANSFER] = {
+
+ [RUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MCLBYTES,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = rue_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+
+ [RUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = (MCLBYTES + 4),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = rue_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ },
+
+ [RUE_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = 0, /* use wMaxPacketSize */
+ .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),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, rue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, rue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, rue_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t rue_driver = {
+ .name = "rue",
+ .methods = rue_methods,
+ .size = sizeof(struct rue_softc),
+};
+
+static devclass_t rue_devclass;
+
+DRIVER_MODULE_ORDERED(rue, uhub, rue_driver, rue_devclass, NULL, NULL,
+ SI_ORDER_ANY);
+DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, NULL, NULL);
+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);
+MODULE_VERSION(rue, 1);
+USB_PNP_HOST_INFO(rue_devs);
+
+static const struct usb_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 usb_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 (uether_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 usb_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 (uether_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 usb_ether *ue)
+{
+ struct rue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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 usb_ether *ue)
+{
+ struct rue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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_maddr_rlock(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_maddr_runlock(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 (uether_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");
+
+ uether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+rue_attach_post(struct usb_ether *ue)
+{
+ struct rue_softc *sc = uether_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 usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_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 (usbd_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 usb_attach_arg *uaa = device_get_ivars(dev);
+ struct rue_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = RUE_IFACE_IDX;
+ error = usbd_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 = uether_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 usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, RUE_N_TRANSFER);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+rue_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rue_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct rue_intrpkt pkt;
+ struct usb_page_cache *pc;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
+ actlen >= (int)sizeof(pkt)) {
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, &pkt, sizeof(pkt));
+
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, pkt.rue_rxlost_cnt);
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, pkt.rue_crcerr_cnt);
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS, pkt.rue_col_cnt);
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ return;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rue_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = uether_getifp(ue);
+ struct usb_page_cache *pc;
+ uint16_t status;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (actlen < 4) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, actlen - 4, &status, sizeof(status));
+ actlen -= 4;
+
+ /* check receive packet was valid or not */
+ status = le16toh(status);
+ if ((status & RUE_RXSTAT_VALID) == 0) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ uether_rxbuf(ue, pc, 0, actlen);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rue_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ int temp_len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /* 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;
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_m_copy_in(pc, 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) {
+ usbd_frame_zero(pc, temp_len,
+ RUE_MIN_FRAMELEN - temp_len);
+ temp_len = RUE_MIN_FRAMELEN;
+ }
+ usbd_xfer_set_frame_len(xfer, 0, temp_len);
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usbd_transfer_submit(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_tick(struct usb_ether *ue)
+{
+ struct rue_softc *sc = uether_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 usb_ether *ue)
+{
+ struct rue_softc *sc = uether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[RUE_INTR_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[RUE_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[RUE_BULK_DT_WR]);
+}
+
+static void
+rue_init(struct usb_ether *ue)
+{
+ struct rue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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));
+
+ usbd_xfer_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);
+ struct mii_softc *miisc;
+ int error;
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~RUE_FLAG_LINK;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ error = mii_mediachg(mii);
+ return (error);
+}
+
+/*
+ * 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);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ RUE_UNLOCK(sc);
+}
+
+static void
+rue_stop(struct usb_ether *ue)
+{
+ struct rue_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_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:
+ */
+ usbd_transfer_stop(sc->sc_xfer[RUE_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[RUE_BULK_DT_RD]);
+ usbd_transfer_stop(sc->sc_xfer[RUE_INTR_DT_RD]);
+
+ rue_csr_write_1(sc, RUE_CR, 0x00);
+
+ rue_reset(sc);
+}
diff --git a/freebsd/sys/dev/usb/net/if_ruereg.h b/freebsd/sys/dev/usb/net/if_ruereg.h
new file mode 100644
index 00000000..c90a9692
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_ruereg.h
@@ -0,0 +1,178 @@
+/*-
+ * 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) uether_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;
+
+enum {
+ RUE_BULK_DT_WR,
+ RUE_BULK_DT_RD,
+ RUE_INTR_DT_RD,
+ RUE_N_TRANSFER,
+};
+
+struct rue_softc {
+ struct usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb_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/freebsd/sys/dev/usb/net/if_smsc.c b/freebsd/sys/dev/usb/net/if_smsc.c
new file mode 100644
index 00000000..5176fda9
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_smsc.c
@@ -0,0 +1,1931 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2012
+ * Ben Gray <bgray@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 ``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 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$");
+
+/*
+ * SMSC LAN9xxx devices (http://www.smsc.com/)
+ *
+ * The LAN9500 & LAN9500A devices are stand-alone USB to Ethernet chips that
+ * support USB 2.0 and 10/100 Mbps Ethernet.
+ *
+ * The LAN951x devices are an integrated USB hub and USB to Ethernet adapter.
+ * The driver only covers the Ethernet part, the standard USB hub driver
+ * supports the hub part.
+ *
+ * This driver is closely modelled on the Linux driver written and copyrighted
+ * by SMSC.
+ *
+ *
+ *
+ *
+ * H/W TCP & UDP Checksum Offloading
+ * ---------------------------------
+ * The chip supports both tx and rx offloading of UDP & TCP checksums, this
+ * feature can be dynamically enabled/disabled.
+ *
+ * RX checksuming is performed across bytes after the IPv4 header to the end of
+ * the Ethernet frame, this means if the frame is padded with non-zero values
+ * the H/W checksum will be incorrect, however the rx code compensates for this.
+ *
+ * TX checksuming is more complicated, the device requires a special header to
+ * be prefixed onto the start of the frame which indicates the start and end
+ * positions of the UDP or TCP frame. This requires the driver to manually
+ * go through the packet data and decode the headers prior to sending.
+ * On Linux they generally provide cues to the location of the csum and the
+ * area to calculate it over, on FreeBSD we seem to have to do it all ourselves,
+ * hence this is not as optimal and therefore h/w tX checksum is currently not
+ * implemented.
+ *
+ */
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/random.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <rtems/bsd/local/opt_platform.h>
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR smsc_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+
+#include <dev/usb/net/if_smscreg.h>
+
+#ifdef USB_DEBUG
+static int smsc_debug = 0;
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, smsc, CTLFLAG_RW, 0, "USB smsc");
+SYSCTL_INT(_hw_usb_smsc, OID_AUTO, debug, CTLFLAG_RWTUN, &smsc_debug, 0,
+ "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb_device_id smsc_devs[] = {
+#define SMSC_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, i) }
+ SMSC_DEV(LAN89530_ETH, 0),
+ SMSC_DEV(LAN9500_ETH, 0),
+ SMSC_DEV(LAN9500_ETH_2, 0),
+ SMSC_DEV(LAN9500A_ETH, 0),
+ SMSC_DEV(LAN9500A_ETH_2, 0),
+ SMSC_DEV(LAN9505_ETH, 0),
+ SMSC_DEV(LAN9505A_ETH, 0),
+ SMSC_DEV(LAN9514_ETH, 0),
+ SMSC_DEV(LAN9514_ETH_2, 0),
+ SMSC_DEV(LAN9530_ETH, 0),
+ SMSC_DEV(LAN9730_ETH, 0),
+ SMSC_DEV(LAN9500_SAL10, 0),
+ SMSC_DEV(LAN9505_SAL10, 0),
+ SMSC_DEV(LAN9500A_SAL10, 0),
+ SMSC_DEV(LAN9505A_SAL10, 0),
+ SMSC_DEV(LAN9514_SAL10, 0),
+ SMSC_DEV(LAN9500A_HAL, 0),
+ SMSC_DEV(LAN9505A_HAL, 0),
+#undef SMSC_DEV
+};
+
+
+#ifdef USB_DEBUG
+#define smsc_dbg_printf(sc, fmt, args...) \
+ do { \
+ if (smsc_debug > 0) \
+ device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, ##args); \
+ } while(0)
+#else
+#define smsc_dbg_printf(sc, fmt, args...) do { } while (0)
+#endif
+
+#define smsc_warn_printf(sc, fmt, args...) \
+ device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args)
+
+#define smsc_err_printf(sc, fmt, args...) \
+ device_printf((sc)->sc_ue.ue_dev, "error: " fmt, ##args)
+
+
+#define ETHER_IS_ZERO(addr) \
+ (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]))
+
+#define ETHER_IS_VALID(addr) \
+ (!ETHER_IS_MULTICAST(addr) && !ETHER_IS_ZERO(addr))
+
+static device_probe_t smsc_probe;
+static device_attach_t smsc_attach;
+static device_detach_t smsc_detach;
+
+static usb_callback_t smsc_bulk_read_callback;
+static usb_callback_t smsc_bulk_write_callback;
+
+static miibus_readreg_t smsc_miibus_readreg;
+static miibus_writereg_t smsc_miibus_writereg;
+static miibus_statchg_t smsc_miibus_statchg;
+
+#if __FreeBSD_version > 1000000
+static int smsc_attach_post_sub(struct usb_ether *ue);
+#endif
+static uether_fn_t smsc_attach_post;
+static uether_fn_t smsc_init;
+static uether_fn_t smsc_stop;
+static uether_fn_t smsc_start;
+static uether_fn_t smsc_tick;
+static uether_fn_t smsc_setmulti;
+static uether_fn_t smsc_setpromisc;
+
+static int smsc_ifmedia_upd(struct ifnet *);
+static void smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static int smsc_chip_init(struct smsc_softc *sc);
+static int smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
+
+static const struct usb_config smsc_config[SMSC_N_TRANSFER] = {
+
+ [SMSC_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .frames = 16,
+ .bufsize = 16 * (MCLBYTES + 16),
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = smsc_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+
+ [SMSC_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = 20480, /* bytes */
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = smsc_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ },
+
+ /* The SMSC chip supports an interrupt endpoints, however they aren't
+ * needed as we poll on the MII status.
+ */
+};
+
+static const struct usb_ether_methods smsc_ue_methods = {
+ .ue_attach_post = smsc_attach_post,
+#if __FreeBSD_version > 1000000
+ .ue_attach_post_sub = smsc_attach_post_sub,
+#endif
+ .ue_start = smsc_start,
+ .ue_ioctl = smsc_ioctl,
+ .ue_init = smsc_init,
+ .ue_stop = smsc_stop,
+ .ue_tick = smsc_tick,
+ .ue_setmulti = smsc_setmulti,
+ .ue_setpromisc = smsc_setpromisc,
+ .ue_mii_upd = smsc_ifmedia_upd,
+ .ue_mii_sts = smsc_ifmedia_sts,
+};
+
+/**
+ * smsc_read_reg - Reads a 32-bit register on the device
+ * @sc: driver soft context
+ * @off: offset of the register
+ * @data: pointer a value that will be populated with the register value
+ *
+ * LOCKING:
+ * The device lock must be held before calling this function.
+ *
+ * RETURNS:
+ * 0 on success, a USB_ERR_?? error code on failure.
+ */
+static int
+smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data)
+{
+ struct usb_device_request req;
+ uint32_t buf;
+ usb_error_t err;
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = SMSC_UR_READ_REG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, off);
+ USETW(req.wLength, 4);
+
+ err = uether_do_request(&sc->sc_ue, &req, &buf, 1000);
+ if (err != 0)
+ smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off);
+
+ *data = le32toh(buf);
+
+ return (err);
+}
+
+/**
+ * smsc_write_reg - Writes a 32-bit register on the device
+ * @sc: driver soft context
+ * @off: offset of the register
+ * @data: the 32-bit value to write into the register
+ *
+ * LOCKING:
+ * The device lock must be held before calling this function.
+ *
+ * RETURNS:
+ * 0 on success, a USB_ERR_?? error code on failure.
+ */
+static int
+smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data)
+{
+ struct usb_device_request req;
+ uint32_t buf;
+ usb_error_t err;
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ buf = htole32(data);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = SMSC_UR_WRITE_REG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, off);
+ USETW(req.wLength, 4);
+
+ err = uether_do_request(&sc->sc_ue, &req, &buf, 1000);
+ if (err != 0)
+ smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off);
+
+ return (err);
+}
+
+/**
+ * smsc_wait_for_bits - Polls on a register value until bits are cleared
+ * @sc: soft context
+ * @reg: offset of the register
+ * @bits: if the bits are clear the function returns
+ *
+ * LOCKING:
+ * The device lock must be held before calling this function.
+ *
+ * RETURNS:
+ * 0 on success, or a USB_ERR_?? error code on failure.
+ */
+static int
+smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits)
+{
+ usb_ticks_t start_ticks;
+ const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000);
+ uint32_t val;
+ int err;
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ start_ticks = (usb_ticks_t)ticks;
+ do {
+ if ((err = smsc_read_reg(sc, reg, &val)) != 0)
+ return (err);
+ if (!(val & bits))
+ return (0);
+
+ uether_pause(&sc->sc_ue, hz / 100);
+ } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks);
+
+ return (USB_ERR_TIMEOUT);
+}
+
+/**
+ * smsc_eeprom_read - Reads the attached EEPROM
+ * @sc: soft context
+ * @off: the eeprom address offset
+ * @buf: stores the bytes
+ * @buflen: the number of bytes to read
+ *
+ * Simply reads bytes from an attached eeprom.
+ *
+ * LOCKING:
+ * The function takes and releases the device lock if it is not already held.
+ *
+ * RETURNS:
+ * 0 on success, or a USB_ERR_?? error code on failure.
+ */
+static int
+smsc_eeprom_read(struct smsc_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen)
+{
+ usb_ticks_t start_ticks;
+ const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000);
+ int err;
+ int locked;
+ uint32_t val;
+ uint16_t i;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ SMSC_LOCK(sc);
+
+ err = smsc_wait_for_bits(sc, SMSC_EEPROM_CMD, SMSC_EEPROM_CMD_BUSY);
+ if (err != 0) {
+ smsc_warn_printf(sc, "eeprom busy, failed to read data\n");
+ goto done;
+ }
+
+ /* start reading the bytes, one at a time */
+ for (i = 0; i < buflen; i++) {
+
+ val = SMSC_EEPROM_CMD_BUSY | (SMSC_EEPROM_CMD_ADDR_MASK & (off + i));
+ if ((err = smsc_write_reg(sc, SMSC_EEPROM_CMD, val)) != 0)
+ goto done;
+
+ start_ticks = (usb_ticks_t)ticks;
+ do {
+ if ((err = smsc_read_reg(sc, SMSC_EEPROM_CMD, &val)) != 0)
+ goto done;
+ if (!(val & SMSC_EEPROM_CMD_BUSY) || (val & SMSC_EEPROM_CMD_TIMEOUT))
+ break;
+
+ uether_pause(&sc->sc_ue, hz / 100);
+ } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks);
+
+ if (val & (SMSC_EEPROM_CMD_BUSY | SMSC_EEPROM_CMD_TIMEOUT)) {
+ smsc_warn_printf(sc, "eeprom command failed\n");
+ err = USB_ERR_IOERROR;
+ break;
+ }
+
+ if ((err = smsc_read_reg(sc, SMSC_EEPROM_DATA, &val)) != 0)
+ goto done;
+
+ buf[i] = (val & 0xff);
+ }
+
+done:
+ if (!locked)
+ SMSC_UNLOCK(sc);
+
+ return (err);
+}
+
+/**
+ * smsc_miibus_readreg - Reads a MII/MDIO register
+ * @dev: usb ether device
+ * @phy: the number of phy reading from
+ * @reg: the register address
+ *
+ * Attempts to read a phy register over the MII bus.
+ *
+ * LOCKING:
+ * Takes and releases the device mutex lock if not already held.
+ *
+ * RETURNS:
+ * Returns the 16-bits read from the MII register, if this function fails 0
+ * is returned.
+ */
+static int
+smsc_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct smsc_softc *sc = device_get_softc(dev);
+ int locked;
+ uint32_t addr;
+ uint32_t val = 0;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ SMSC_LOCK(sc);
+
+ if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
+ smsc_warn_printf(sc, "MII is busy\n");
+ goto done;
+ }
+
+ addr = (phy << 11) | (reg << 6) | SMSC_MII_READ;
+ smsc_write_reg(sc, SMSC_MII_ADDR, addr);
+
+ if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
+ smsc_warn_printf(sc, "MII read timeout\n");
+
+ smsc_read_reg(sc, SMSC_MII_DATA, &val);
+ val = le32toh(val);
+
+done:
+ if (!locked)
+ SMSC_UNLOCK(sc);
+
+ return (val & 0xFFFF);
+}
+
+/**
+ * smsc_miibus_writereg - Writes a MII/MDIO register
+ * @dev: usb ether device
+ * @phy: the number of phy writing to
+ * @reg: the register address
+ * @val: the value to write
+ *
+ * Attempts to write a phy register over the MII bus.
+ *
+ * LOCKING:
+ * Takes and releases the device mutex lock if not already held.
+ *
+ * RETURNS:
+ * Always returns 0 regardless of success or failure.
+ */
+static int
+smsc_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct smsc_softc *sc = device_get_softc(dev);
+ int locked;
+ uint32_t addr;
+
+ if (sc->sc_phyno != phy)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ SMSC_LOCK(sc);
+
+ if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) {
+ smsc_warn_printf(sc, "MII is busy\n");
+ goto done;
+ }
+
+ val = htole32(val);
+ smsc_write_reg(sc, SMSC_MII_DATA, val);
+
+ addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE;
+ smsc_write_reg(sc, SMSC_MII_ADDR, addr);
+
+ if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0)
+ smsc_warn_printf(sc, "MII write timeout\n");
+
+done:
+ if (!locked)
+ SMSC_UNLOCK(sc);
+ return (0);
+}
+
+
+
+/**
+ * smsc_miibus_statchg - Called to detect phy status change
+ * @dev: usb ether device
+ *
+ * This function is called periodically by the system to poll for status
+ * changes of the link.
+ *
+ * LOCKING:
+ * Takes and releases the device mutex lock if not already held.
+ */
+static void
+smsc_miibus_statchg(device_t dev)
+{
+ struct smsc_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = uether_getmii(&sc->sc_ue);
+ struct ifnet *ifp;
+ int locked;
+ int err;
+ uint32_t flow;
+ uint32_t afc_cfg;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ SMSC_LOCK(sc);
+
+ ifp = uether_getifp(&sc->sc_ue);
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ goto done;
+
+ /* Use the MII status to determine link status */
+ sc->sc_flags &= ~SMSC_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 |= SMSC_FLAG_LINK;
+ break;
+ case IFM_1000_T:
+ /* Gigabit ethernet not supported by chipset */
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Lost link, do nothing. */
+ if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) {
+ smsc_dbg_printf(sc, "link flag not set\n");
+ goto done;
+ }
+
+ err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg);
+ if (err) {
+ smsc_warn_printf(sc, "failed to read initial AFC_CFG, error %d\n", err);
+ goto done;
+ }
+
+ /* Enable/disable full duplex operation and TX/RX pause */
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
+ smsc_dbg_printf(sc, "full duplex operation\n");
+ sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN;
+ sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX;
+
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
+ flow = 0xffff0002;
+ else
+ flow = 0;
+
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
+ afc_cfg |= 0xf;
+ else
+ afc_cfg &= ~0xf;
+
+ } else {
+ smsc_dbg_printf(sc, "half duplex operation\n");
+ sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX;
+ sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN;
+
+ flow = 0;
+ afc_cfg |= 0xf;
+ }
+
+ err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
+ err += smsc_write_reg(sc, SMSC_FLOW, flow);
+ err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg);
+ if (err)
+ smsc_warn_printf(sc, "media change failed, error %d\n", err);
+
+done:
+ if (!locked)
+ SMSC_UNLOCK(sc);
+}
+
+/**
+ * smsc_ifmedia_upd - Set media options
+ * @ifp: interface pointer
+ *
+ * Basically boilerplate code that simply calls the mii functions to set the
+ * media options.
+ *
+ * LOCKING:
+ * The device lock must be held before this function is called.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code.
+ */
+static int
+smsc_ifmedia_upd(struct ifnet *ifp)
+{
+ struct smsc_softc *sc = ifp->if_softc;
+ struct mii_data *mii = uether_getmii(&sc->sc_ue);
+ struct mii_softc *miisc;
+ int err;
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ err = mii_mediachg(mii);
+ return (err);
+}
+
+/**
+ * smsc_ifmedia_sts - Report current media status
+ * @ifp: inet interface pointer
+ * @ifmr: interface media request
+ *
+ * Basically boilerplate code that simply calls the mii functions to get the
+ * media status.
+ *
+ * LOCKING:
+ * Internally takes and releases the device lock.
+ */
+static void
+smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct smsc_softc *sc = ifp->if_softc;
+ struct mii_data *mii = uether_getmii(&sc->sc_ue);
+
+ SMSC_LOCK(sc);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ SMSC_UNLOCK(sc);
+}
+
+/**
+ * smsc_hash - Calculate the hash of a mac address
+ * @addr: The mac address to calculate the hash on
+ *
+ * This function is used when configuring a range of m'cast mac addresses to
+ * filter on. The hash of the mac address is put in the device's mac hash
+ * table.
+ *
+ * RETURNS:
+ * Returns a value from 0-63 value which is the hash of the mac address.
+ */
+static inline uint32_t
+smsc_hash(uint8_t addr[ETHER_ADDR_LEN])
+{
+ return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f;
+}
+
+/**
+ * smsc_setmulti - Setup multicast
+ * @ue: usb ethernet device context
+ *
+ * Tells the device to either accept frames with a multicast mac address, a
+ * select group of m'cast mac addresses or just the devices mac address.
+ *
+ * LOCKING:
+ * Should be called with the SMSC lock held.
+ */
+static void
+smsc_setmulti(struct usb_ether *ue)
+{
+ struct smsc_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ uint32_t hashtbl[2] = { 0, 0 };
+ uint32_t hash;
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+ smsc_dbg_printf(sc, "receive all multicast enabled\n");
+ sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS;
+ sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT;
+
+ } else {
+ /* Take the lock of the mac address list before hashing each of them */
+ if_maddr_rlock(ifp);
+
+ if (!TAILQ_EMPTY(&ifp->if_multiaddrs)) {
+ /* We are filtering on a set of address so calculate hashes of each
+ * of the address and set the corresponding bits in the register.
+ */
+ sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT;
+ sc->sc_mac_csr &= ~(SMSC_MAC_CSR_PRMS | SMSC_MAC_CSR_MCPAS);
+
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+
+ hash = smsc_hash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ hashtbl[hash >> 5] |= 1 << (hash & 0x1F);
+ }
+ } else {
+ /* Only receive packets with destination set to our mac address */
+ sc->sc_mac_csr &= ~(SMSC_MAC_CSR_MCPAS | SMSC_MAC_CSR_HPFILT);
+ }
+
+ if_maddr_runlock(ifp);
+
+ /* Debug */
+ if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT)
+ smsc_dbg_printf(sc, "receive select group of macs\n");
+ else
+ smsc_dbg_printf(sc, "receive own packets only\n");
+ }
+
+ /* Write the hash table and mac control registers */
+ smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]);
+ smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]);
+ smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
+}
+
+
+/**
+ * smsc_setpromisc - Enables/disables promiscuous mode
+ * @ue: usb ethernet device context
+ *
+ * LOCKING:
+ * Should be called with the SMSC lock held.
+ */
+static void
+smsc_setpromisc(struct usb_ether *ue)
+{
+ struct smsc_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ smsc_dbg_printf(sc, "promiscuous mode %sabled\n",
+ (ifp->if_flags & IFF_PROMISC) ? "en" : "dis");
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_PROMISC)
+ sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS;
+ else
+ sc->sc_mac_csr &= ~SMSC_MAC_CSR_PRMS;
+
+ smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
+}
+
+
+/**
+ * smsc_sethwcsum - Enable or disable H/W UDP and TCP checksumming
+ * @sc: driver soft context
+ *
+ * LOCKING:
+ * Should be called with the SMSC lock held.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code.
+ */
+static int smsc_sethwcsum(struct smsc_softc *sc)
+{
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ uint32_t val;
+ int err;
+
+ if (!ifp)
+ return (-EIO);
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ err = smsc_read_reg(sc, SMSC_COE_CTRL, &val);
+ if (err != 0) {
+ smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n", err);
+ return (err);
+ }
+
+ /* Enable/disable the Rx checksum */
+ if ((ifp->if_capabilities & ifp->if_capenable) & IFCAP_RXCSUM)
+ val |= SMSC_COE_CTRL_RX_EN;
+ else
+ val &= ~SMSC_COE_CTRL_RX_EN;
+
+ /* Enable/disable the Tx checksum (currently not supported) */
+ if ((ifp->if_capabilities & ifp->if_capenable) & IFCAP_TXCSUM)
+ val |= SMSC_COE_CTRL_TX_EN;
+ else
+ val &= ~SMSC_COE_CTRL_TX_EN;
+
+ err = smsc_write_reg(sc, SMSC_COE_CTRL, val);
+ if (err != 0) {
+ smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n", err);
+ return (err);
+ }
+
+ return (0);
+}
+
+/**
+ * smsc_setmacaddress - Sets the mac address in the device
+ * @sc: driver soft context
+ * @addr: pointer to array contain at least 6 bytes of the mac
+ *
+ * Writes the MAC address into the device, usually the MAC is programmed with
+ * values from the EEPROM.
+ *
+ * LOCKING:
+ * Should be called with the SMSC lock held.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code.
+ */
+static int
+smsc_setmacaddress(struct smsc_softc *sc, const uint8_t *addr)
+{
+ int err;
+ uint32_t val;
+
+ smsc_dbg_printf(sc, "setting mac address to %02x:%02x:%02x:%02x:%02x:%02x\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
+ if ((err = smsc_write_reg(sc, SMSC_MAC_ADDRL, val)) != 0)
+ goto done;
+
+ val = (addr[5] << 8) | addr[4];
+ err = smsc_write_reg(sc, SMSC_MAC_ADDRH, val);
+
+done:
+ return (err);
+}
+
+/**
+ * smsc_reset - Reset the SMSC chip
+ * @sc: device soft context
+ *
+ * LOCKING:
+ * Should be called with the SMSC lock held.
+ */
+static void
+smsc_reset(struct smsc_softc *sc)
+{
+ struct usb_config_descriptor *cd;
+ usb_error_t err;
+
+ cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev);
+
+ err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
+ cd->bConfigurationValue);
+ if (err)
+ smsc_warn_printf(sc, "reset failed (ignored)\n");
+
+ /* Wait a little while for the chip to get its brains in order. */
+ uether_pause(&sc->sc_ue, hz / 100);
+
+ /* Reinitialize controller to achieve full reset. */
+ smsc_chip_init(sc);
+}
+
+
+/**
+ * smsc_init - Initialises the LAN95xx chip
+ * @ue: USB ether interface
+ *
+ * Called when the interface is brought up (i.e. ifconfig ue0 up), this
+ * initialise the interface and the rx/tx pipes.
+ *
+ * LOCKING:
+ * Should be called with the SMSC lock held.
+ */
+static void
+smsc_init(struct usb_ether *ue)
+{
+ struct smsc_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (smsc_setmacaddress(sc, IF_LLADDR(ifp)))
+ smsc_dbg_printf(sc, "setting MAC address failed\n");
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ return;
+
+ /* Cancel pending I/O */
+ smsc_stop(ue);
+
+#if __FreeBSD_version <= 1000000
+ /* On earlier versions this was the first place we could tell the system
+ * that we supported h/w csuming, however this is only called after the
+ * the interface has been brought up - not ideal.
+ */
+ if (!(ifp->if_capabilities & IFCAP_RXCSUM)) {
+ ifp->if_capabilities |= IFCAP_RXCSUM;
+ ifp->if_capenable |= IFCAP_RXCSUM;
+ ifp->if_hwassist = 0;
+ }
+
+ /* TX checksuming is disabled for now
+ ifp->if_capabilities |= IFCAP_TXCSUM;
+ ifp->if_capenable |= IFCAP_TXCSUM;
+ ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
+ */
+#endif
+
+ /* Reset the ethernet interface. */
+ smsc_reset(sc);
+
+ /* Load the multicast filter. */
+ smsc_setmulti(ue);
+
+ /* TCP/UDP checksum offload engines. */
+ smsc_sethwcsum(sc);
+
+ usbd_xfer_set_stall(sc->sc_xfer[SMSC_BULK_DT_WR]);
+
+ /* Indicate we are up and running. */
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ /* Switch to selected media. */
+ smsc_ifmedia_upd(ifp);
+ smsc_start(ue);
+}
+
+/**
+ * smsc_bulk_read_callback - Read callback used to process the USB URB
+ * @xfer: the USB transfer
+ * @error:
+ *
+ * Reads the URB data which can contain one or more ethernet frames, the
+ * frames are copyed into a mbuf and given to the system.
+ *
+ * LOCKING:
+ * No locking required, doesn't access internal driver settings.
+ */
+static void
+smsc_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct smsc_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = uether_getifp(ue);
+ struct mbuf *m;
+ struct usb_page_cache *pc;
+ uint32_t rxhdr;
+ uint16_t pktlen;
+ int off;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+ smsc_dbg_printf(sc, "rx : actlen %d\n", actlen);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ /* There is always a zero length frame after bringing the IF up */
+ if (actlen < (sizeof(rxhdr) + ETHER_CRC_LEN))
+ goto tr_setup;
+
+ /* There maybe multiple packets in the USB frame, each will have a
+ * header and each needs to have it's own mbuf allocated and populated
+ * for it.
+ */
+ pc = usbd_xfer_get_frame(xfer, 0);
+ off = 0;
+
+ while (off < actlen) {
+
+ /* The frame header is always aligned on a 4 byte boundary */
+ off = ((off + 0x3) & ~0x3);
+
+ usbd_copy_out(pc, off, &rxhdr, sizeof(rxhdr));
+ off += (sizeof(rxhdr) + ETHER_ALIGN);
+ rxhdr = le32toh(rxhdr);
+
+ pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr);
+
+ smsc_dbg_printf(sc, "rx : rxhdr 0x%08x : pktlen %d : actlen %d : "
+ "off %d\n", rxhdr, pktlen, actlen, off);
+
+
+ if (rxhdr & SMSC_RX_STAT_ERROR) {
+ smsc_dbg_printf(sc, "rx error (hdr 0x%08x)\n", rxhdr);
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ if (rxhdr & SMSC_RX_STAT_COLLISION)
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
+ } else {
+
+ /* Check if the ethernet frame is too big or too small */
+ if ((pktlen < ETHER_HDR_LEN) || (pktlen > (actlen - off)))
+ goto tr_setup;
+
+ /* Create a new mbuf to store the packet in */
+ m = uether_newbuf();
+ if (m == NULL) {
+ smsc_warn_printf(sc, "failed to create new mbuf\n");
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ goto tr_setup;
+ }
+
+ usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen);
+
+ /* Check if RX TCP/UDP checksumming is being offloaded */
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+
+ struct ether_header *eh;
+
+ eh = mtod(m, struct ether_header *);
+
+ /* Remove the extra 2 bytes of the csum */
+ pktlen -= 2;
+
+ /* The checksum appears to be simplistically calculated
+ * over the udp/tcp header and data up to the end of the
+ * eth frame. Which means if the eth frame is padded
+ * the csum calculation is incorrectly performed over
+ * the padding bytes as well. Therefore to be safe we
+ * ignore the H/W csum on frames less than or equal to
+ * 64 bytes.
+ *
+ * Ignore H/W csum for non-IPv4 packets.
+ */
+ if ((be16toh(eh->ether_type) == ETHERTYPE_IP) &&
+ (pktlen > ETHER_MIN_LEN)) {
+ struct ip *ip;
+
+ ip = (struct ip *)(eh + 1);
+ if ((ip->ip_v == IPVERSION) &&
+ ((ip->ip_p == IPPROTO_TCP) ||
+ (ip->ip_p == IPPROTO_UDP))) {
+ /* Indicate the UDP/TCP csum has been calculated */
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
+
+ /* Copy the TCP/UDP checksum from the last 2 bytes
+ * of the transfer and put in the csum_data field.
+ */
+ usbd_copy_out(pc, (off + pktlen),
+ &m->m_pkthdr.csum_data, 2);
+
+ /* The data is copied in network order, but the
+ * csum algorithm in the kernel expects it to be
+ * in host network order.
+ */
+ m->m_pkthdr.csum_data = ntohs(m->m_pkthdr.csum_data);
+
+ smsc_dbg_printf(sc, "RX checksum offloaded (0x%04x)\n",
+ m->m_pkthdr.csum_data);
+ }
+ }
+
+ /* Need to adjust the offset as well or we'll be off
+ * by 2 because the csum is removed from the packet
+ * length.
+ */
+ off += 2;
+ }
+
+ /* Finally enqueue the mbuf on the receive queue */
+ /* Remove 4 trailing bytes */
+ if (pktlen < (4 + ETHER_HDR_LEN)) {
+ m_freem(m);
+ goto tr_setup;
+ }
+ uether_rxmbuf(ue, m, pktlen - 4);
+ }
+
+ /* Update the offset to move to the next potential packet */
+ off += pktlen;
+ }
+
+ /* FALLTHROUGH */
+
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+
+ default:
+ if (error != USB_ERR_CANCELLED) {
+ smsc_warn_printf(sc, "bulk read error, %s\n", usbd_errstr(error));
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+/**
+ * smsc_bulk_write_callback - Write callback used to send ethernet frame(s)
+ * @xfer: the USB transfer
+ * @error: error code if the transfers is in an errored state
+ *
+ * The main write function that pulls ethernet frames off the queue and sends
+ * them out.
+ *
+ * LOCKING:
+ *
+ */
+static void
+smsc_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct smsc_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ uint32_t txhdr;
+ uint32_t frm_len = 0;
+ int nframes;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ /* FALLTHROUGH */
+
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & SMSC_FLAG_LINK) == 0 ||
+ (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) {
+ /* Don't send anything if there is no link or controller is busy. */
+ return;
+ }
+
+ for (nframes = 0; nframes < 16 &&
+ !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES,
+ nframes);
+ frm_len = 0;
+ pc = usbd_xfer_get_frame(xfer, nframes);
+
+ /* Each frame is prefixed with two 32-bit values describing the
+ * length of the packet and buffer.
+ */
+ txhdr = SMSC_TX_CTRL_0_BUF_SIZE(m->m_pkthdr.len) |
+ SMSC_TX_CTRL_0_FIRST_SEG | SMSC_TX_CTRL_0_LAST_SEG;
+ txhdr = htole32(txhdr);
+ usbd_copy_in(pc, 0, &txhdr, sizeof(txhdr));
+
+ txhdr = SMSC_TX_CTRL_1_PKT_LENGTH(m->m_pkthdr.len);
+ txhdr = htole32(txhdr);
+ usbd_copy_in(pc, 4, &txhdr, sizeof(txhdr));
+
+ frm_len += 8;
+
+ /* Next copy in the actual packet */
+ usbd_m_copy_in(pc, frm_len, m, 0, m->m_pkthdr.len);
+ frm_len += m->m_pkthdr.len;
+
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /* If there's a BPF listener, bounce a copy of this frame to him */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ /* Set frame length. */
+ usbd_xfer_set_frame_len(xfer, nframes, frm_len);
+ }
+ if (nframes != 0) {
+ usbd_xfer_set_frames(xfer, nframes);
+ usbd_transfer_submit(xfer);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ }
+ return;
+
+ default:
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ if (error != USB_ERR_CANCELLED) {
+ smsc_err_printf(sc, "usb error on tx: %s\n", usbd_errstr(error));
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+/**
+ * smsc_tick - Called periodically to monitor the state of the LAN95xx chip
+ * @ue: USB ether interface
+ *
+ * Simply calls the mii status functions to check the state of the link.
+ *
+ * LOCKING:
+ * Should be called with the SMSC lock held.
+ */
+static void
+smsc_tick(struct usb_ether *ue)
+{
+ struct smsc_softc *sc = uether_getsc(ue);
+ struct mii_data *mii = uether_getmii(&sc->sc_ue);
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) {
+ smsc_miibus_statchg(ue->ue_dev);
+ if ((sc->sc_flags & SMSC_FLAG_LINK) != 0)
+ smsc_start(ue);
+ }
+}
+
+/**
+ * smsc_start - Starts communication with the LAN95xx chip
+ * @ue: USB ether interface
+ *
+ *
+ *
+ */
+static void
+smsc_start(struct usb_ether *ue)
+{
+ struct smsc_softc *sc = uether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_WR]);
+}
+
+/**
+ * smsc_stop - Stops communication with the LAN95xx chip
+ * @ue: USB ether interface
+ *
+ *
+ *
+ */
+static void
+smsc_stop(struct usb_ether *ue)
+{
+ struct smsc_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ sc->sc_flags &= ~SMSC_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_RD]);
+}
+
+/**
+ * smsc_phy_init - Initialises the in-built SMSC phy
+ * @sc: driver soft context
+ *
+ * Resets the PHY part of the chip and then initialises it to default
+ * values. The 'link down' and 'auto-negotiation complete' interrupts
+ * from the PHY are also enabled, however we don't monitor the interrupt
+ * endpoints for the moment.
+ *
+ * RETURNS:
+ * Returns 0 on success or EIO if failed to reset the PHY.
+ */
+static int
+smsc_phy_init(struct smsc_softc *sc)
+{
+ int bmcr;
+ usb_ticks_t start_ticks;
+ const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000);
+
+ SMSC_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* Reset phy and wait for reset to complete */
+ smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, BMCR_RESET);
+
+ start_ticks = ticks;
+ do {
+ uether_pause(&sc->sc_ue, hz / 100);
+ bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR);
+ } while ((bmcr & MII_BMCR) && ((ticks - start_ticks) < max_ticks));
+
+ if (((usb_ticks_t)(ticks - start_ticks)) >= max_ticks) {
+ smsc_err_printf(sc, "PHY reset timed-out");
+ return (EIO);
+ }
+
+ smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_ANAR,
+ ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD | /* all modes */
+ ANAR_CSMA |
+ ANAR_FC |
+ ANAR_PAUSE_ASYM);
+
+ /* Setup the phy to interrupt when the link goes down or autoneg completes */
+ smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, SMSC_PHY_INTR_STAT);
+ smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, SMSC_PHY_INTR_MASK,
+ (SMSC_PHY_INTR_ANEG_COMP | SMSC_PHY_INTR_LINK_DOWN));
+
+ /* Restart auto-negotation */
+ bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR);
+ bmcr |= BMCR_STARTNEG;
+ smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, bmcr);
+
+ return (0);
+}
+
+
+/**
+ * smsc_chip_init - Initialises the chip after power on
+ * @sc: driver soft context
+ *
+ * This initialisation sequence is modelled on the procedure in the Linux
+ * driver.
+ *
+ * RETURNS:
+ * Returns 0 on success or an error code on failure.
+ */
+static int
+smsc_chip_init(struct smsc_softc *sc)
+{
+ int err;
+ int locked;
+ uint32_t reg_val;
+ int burst_cap;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ SMSC_LOCK(sc);
+
+ /* Enter H/W config mode */
+ smsc_write_reg(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST);
+
+ if ((err = smsc_wait_for_bits(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST)) != 0) {
+ smsc_warn_printf(sc, "timed-out waiting for reset to complete\n");
+ goto init_failed;
+ }
+
+ /* Reset the PHY */
+ smsc_write_reg(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST);
+
+ if ((err = smsc_wait_for_bits(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST)) != 0) {
+ smsc_warn_printf(sc, "timed-out waiting for phy reset to complete\n");
+ goto init_failed;
+ }
+
+ /* Set the mac address */
+ if ((err = smsc_setmacaddress(sc, sc->sc_ue.ue_eaddr)) != 0) {
+ smsc_warn_printf(sc, "failed to set the MAC address\n");
+ goto init_failed;
+ }
+
+ /* Don't know what the HW_CFG_BIR bit is, but following the reset sequence
+ * as used in the Linux driver.
+ */
+ if ((err = smsc_read_reg(sc, SMSC_HW_CFG, &reg_val)) != 0) {
+ smsc_warn_printf(sc, "failed to read HW_CFG: %d\n", err);
+ goto init_failed;
+ }
+ reg_val |= SMSC_HW_CFG_BIR;
+ smsc_write_reg(sc, SMSC_HW_CFG, reg_val);
+
+ /* There is a so called 'turbo mode' that the linux driver supports, it
+ * seems to allow you to jam multiple frames per Rx transaction. By default
+ * this driver supports that and therefore allows multiple frames per URB.
+ *
+ * The xfer buffer size needs to reflect this as well, therefore based on
+ * the calculations in the Linux driver the RX bufsize is set to 18944,
+ * bufsz = (16 * 1024 + 5 * 512)
+ *
+ * Burst capability is the number of URBs that can be in a burst of data/
+ * ethernet frames.
+ */
+ if (usbd_get_speed(sc->sc_ue.ue_udev) == USB_SPEED_HIGH)
+ burst_cap = 37;
+ else
+ burst_cap = 128;
+
+ smsc_write_reg(sc, SMSC_BURST_CAP, burst_cap);
+
+ /* Set the default bulk in delay (magic value from Linux driver) */
+ smsc_write_reg(sc, SMSC_BULK_IN_DLY, 0x00002000);
+
+
+
+ /*
+ * Initialise the RX interface
+ */
+ if ((err = smsc_read_reg(sc, SMSC_HW_CFG, &reg_val)) < 0) {
+ smsc_warn_printf(sc, "failed to read HW_CFG: (err = %d)\n", err);
+ goto init_failed;
+ }
+
+ /* Adjust the packet offset in the buffer (designed to try and align IP
+ * header on 4 byte boundary)
+ */
+ reg_val &= ~SMSC_HW_CFG_RXDOFF;
+ reg_val |= (ETHER_ALIGN << 9) & SMSC_HW_CFG_RXDOFF;
+
+ /* The following setings are used for 'turbo mode', a.k.a multiple frames
+ * per Rx transaction (again info taken form Linux driver).
+ */
+ reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE);
+
+ smsc_write_reg(sc, SMSC_HW_CFG, reg_val);
+
+ /* Clear the status register ? */
+ smsc_write_reg(sc, SMSC_INTR_STATUS, 0xffffffff);
+
+ /* Read and display the revision register */
+ if ((err = smsc_read_reg(sc, SMSC_ID_REV, &sc->sc_rev_id)) < 0) {
+ smsc_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err);
+ goto init_failed;
+ }
+
+ device_printf(sc->sc_ue.ue_dev, "chip 0x%04lx, rev. %04lx\n",
+ (sc->sc_rev_id & SMSC_ID_REV_CHIP_ID_MASK) >> 16,
+ (sc->sc_rev_id & SMSC_ID_REV_CHIP_REV_MASK));
+
+ /* GPIO/LED setup */
+ reg_val = SMSC_LED_GPIO_CFG_SPD_LED | SMSC_LED_GPIO_CFG_LNK_LED |
+ SMSC_LED_GPIO_CFG_FDX_LED;
+ smsc_write_reg(sc, SMSC_LED_GPIO_CFG, reg_val);
+
+ /*
+ * Initialise the TX interface
+ */
+ smsc_write_reg(sc, SMSC_FLOW, 0);
+
+ smsc_write_reg(sc, SMSC_AFC_CFG, AFC_CFG_DEFAULT);
+
+ /* Read the current MAC configuration */
+ if ((err = smsc_read_reg(sc, SMSC_MAC_CSR, &sc->sc_mac_csr)) < 0) {
+ smsc_warn_printf(sc, "failed to read MAC_CSR (err=%d)\n", err);
+ goto init_failed;
+ }
+
+ /* Vlan */
+ smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN);
+
+ /*
+ * Initialise the PHY
+ */
+ if ((err = smsc_phy_init(sc)) != 0)
+ goto init_failed;
+
+
+ /*
+ * Start TX
+ */
+ sc->sc_mac_csr |= SMSC_MAC_CSR_TXEN;
+ smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
+ smsc_write_reg(sc, SMSC_TX_CFG, SMSC_TX_CFG_ON);
+
+ /*
+ * Start RX
+ */
+ sc->sc_mac_csr |= SMSC_MAC_CSR_RXEN;
+ smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr);
+
+ if (!locked)
+ SMSC_UNLOCK(sc);
+
+ return (0);
+
+init_failed:
+ if (!locked)
+ SMSC_UNLOCK(sc);
+
+ smsc_err_printf(sc, "smsc_chip_init failed (err=%d)\n", err);
+ return (err);
+}
+
+
+/**
+ * smsc_ioctl - ioctl function for the device
+ * @ifp: interface pointer
+ * @cmd: the ioctl command
+ * @data: data passed in the ioctl call, typically a pointer to struct ifreq.
+ *
+ * The ioctl routine is overridden to detect change requests for the H/W
+ * checksum capabilities.
+ *
+ * RETURNS:
+ * 0 on success and an error code on failure.
+ */
+static int
+smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct usb_ether *ue = ifp->if_softc;
+ struct smsc_softc *sc;
+ struct ifreq *ifr;
+ int rc;
+ int mask;
+ int reinit;
+
+ if (cmd == SIOCSIFCAP) {
+
+ sc = uether_getsc(ue);
+ ifr = (struct ifreq *)data;
+
+ SMSC_LOCK(sc);
+
+ rc = 0;
+ reinit = 0;
+
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+
+ /* Modify the RX CSUM enable bits */
+ if ((mask & IFCAP_RXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_RXCSUM;
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ reinit = 1;
+ }
+ }
+
+ SMSC_UNLOCK(sc);
+ if (reinit)
+#if __FreeBSD_version > 1000000
+ uether_init(ue);
+#else
+ ifp->if_init(ue);
+#endif
+
+ } else {
+ rc = uether_ioctl(ifp, cmd, data);
+ }
+
+ return (rc);
+}
+
+#ifdef FDT
+/*
+ * This is FreeBSD-specific compatibility strings for RPi/RPi2
+ */
+static phandle_t
+smsc_fdt_find_eth_node(phandle_t start)
+{
+ phandle_t child, node;
+
+ /* Traverse through entire tree to find usb ethernet nodes. */
+ for (node = OF_child(start); node != 0; node = OF_peer(node)) {
+ if (ofw_bus_node_is_compatible(node, "net,ethernet") &&
+ ofw_bus_node_is_compatible(node, "usb,device"))
+ return (node);
+ child = smsc_fdt_find_eth_node(node);
+ if (child != -1)
+ return (child);
+ }
+
+ return (-1);
+}
+
+/*
+ * Check if node's path is <*>/usb/hub/ethernet
+ */
+static int
+smsc_fdt_is_usb_eth(phandle_t node)
+{
+ char name[16];
+ int len;
+
+ memset(name, 0, sizeof(name));
+ len = OF_getprop(node, "name", name, sizeof(name));
+ if (len <= 0)
+ return (0);
+
+ if (strcmp(name, "ethernet"))
+ return (0);
+
+ node = OF_parent(node);
+ if (node == -1)
+ return (0);
+ len = OF_getprop(node, "name", name, sizeof(name));
+ if (len <= 0)
+ return (0);
+
+ if (strcmp(name, "hub"))
+ return (0);
+
+ node = OF_parent(node);
+ if (node == -1)
+ return (0);
+ len = OF_getprop(node, "name", name, sizeof(name));
+ if (len <= 0)
+ return (0);
+
+ if (strcmp(name, "usb"))
+ return (0);
+
+ return (1);
+}
+
+static phandle_t
+smsc_fdt_find_eth_node_by_path(phandle_t start)
+{
+ phandle_t child, node;
+
+ /* Traverse through entire tree to find usb ethernet nodes. */
+ for (node = OF_child(start); node != 0; node = OF_peer(node)) {
+ if (smsc_fdt_is_usb_eth(node))
+ return (node);
+ child = smsc_fdt_find_eth_node_by_path(node);
+ if (child != -1)
+ return (child);
+ }
+
+ return (-1);
+}
+
+/**
+ * Get MAC address from FDT blob. Firmware or loader should fill
+ * mac-address or local-mac-address property. Returns 0 if MAC address
+ * obtained, error code otherwise.
+ */
+static int
+smsc_fdt_find_mac(unsigned char *mac)
+{
+ phandle_t node, root;
+ int len;
+
+ root = OF_finddevice("/");
+ node = smsc_fdt_find_eth_node(root);
+ /*
+ * If it's not FreeBSD FDT blob for RPi, try more
+ * generic .../usb/hub/ethernet
+ */
+ if (node == -1)
+ node = smsc_fdt_find_eth_node_by_path(root);
+
+ if (node != -1) {
+ /* Check if there is property */
+ if ((len = OF_getproplen(node, "local-mac-address")) > 0) {
+ if (len != ETHER_ADDR_LEN)
+ return (EINVAL);
+
+ OF_getprop(node, "local-mac-address", mac,
+ ETHER_ADDR_LEN);
+ return (0);
+ }
+
+ if ((len = OF_getproplen(node, "mac-address")) > 0) {
+ if (len != ETHER_ADDR_LEN)
+ return (EINVAL);
+
+ OF_getprop(node, "mac-address", mac,
+ ETHER_ADDR_LEN);
+ return (0);
+ }
+ }
+
+ return (ENXIO);
+}
+#endif
+
+/**
+ * smsc_attach_post - Called after the driver attached to the USB interface
+ * @ue: the USB ethernet device
+ *
+ * This is where the chip is intialised for the first time. This is different
+ * from the smsc_init() function in that that one is designed to setup the
+ * H/W to match the UE settings and can be called after a reset.
+ *
+ *
+ */
+static void
+smsc_attach_post(struct usb_ether *ue)
+{
+ struct smsc_softc *sc = uether_getsc(ue);
+ uint32_t mac_h, mac_l;
+ int err;
+
+ smsc_dbg_printf(sc, "smsc_attach_post\n");
+
+ /* Setup some of the basics */
+ sc->sc_phyno = 1;
+
+
+ /* Attempt to get the mac address, if an EEPROM is not attached this
+ * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC
+ * address based on urandom.
+ */
+ memset(sc->sc_ue.ue_eaddr, 0xff, ETHER_ADDR_LEN);
+
+ /* Check if there is already a MAC address in the register */
+ if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) &&
+ (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) {
+ sc->sc_ue.ue_eaddr[5] = (uint8_t)((mac_h >> 8) & 0xff);
+ sc->sc_ue.ue_eaddr[4] = (uint8_t)((mac_h) & 0xff);
+ sc->sc_ue.ue_eaddr[3] = (uint8_t)((mac_l >> 24) & 0xff);
+ sc->sc_ue.ue_eaddr[2] = (uint8_t)((mac_l >> 16) & 0xff);
+ sc->sc_ue.ue_eaddr[1] = (uint8_t)((mac_l >> 8) & 0xff);
+ sc->sc_ue.ue_eaddr[0] = (uint8_t)((mac_l) & 0xff);
+ }
+
+ /* MAC address is not set so try to read from EEPROM, if that fails generate
+ * a random MAC address.
+ */
+ if (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr)) {
+
+ err = smsc_eeprom_read(sc, 0x01, sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN);
+#ifdef FDT
+ if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr)))
+ err = smsc_fdt_find_mac(sc->sc_ue.ue_eaddr);
+#endif
+ if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr))) {
+ read_random(sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN);
+ sc->sc_ue.ue_eaddr[0] &= ~0x01; /* unicast */
+ sc->sc_ue.ue_eaddr[0] |= 0x02; /* locally administered */
+ }
+ }
+
+ /* Initialise the chip for the first time */
+ smsc_chip_init(sc);
+}
+
+
+/**
+ * smsc_attach_post_sub - Called after the driver attached to the USB interface
+ * @ue: the USB ethernet device
+ *
+ * Most of this is boilerplate code and copied from the base USB ethernet
+ * driver. It has been overriden so that we can indicate to the system that
+ * the chip supports H/W checksumming.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code.
+ */
+#if __FreeBSD_version > 1000000
+static int
+smsc_attach_post_sub(struct usb_ether *ue)
+{
+ struct smsc_softc *sc;
+ struct ifnet *ifp;
+ int error;
+
+ sc = uether_getsc(ue);
+ ifp = ue->ue_ifp;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_start = uether_start;
+ ifp->if_ioctl = smsc_ioctl;
+ ifp->if_init = uether_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+ ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ /* The chip supports TCP/UDP checksum offloading on TX and RX paths, however
+ * currently only RX checksum is supported in the driver (see top of file).
+ */
+ ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_VLAN_MTU;
+ ifp->if_hwassist = 0;
+
+ /* TX checksuming is disabled (for now?)
+ ifp->if_capabilities |= IFCAP_TXCSUM;
+ ifp->if_capenable |= IFCAP_TXCSUM;
+ ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
+ */
+
+ ifp->if_capenable = ifp->if_capabilities;
+
+ mtx_lock(&Giant);
+ error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
+ uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
+ BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, 0);
+ mtx_unlock(&Giant);
+
+ return (error);
+}
+#endif /* __FreeBSD_version > 1000000 */
+
+
+/**
+ * smsc_probe - Probe the interface.
+ * @dev: smsc device handle
+ *
+ * Checks if the device is a match for this driver.
+ *
+ * RETURNS:
+ * Returns 0 on success or an error code on failure.
+ */
+static int
+smsc_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != SMSC_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != SMSC_IFACE_IDX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(smsc_devs, sizeof(smsc_devs), uaa));
+}
+
+
+/**
+ * smsc_attach - Attach the interface.
+ * @dev: smsc device handle
+ *
+ * Allocate softc structures, do ifmedia setup and ethernet/BPF attach.
+ *
+ * RETURNS:
+ * Returns 0 on success or a negative error code.
+ */
+static int
+smsc_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct smsc_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int err;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ /* Setup the endpoints for the SMSC LAN95xx device(s) */
+ iface_index = SMSC_IFACE_IDX;
+ err = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ smsc_config, SMSC_N_TRANSFER, sc, &sc->sc_mtx);
+ if (err) {
+ device_printf(dev, "error: 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 = &smsc_ue_methods;
+
+ err = uether_ifattach(ue);
+ if (err) {
+ device_printf(dev, "error: could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ smsc_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+/**
+ * smsc_detach - Detach the interface.
+ * @dev: smsc device handle
+ *
+ * RETURNS:
+ * Returns 0.
+ */
+static int
+smsc_detach(device_t dev)
+{
+ struct smsc_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, SMSC_N_TRANSFER);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static device_method_t smsc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, smsc_probe),
+ DEVMETHOD(device_attach, smsc_attach),
+ DEVMETHOD(device_detach, smsc_detach),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, smsc_miibus_readreg),
+ DEVMETHOD(miibus_writereg, smsc_miibus_writereg),
+ DEVMETHOD(miibus_statchg, smsc_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t smsc_driver = {
+ .name = "smsc",
+ .methods = smsc_methods,
+ .size = sizeof(struct smsc_softc),
+};
+
+static devclass_t smsc_devclass;
+
+DRIVER_MODULE(smsc, uhub, smsc_driver, smsc_devclass, NULL, 0);
+DRIVER_MODULE(miibus, smsc, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(smsc, uether, 1, 1, 1);
+MODULE_DEPEND(smsc, usb, 1, 1, 1);
+MODULE_DEPEND(smsc, ether, 1, 1, 1);
+MODULE_DEPEND(smsc, miibus, 1, 1, 1);
+MODULE_VERSION(smsc, 1);
+USB_PNP_HOST_INFO(smsc_devs);
diff --git a/freebsd/sys/dev/usb/net/if_smscreg.h b/freebsd/sys/dev/usb/net/if_smscreg.h
new file mode 100644
index 00000000..31a63828
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_smscreg.h
@@ -0,0 +1,277 @@
+/*-
+ * Copyright (c) 2012
+ * Ben Gray <bgray@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 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 AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _IF_SMSCREG_H_
+#define _IF_SMSCREG_H_
+
+/*
+ * Definitions for the SMSC LAN9514 and LAN9514 USB to ethernet controllers.
+ *
+ * This information was gleaned from the SMSC driver in the linux kernel, where
+ * it is Copyrighted (C) 2007-2008 SMSC.
+ *
+ */
+
+/**
+ * TRANSMIT FRAMES
+ * ---------------
+ * Tx frames are prefixed with an 8-byte header which describes the frame
+ *
+ * 4 bytes 4 bytes variable
+ * +------------+------------+--- . . . . . . . . . . . . ---+
+ * | TX_CTRL_0 | TX_CTRL_1 | Ethernet frame data |
+ * +------------+------------+--- . . . . . . . . . . . . ---+
+ *
+ * Where the headers have the following fields:
+ *
+ * TX_CTRL_0 <20:16> Data offset
+ * TX_CTRL_0 <13> First segment of frame indicator
+ * TX_CTRL_0 <12> Last segment of frame indicator
+ * TX_CTRL_0 <10:0> Buffer size (?)
+ *
+ * TX_CTRL_1 <14> Perform H/W checksuming on IP packets
+ * TX_CTRL_1 <13> Disable automatic ethernet CRC generation
+ * TX_CTRL_1 <12> Disable padding (?)
+ * TX_CTRL_1 <10:0> Packet byte length
+ *
+ */
+#define SMSC_TX_CTRL_0_OFFSET(x) (((x) & 0x1FUL) << 16)
+#define SMSC_TX_CTRL_0_FIRST_SEG (0x1UL << 13)
+#define SMSC_TX_CTRL_0_LAST_SEG (0x1UL << 12)
+#define SMSC_TX_CTRL_0_BUF_SIZE(x) ((x) & 0x000007FFUL)
+
+#define SMSC_TX_CTRL_1_CSUM_ENABLE (0x1UL << 14)
+#define SMSC_TX_CTRL_1_CRC_DISABLE (0x1UL << 13)
+#define SMSC_TX_CTRL_1_PADDING_DISABLE (0x1UL << 12)
+#define SMSC_TX_CTRL_1_PKT_LENGTH(x) ((x) & 0x000007FFUL)
+
+/**
+ * RECEIVE FRAMES
+ * --------------
+ * Rx frames are prefixed with an 4-byte status header which describes any
+ * errors with the frame as well as things like the length
+ *
+ * 4 bytes variable
+ * +------------+--- . . . . . . . . . . . . ---+
+ * | RX_STAT | Ethernet frame data |
+ * +------------+--- . . . . . . . . . . . . ---+
+ *
+ * Where the status header has the following fields:
+ *
+ * RX_STAT <30> Filter Fail
+ * RX_STAT <29:16> Frame Length
+ * RX_STAT <15> Error Summary
+ * RX_STAT <13> Broadcast Frame
+ * RX_STAT <12> Length Error
+ * RX_STAT <11> Runt Frame
+ * RX_STAT <10> Multicast Frame
+ * RX_STAT <7> Frame too long
+ * RX_STAT <6> Collision Seen
+ * RX_STAT <5> Frame Type
+ * RX_STAT <4> Receive Watchdog
+ * RX_STAT <3> Mii Error
+ * RX_STAT <2> Dribbling
+ * RX_STAT <1> CRC Error
+ *
+ */
+#define SMSC_RX_STAT_FILTER_FAIL (0x1UL << 30)
+#define SMSC_RX_STAT_FRM_LENGTH(x) (((x) >> 16) & 0x3FFFUL)
+#define SMSC_RX_STAT_ERROR (0x1UL << 15)
+#define SMSC_RX_STAT_BROADCAST (0x1UL << 13)
+#define SMSC_RX_STAT_LENGTH_ERROR (0x1UL << 12)
+#define SMSC_RX_STAT_RUNT (0x1UL << 11)
+#define SMSC_RX_STAT_MULTICAST (0x1UL << 10)
+#define SMSC_RX_STAT_FRM_TO_LONG (0x1UL << 7)
+#define SMSC_RX_STAT_COLLISION (0x1UL << 6)
+#define SMSC_RX_STAT_FRM_TYPE (0x1UL << 5)
+#define SMSC_RX_STAT_WATCHDOG (0x1UL << 4)
+#define SMSC_RX_STAT_MII_ERROR (0x1UL << 3)
+#define SMSC_RX_STAT_DRIBBLING (0x1UL << 2)
+#define SMSC_RX_STAT_CRC_ERROR (0x1UL << 1)
+
+/**
+ * REGISTERS
+ *
+ */
+#define SMSC_ID_REV 0x000
+#define SMSC_INTR_STATUS 0x008
+#define SMSC_RX_CFG 0x00C
+#define SMSC_TX_CFG 0x010
+#define SMSC_HW_CFG 0x014
+#define SMSC_PM_CTRL 0x020
+#define SMSC_LED_GPIO_CFG 0x024
+#define SMSC_GPIO_CFG 0x028
+#define SMSC_AFC_CFG 0x02C
+#define SMSC_EEPROM_CMD 0x030
+#define SMSC_EEPROM_DATA 0x034
+#define SMSC_BURST_CAP 0x038
+#define SMSC_GPIO_WAKE 0x064
+#define SMSC_INTR_CFG 0x068
+#define SMSC_BULK_IN_DLY 0x06C
+#define SMSC_MAC_CSR 0x100
+#define SMSC_MAC_ADDRH 0x104
+#define SMSC_MAC_ADDRL 0x108
+#define SMSC_HASHH 0x10C
+#define SMSC_HASHL 0x110
+#define SMSC_MII_ADDR 0x114
+#define SMSC_MII_DATA 0x118
+#define SMSC_FLOW 0x11C
+#define SMSC_VLAN1 0x120
+#define SMSC_VLAN2 0x124
+#define SMSC_WUFF 0x128
+#define SMSC_WUCSR 0x12C
+#define SMSC_COE_CTRL 0x130
+
+/* ID / Revision register */
+#define SMSC_ID_REV_CHIP_ID_MASK 0xFFFF0000UL
+#define SMSC_ID_REV_CHIP_REV_MASK 0x0000FFFFUL
+
+#define SMSC_RX_FIFO_FLUSH (0x1UL << 0)
+
+#define SMSC_TX_CFG_ON (0x1UL << 2)
+#define SMSC_TX_CFG_STOP (0x1UL << 1)
+#define SMSC_TX_CFG_FIFO_FLUSH (0x1UL << 0)
+
+#define SMSC_HW_CFG_BIR (0x1UL << 12)
+#define SMSC_HW_CFG_LEDB (0x1UL << 11)
+#define SMSC_HW_CFG_RXDOFF (0x3UL << 9) /* RX pkt alignment */
+#define SMSC_HW_CFG_DRP (0x1UL << 6)
+#define SMSC_HW_CFG_MEF (0x1UL << 5)
+#define SMSC_HW_CFG_LRST (0x1UL << 3) /* Lite reset */
+#define SMSC_HW_CFG_PSEL (0x1UL << 2)
+#define SMSC_HW_CFG_BCE (0x1UL << 1)
+#define SMSC_HW_CFG_SRST (0x1UL << 0)
+
+#define SMSC_PM_CTRL_PHY_RST (0x1UL << 4) /* PHY reset */
+
+#define SMSC_LED_GPIO_CFG_SPD_LED (0x1UL << 24)
+#define SMSC_LED_GPIO_CFG_LNK_LED (0x1UL << 20)
+#define SMSC_LED_GPIO_CFG_FDX_LED (0x1UL << 16)
+
+/* Hi watermark = 15.5Kb (~10 mtu pkts) */
+/* low watermark = 3k (~2 mtu pkts) */
+/* backpressure duration = ~ 350us */
+/* Apply FC on any frame. */
+#define AFC_CFG_DEFAULT (0x00F830A1)
+
+#define SMSC_EEPROM_CMD_BUSY (0x1UL << 31)
+#define SMSC_EEPROM_CMD_MASK (0x7UL << 28)
+#define SMSC_EEPROM_CMD_READ (0x0UL << 28)
+#define SMSC_EEPROM_CMD_WRITE (0x3UL << 28)
+#define SMSC_EEPROM_CMD_ERASE (0x5UL << 28)
+#define SMSC_EEPROM_CMD_RELOAD (0x7UL << 28)
+#define SMSC_EEPROM_CMD_TIMEOUT (0x1UL << 10)
+#define SMSC_EEPROM_CMD_ADDR_MASK 0x000001FFUL
+
+/* MAC Control and Status Register */
+#define SMSC_MAC_CSR_RCVOWN (0x1UL << 23) /* Half duplex */
+#define SMSC_MAC_CSR_LOOPBK (0x1UL << 21) /* Loopback */
+#define SMSC_MAC_CSR_FDPX (0x1UL << 20) /* Full duplex */
+#define SMSC_MAC_CSR_MCPAS (0x1UL << 19) /* Multicast mode */
+#define SMSC_MAC_CSR_PRMS (0x1UL << 18) /* Promiscuous mode */
+#define SMSC_MAC_CSR_INVFILT (0x1UL << 17) /* Inverse filtering */
+#define SMSC_MAC_CSR_PASSBAD (0x1UL << 16) /* Pass on bad frames */
+#define SMSC_MAC_CSR_HPFILT (0x1UL << 13) /* Hash filtering */
+#define SMSC_MAC_CSR_BCAST (0x1UL << 11) /* Broadcast */
+#define SMSC_MAC_CSR_TXEN (0x1UL << 3) /* TX enable */
+#define SMSC_MAC_CSR_RXEN (0x1UL << 2) /* RX enable */
+
+/* Interrupt control register */
+#define SMSC_INTR_NTEP (0x1UL << 31)
+#define SMSC_INTR_MACRTO (0x1UL << 19)
+#define SMSC_INTR_TX_STOP (0x1UL << 17)
+#define SMSC_INTR_RX_STOP (0x1UL << 16)
+#define SMSC_INTR_PHY_INT (0x1UL << 15)
+#define SMSC_INTR_TXE (0x1UL << 14)
+#define SMSC_INTR_TDFU (0x1UL << 13)
+#define SMSC_INTR_TDFO (0x1UL << 12)
+#define SMSC_INTR_RXDF (0x1UL << 11)
+#define SMSC_INTR_GPIOS 0x000007FFUL
+
+/* Phy MII interface register */
+#define SMSC_MII_WRITE (0x1UL << 1)
+#define SMSC_MII_READ (0x0UL << 1)
+#define SMSC_MII_BUSY (0x1UL << 0)
+
+/* H/W checksum register */
+#define SMSC_COE_CTRL_TX_EN (0x1UL << 16) /* Tx H/W csum enable */
+#define SMSC_COE_CTRL_RX_MODE (0x1UL << 1)
+#define SMSC_COE_CTRL_RX_EN (0x1UL << 0) /* Rx H/W csum enable */
+
+/* Registers on the phy, accessed via MII/MDIO */
+#define SMSC_PHY_INTR_STAT (29)
+#define SMSC_PHY_INTR_MASK (30)
+
+#define SMSC_PHY_INTR_ENERGY_ON (0x1U << 7)
+#define SMSC_PHY_INTR_ANEG_COMP (0x1U << 6)
+#define SMSC_PHY_INTR_REMOTE_FAULT (0x1U << 5)
+#define SMSC_PHY_INTR_LINK_DOWN (0x1U << 4)
+
+/* USB Vendor Requests */
+#define SMSC_UR_WRITE_REG 0xA0
+#define SMSC_UR_READ_REG 0xA1
+#define SMSC_UR_GET_STATS 0xA2
+
+#define SMSC_CONFIG_INDEX 0 /* config number 1 */
+#define SMSC_IFACE_IDX 0
+
+/*
+ * USB endpoints.
+ */
+enum {
+ SMSC_BULK_DT_RD,
+ SMSC_BULK_DT_WR,
+ /* the LAN9514 device does support interrupt endpoints, however I couldn't
+ * get then to work reliably and since they are unneeded (poll the mii
+ * status) they are unused.
+ * SMSC_INTR_DT_WR,
+ * SMSC_INTR_DT_RD,
+ */
+ SMSC_N_TRANSFER,
+};
+
+struct smsc_softc {
+ struct usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb_xfer *sc_xfer[SMSC_N_TRANSFER];
+ int sc_phyno;
+
+ /* The following stores the settings in the mac control (MAC_CSR) register */
+ uint32_t sc_mac_csr;
+ uint32_t sc_rev_id;
+
+ uint32_t sc_flags;
+#define SMSC_FLAG_LINK 0x0001
+#define SMSC_FLAG_LAN9514 0x1000 /* LAN9514 */
+};
+
+#define SMSC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define SMSC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define SMSC_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
+
+#endif /* _IF_SMSCREG_H_ */
diff --git a/freebsd/sys/dev/usb/net/if_udav.c b/freebsd/sys/dev/usb/net/if_udav.c
new file mode 100644
index 00000000..f4fcc4a5
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_udav.c
@@ -0,0 +1,883 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $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://ptm2.cc.utu.fi/ftp/network/cards/DM9601/From_NET/DM9601-DS-P01-930914.pdf
+ */
+
+/*
+ * TODO:
+ * Interrupt Endpoint support
+ * External PHYs
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR udav_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.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 usb_callback_t udav_bulk_write_callback;
+static usb_callback_t udav_bulk_read_callback;
+static usb_callback_t udav_intr_callback;
+
+static uether_fn_t udav_attach_post;
+static uether_fn_t udav_init;
+static uether_fn_t udav_stop;
+static uether_fn_t udav_start;
+static uether_fn_t udav_tick;
+static uether_fn_t udav_setmulti;
+static uether_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 usb_config udav_config[UDAV_N_TRANSFER] = {
+
+ [UDAV_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = (MCLBYTES + 2),
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = udav_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+
+ [UDAV_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = (MCLBYTES + 3),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = udav_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ },
+
+ [UDAV_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .bufsize = 0, /* use wMaxPacketSize */
+ .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),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, udav_miibus_readreg),
+ DEVMETHOD(miibus_writereg, udav_miibus_writereg),
+ DEVMETHOD(miibus_statchg, udav_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t udav_driver = {
+ .name = "udav",
+ .methods = udav_methods,
+ .size = sizeof(struct udav_softc),
+};
+
+static devclass_t udav_devclass;
+
+static const STRUCT_USB_HOST_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)},
+ /* ShanTou AMD8515 USB NIC */
+ {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ADM8515, 0)},
+ /* Kontron AG USB Ethernet */
+ {USB_VPI(USB_VENDOR_KONTRON, USB_PRODUCT_KONTRON_DM9601, 0)},
+ {USB_VPI(USB_VENDOR_KONTRON, USB_PRODUCT_KONTRON_JP1082,
+ UDAV_FLAG_NO_PHY)},
+};
+
+DRIVER_MODULE(udav, uhub, 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);
+MODULE_VERSION(udav, 1);
+USB_PNP_HOST_INFO(udav_devs);
+
+static const struct usb_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,
+};
+
+static const struct usb_ether_methods udav_ue_methods_nophy = {
+ .ue_attach_post = udav_attach_post,
+ .ue_start = udav_start,
+ .ue_init = udav_init,
+ .ue_stop = udav_stop,
+ .ue_setmulti = udav_setmulti,
+ .ue_setpromisc = udav_setpromisc,
+};
+
+#ifdef USB_DEBUG
+static int udav_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav");
+SYSCTL_INT(_hw_usb_udav, OID_AUTO, debug, CTLFLAG_RWTUN, &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 void
+udav_attach_post(struct usb_ether *ue)
+{
+ struct udav_softc *sc = uether_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 usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_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 (usbd_lookup_id_by_uaa(udav_devs, sizeof(udav_devs), uaa));
+}
+
+static int
+udav_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct udav_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = UDAV_IFACE_INDEX;
+ error = usbd_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;
+ }
+
+ /*
+ * The JP1082 has an unusable PHY and provides no link information.
+ */
+ if (sc->sc_flags & UDAV_FLAG_NO_PHY) {
+ ue->ue_methods = &udav_ue_methods_nophy;
+ sc->sc_flags |= UDAV_FLAG_LINK;
+ } else {
+ ue->ue_methods = &udav_ue_methods;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+
+ error = uether_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 usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER);
+ uether_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 usb_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 (uether_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 usb_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 (uether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+udav_mem_write1(struct udav_softc *sc, uint16_t offset,
+ uint8_t ch)
+{
+ struct usb_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 (uether_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 usb_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 (uether_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 usb_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 (uether_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 usb_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 (uether_do_request(&sc->sc_ue, &req, NULL, 1000));
+}
+
+static void
+udav_init(struct usb_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = uether_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);
+
+ usbd_xfer_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 (uether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ uether_pause(&sc->sc_ue, hz / 100);
+}
+
+#define UDAV_BITS 6
+static void
+udav_setmulti(struct usb_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = uether_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_maddr_rlock(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_maddr_runlock(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 usb_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = uether_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 usb_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[UDAV_INTR_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[UDAV_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[UDAV_BULK_DT_WR]);
+}
+
+static void
+udav_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct udav_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ 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");
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /* 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;
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, buf, 2);
+ usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len);
+
+ if (extra_len)
+ usbd_frame_zero(pc, 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);
+
+ usbd_xfer_set_frame_len(xfer, 0, temp_len);
+ usbd_transfer_submit(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct udav_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = uether_getifp(ue);
+ struct usb_page_cache *pc;
+ struct udav_rxpkt stat;
+ int len;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (actlen < (int)(sizeof(stat) + ETHER_CRC_LEN)) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, &stat, sizeof(stat));
+ actlen -= sizeof(stat);
+ len = min(actlen, le16toh(stat.pktlen));
+ len -= ETHER_CRC_LEN;
+
+ if (stat.rxstat & UDAV_RSR_LCS) {
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
+ goto tr_setup;
+ }
+ if (stat.rxstat & UDAV_RSR_ERR) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ uether_rxbuf(ue, pc, sizeof(stat), len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ return;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_stop(struct usb_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ if (!(sc->sc_flags & UDAV_FLAG_NO_PHY))
+ sc->sc_flags &= ~UDAV_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_RD]);
+ usbd_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);
+ struct mii_softc *miisc;
+ int error;
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~UDAV_FLAG_LINK;
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ error = mii_mediachg(mii);
+ return (error);
+}
+
+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);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ UDAV_UNLOCK(sc);
+}
+
+static void
+udav_tick(struct usb_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 */
+}
diff --git a/freebsd/sys/dev/usb/net/if_udavreg.h b/freebsd/sys/dev/usb/net/if_udavreg.h
new file mode 100644
index 00000000..7c35829b
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_udavreg.h
@@ -0,0 +1,167 @@
+/* $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) uether_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 usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb_xfer *sc_xfer[UDAV_N_TRANSFER];
+
+ int sc_flags;
+#define UDAV_FLAG_LINK 0x0001
+#define UDAV_FLAG_EXT_PHY 0x0040
+#define UDAV_FLAG_NO_PHY 0x0080
+};
+
+#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/freebsd/sys/dev/usb/net/if_ure.c b/freebsd/sys/dev/usb/net/if_ure.c
new file mode 100644
index 00000000..d5778ce4
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_ure.c
@@ -0,0 +1,1259 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2015-2016 Kevin Lo <kevlo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <rtems/bsd/sys/unistd.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR ure_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_urereg.h>
+
+#ifdef USB_DEBUG
+static int ure_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ure, CTLFLAG_RW, 0, "USB ure");
+SYSCTL_INT(_hw_usb_ure, OID_AUTO, debug, CTLFLAG_RWTUN, &ure_debug, 0,
+ "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const STRUCT_USB_HOST_ID ure_devs[] = {
+#define URE_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+ URE_DEV(REALTEK, RTL8152, URE_FLAG_8152),
+ URE_DEV(REALTEK, RTL8153, 0),
+#undef URE_DEV
+};
+
+static device_probe_t ure_probe;
+static device_attach_t ure_attach;
+static device_detach_t ure_detach;
+
+static usb_callback_t ure_bulk_read_callback;
+static usb_callback_t ure_bulk_write_callback;
+
+static miibus_readreg_t ure_miibus_readreg;
+static miibus_writereg_t ure_miibus_writereg;
+static miibus_statchg_t ure_miibus_statchg;
+
+static uether_fn_t ure_attach_post;
+static uether_fn_t ure_init;
+static uether_fn_t ure_stop;
+static uether_fn_t ure_start;
+static uether_fn_t ure_tick;
+static uether_fn_t ure_rxfilter;
+
+static int ure_ctl(struct ure_softc *, uint8_t, uint16_t, uint16_t,
+ void *, int);
+static int ure_read_mem(struct ure_softc *, uint16_t, uint16_t, void *,
+ int);
+static int ure_write_mem(struct ure_softc *, uint16_t, uint16_t, void *,
+ int);
+static uint8_t ure_read_1(struct ure_softc *, uint16_t, uint16_t);
+static uint16_t ure_read_2(struct ure_softc *, uint16_t, uint16_t);
+static uint32_t ure_read_4(struct ure_softc *, uint16_t, uint16_t);
+static int ure_write_1(struct ure_softc *, uint16_t, uint16_t, uint32_t);
+static int ure_write_2(struct ure_softc *, uint16_t, uint16_t, uint32_t);
+static int ure_write_4(struct ure_softc *, uint16_t, uint16_t, uint32_t);
+static uint16_t ure_ocp_reg_read(struct ure_softc *, uint16_t);
+static void ure_ocp_reg_write(struct ure_softc *, uint16_t, uint16_t);
+
+static void ure_read_chipver(struct ure_softc *);
+static int ure_attach_post_sub(struct usb_ether *);
+static void ure_reset(struct ure_softc *);
+static int ure_ifmedia_upd(struct ifnet *);
+static void ure_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static int ure_ioctl(struct ifnet *, u_long, caddr_t);
+static void ure_rtl8152_init(struct ure_softc *);
+static void ure_rtl8153_init(struct ure_softc *);
+static void ure_disable_teredo(struct ure_softc *);
+static void ure_init_fifo(struct ure_softc *);
+
+static const struct usb_config ure_config[URE_N_TRANSFER] = {
+ [URE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MCLBYTES,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = ure_bulk_write_callback,
+ .timeout = 10000, /* 10 seconds */
+ },
+ [URE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = 16384,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = ure_bulk_read_callback,
+ .timeout = 0, /* no timeout */
+ },
+};
+
+static device_method_t ure_methods[] = {
+ /* Device interface. */
+ DEVMETHOD(device_probe, ure_probe),
+ DEVMETHOD(device_attach, ure_attach),
+ DEVMETHOD(device_detach, ure_detach),
+
+ /* MII interface. */
+ DEVMETHOD(miibus_readreg, ure_miibus_readreg),
+ DEVMETHOD(miibus_writereg, ure_miibus_writereg),
+ DEVMETHOD(miibus_statchg, ure_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t ure_driver = {
+ .name = "ure",
+ .methods = ure_methods,
+ .size = sizeof(struct ure_softc),
+};
+
+static devclass_t ure_devclass;
+
+DRIVER_MODULE(ure, uhub, ure_driver, ure_devclass, NULL, NULL);
+DRIVER_MODULE(miibus, ure, miibus_driver, miibus_devclass, NULL, NULL);
+MODULE_DEPEND(ure, uether, 1, 1, 1);
+MODULE_DEPEND(ure, usb, 1, 1, 1);
+MODULE_DEPEND(ure, ether, 1, 1, 1);
+MODULE_DEPEND(ure, miibus, 1, 1, 1);
+MODULE_VERSION(ure, 1);
+
+static const struct usb_ether_methods ure_ue_methods = {
+ .ue_attach_post = ure_attach_post,
+ .ue_attach_post_sub = ure_attach_post_sub,
+ .ue_start = ure_start,
+ .ue_init = ure_init,
+ .ue_stop = ure_stop,
+ .ue_tick = ure_tick,
+ .ue_setmulti = ure_rxfilter,
+ .ue_setpromisc = ure_rxfilter,
+ .ue_mii_upd = ure_ifmedia_upd,
+ .ue_mii_sts = ure_ifmedia_sts,
+};
+
+static int
+ure_ctl(struct ure_softc *sc, uint8_t rw, uint16_t val, uint16_t index,
+ void *buf, int len)
+{
+ struct usb_device_request req;
+
+ URE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (rw == URE_CTL_WRITE)
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, len);
+
+ return (uether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+ure_read_mem(struct ure_softc *sc, uint16_t addr, uint16_t index,
+ void *buf, int len)
+{
+
+ return (ure_ctl(sc, URE_CTL_READ, addr, index, buf, len));
+}
+
+static int
+ure_write_mem(struct ure_softc *sc, uint16_t addr, uint16_t index,
+ void *buf, int len)
+{
+
+ return (ure_ctl(sc, URE_CTL_WRITE, addr, index, buf, len));
+}
+
+static uint8_t
+ure_read_1(struct ure_softc *sc, uint16_t reg, uint16_t index)
+{
+ uint32_t val;
+ uint8_t temp[4];
+ uint8_t shift;
+
+ shift = (reg & 3) << 3;
+ reg &= ~3;
+
+ ure_read_mem(sc, reg, index, &temp, 4);
+ val = UGETDW(temp);
+ val >>= shift;
+
+ return (val & 0xff);
+}
+
+static uint16_t
+ure_read_2(struct ure_softc *sc, uint16_t reg, uint16_t index)
+{
+ uint32_t val;
+ uint8_t temp[4];
+ uint8_t shift;
+
+ shift = (reg & 2) << 3;
+ reg &= ~3;
+
+ ure_read_mem(sc, reg, index, &temp, 4);
+ val = UGETDW(temp);
+ val >>= shift;
+
+ return (val & 0xffff);
+}
+
+static uint32_t
+ure_read_4(struct ure_softc *sc, uint16_t reg, uint16_t index)
+{
+ uint8_t temp[4];
+
+ ure_read_mem(sc, reg, index, &temp, 4);
+ return (UGETDW(temp));
+}
+
+static int
+ure_write_1(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val)
+{
+ uint16_t byen;
+ uint8_t temp[4];
+ uint8_t shift;
+
+ byen = URE_BYTE_EN_BYTE;
+ shift = reg & 3;
+ val &= 0xff;
+
+ if (reg & 3) {
+ byen <<= shift;
+ val <<= (shift << 3);
+ reg &= ~3;
+ }
+
+ USETDW(temp, val);
+ return (ure_write_mem(sc, reg, index | byen, &temp, 4));
+}
+
+static int
+ure_write_2(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val)
+{
+ uint16_t byen;
+ uint8_t temp[4];
+ uint8_t shift;
+
+ byen = URE_BYTE_EN_WORD;
+ shift = reg & 2;
+ val &= 0xffff;
+
+ if (reg & 2) {
+ byen <<= shift;
+ val <<= (shift << 3);
+ reg &= ~3;
+ }
+
+ USETDW(temp, val);
+ return (ure_write_mem(sc, reg, index | byen, &temp, 4));
+}
+
+static int
+ure_write_4(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val)
+{
+ uint8_t temp[4];
+
+ USETDW(temp, val);
+ return (ure_write_mem(sc, reg, index | URE_BYTE_EN_DWORD, &temp, 4));
+}
+
+static uint16_t
+ure_ocp_reg_read(struct ure_softc *sc, uint16_t addr)
+{
+ uint16_t reg;
+
+ ure_write_2(sc, URE_PLA_OCP_GPHY_BASE, URE_MCU_TYPE_PLA, addr & 0xf000);
+ reg = (addr & 0x0fff) | 0xb000;
+
+ return (ure_read_2(sc, reg, URE_MCU_TYPE_PLA));
+}
+
+static void
+ure_ocp_reg_write(struct ure_softc *sc, uint16_t addr, uint16_t data)
+{
+ uint16_t reg;
+
+ ure_write_2(sc, URE_PLA_OCP_GPHY_BASE, URE_MCU_TYPE_PLA, addr & 0xf000);
+ reg = (addr & 0x0fff) | 0xb000;
+
+ ure_write_2(sc, reg, URE_MCU_TYPE_PLA, data);
+}
+
+static int
+ure_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct ure_softc *sc;
+ uint16_t val;
+ int locked;
+
+ sc = device_get_softc(dev);
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ URE_LOCK(sc);
+
+ /* Let the rgephy driver read the URE_GMEDIASTAT register. */
+ if (reg == URE_GMEDIASTAT) {
+ if (!locked)
+ URE_UNLOCK(sc);
+ return (ure_read_1(sc, URE_GMEDIASTAT, URE_MCU_TYPE_PLA));
+ }
+
+ val = ure_ocp_reg_read(sc, URE_OCP_BASE_MII + reg * 2);
+
+ if (!locked)
+ URE_UNLOCK(sc);
+ return (val);
+}
+
+static int
+ure_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct ure_softc *sc;
+ int locked;
+
+ sc = device_get_softc(dev);
+ if (sc->sc_phyno != phy)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ URE_LOCK(sc);
+
+ ure_ocp_reg_write(sc, URE_OCP_BASE_MII + reg * 2, val);
+
+ if (!locked)
+ URE_UNLOCK(sc);
+ return (0);
+}
+
+static void
+ure_miibus_statchg(device_t dev)
+{
+ struct ure_softc *sc;
+ struct mii_data *mii;
+ struct ifnet *ifp;
+ int locked;
+
+ sc = device_get_softc(dev);
+ mii = GET_MII(sc);
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ URE_LOCK(sc);
+
+ ifp = uether_getifp(&sc->sc_ue);
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ goto done;
+
+ sc->sc_flags &= ~URE_FLAG_LINK;
+ if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+ (IFM_ACTIVE | IFM_AVALID)) {
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_10_T:
+ case IFM_100_TX:
+ sc->sc_flags |= URE_FLAG_LINK;
+ break;
+ case IFM_1000_T:
+ if ((sc->sc_flags & URE_FLAG_8152) != 0)
+ break;
+ sc->sc_flags |= URE_FLAG_LINK;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Lost link, do nothing. */
+ if ((sc->sc_flags & URE_FLAG_LINK) == 0)
+ goto done;
+done:
+ if (!locked)
+ URE_UNLOCK(sc);
+}
+
+/*
+ * Probe for a RTL8152/RTL8153 chip.
+ */
+static int
+ure_probe(device_t dev)
+{
+ struct usb_attach_arg *uaa;
+
+ uaa = device_get_ivars(dev);
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != URE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != URE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(ure_devs, sizeof(ure_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+ure_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct ure_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+ device_set_usb_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = URE_IFACE_IDX;
+ error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ ure_config, URE_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error != 0) {
+ 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 = &ure_ue_methods;
+
+ error = uether_ifattach(ue);
+ if (error != 0) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ ure_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+ure_detach(device_t dev)
+{
+ struct ure_softc *sc = device_get_softc(dev);
+ struct usb_ether *ue = &sc->sc_ue;
+
+ usbd_transfer_unsetup(sc->sc_xfer, URE_N_TRANSFER);
+ uether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+ure_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ure_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = uether_getifp(ue);
+ struct usb_page_cache *pc;
+ struct ure_rxpkt pkt;
+ int actlen, len;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (actlen < (int)(sizeof(pkt))) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, &pkt, sizeof(pkt));
+ len = le32toh(pkt.ure_pktlen) & URE_RXPKT_LEN_MASK;
+ len -= ETHER_CRC_LEN;
+ if (actlen < (int)(len + sizeof(pkt))) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ goto tr_setup;
+ }
+
+ uether_rxbuf(ue, pc, sizeof(pkt), len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ uether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ure_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ure_softc *sc = usbd_xfer_softc(xfer);
+ struct ifnet *ifp = uether_getifp(&sc->sc_ue);
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ struct ure_txpkt txpkt;
+ int len, pos;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & URE_FLAG_LINK) == 0 ||
+ (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ pos = 0;
+ len = m->m_pkthdr.len;
+ pc = usbd_xfer_get_frame(xfer, 0);
+ memset(&txpkt, 0, sizeof(txpkt));
+ txpkt.ure_pktlen = htole32((len & URE_TXPKT_LEN_MASK) |
+ URE_TKPKT_TX_FS | URE_TKPKT_TX_LS);
+ usbd_copy_in(pc, pos, &txpkt, sizeof(txpkt));
+ pos += sizeof(txpkt);
+ usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len);
+ pos += m->m_pkthdr.len;
+
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+
+ /*
+ * If there's a BPF listener, bounce a copy
+ * of this frame to him.
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ /* Set frame length. */
+ usbd_xfer_set_frame_len(xfer, 0, pos);
+
+ usbd_transfer_submit(xfer);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ return;
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ure_read_chipver(struct ure_softc *sc)
+{
+ uint16_t ver;
+
+ ver = ure_read_2(sc, URE_PLA_TCR1, URE_MCU_TYPE_PLA) & URE_VERSION_MASK;
+ switch (ver) {
+ case 0x4c00:
+ sc->sc_chip |= URE_CHIP_VER_4C00;
+ break;
+ case 0x4c10:
+ sc->sc_chip |= URE_CHIP_VER_4C10;
+ break;
+ case 0x5c00:
+ sc->sc_chip |= URE_CHIP_VER_5C00;
+ break;
+ case 0x5c10:
+ sc->sc_chip |= URE_CHIP_VER_5C10;
+ break;
+ case 0x5c20:
+ sc->sc_chip |= URE_CHIP_VER_5C20;
+ break;
+ case 0x5c30:
+ sc->sc_chip |= URE_CHIP_VER_5C30;
+ break;
+ default:
+ device_printf(sc->sc_ue.ue_dev,
+ "unknown version 0x%04x\n", ver);
+ break;
+ }
+}
+
+static void
+ure_attach_post(struct usb_ether *ue)
+{
+ struct ure_softc *sc = uether_getsc(ue);
+
+ sc->sc_phyno = 0;
+
+ /* Determine the chip version. */
+ ure_read_chipver(sc);
+
+ /* Initialize controller and get station address. */
+ if (sc->sc_flags & URE_FLAG_8152)
+ ure_rtl8152_init(sc);
+ else
+ ure_rtl8153_init(sc);
+
+ if (sc->sc_chip & URE_CHIP_VER_4C00)
+ ure_read_mem(sc, URE_PLA_IDR, URE_MCU_TYPE_PLA,
+ ue->ue_eaddr, 8);
+ else
+ ure_read_mem(sc, URE_PLA_BACKUP, URE_MCU_TYPE_PLA,
+ ue->ue_eaddr, 8);
+}
+
+static int
+ure_attach_post_sub(struct usb_ether *ue)
+{
+ struct ure_softc *sc;
+ struct ifnet *ifp;
+ int error;
+
+ sc = uether_getsc(ue);
+ ifp = ue->ue_ifp;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_start = uether_start;
+ ifp->if_ioctl = ure_ioctl;
+ ifp->if_init = uether_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+ ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ mtx_lock(&Giant);
+ error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
+ uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
+ BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, 0);
+ mtx_unlock(&Giant);
+
+ return (error);
+}
+
+static void
+ure_init(struct usb_ether *ue)
+{
+ struct ure_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ URE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ return;
+
+ /* Cancel pending I/O. */
+ ure_stop(ue);
+
+ ure_reset(sc);
+
+ /* Set MAC address. */
+ ure_write_mem(sc, URE_PLA_IDR, URE_MCU_TYPE_PLA | URE_BYTE_EN_SIX_BYTES,
+ IF_LLADDR(ifp), 8);
+
+ /* Reset the packet filter. */
+ ure_write_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA) &
+ ~URE_FMC_FCR_MCU_EN);
+ ure_write_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA) |
+ URE_FMC_FCR_MCU_EN);
+
+ /* Enable transmit and receive. */
+ ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA,
+ ure_read_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA) | URE_CR_RE |
+ URE_CR_TE);
+
+ ure_write_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) &
+ ~URE_RXDY_GATED_EN);
+
+ /* Configure RX filters. */
+ ure_rxfilter(ue);
+
+ usbd_xfer_set_stall(sc->sc_xfer[URE_BULK_DT_WR]);
+
+ /* Indicate we are up and running. */
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ /* Switch to selected media. */
+ ure_ifmedia_upd(ifp);
+}
+
+static void
+ure_tick(struct usb_ether *ue)
+{
+ struct ure_softc *sc = uether_getsc(ue);
+ struct mii_data *mii = GET_MII(sc);
+
+ URE_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & URE_FLAG_LINK) == 0
+ && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->sc_flags |= URE_FLAG_LINK;
+ ure_start(ue);
+ }
+}
+
+/*
+ * Program the 64-bit multicast hash filter.
+ */
+static void
+ure_rxfilter(struct usb_ether *ue)
+{
+ struct ure_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ uint32_t h, rxmode;
+ uint32_t hashes[2] = { 0, 0 };
+
+ URE_LOCK_ASSERT(sc, MA_OWNED);
+
+ rxmode = URE_RCR_APM;
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxmode |= URE_RCR_AB;
+ if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+ if (ifp->if_flags & IFF_PROMISC)
+ rxmode |= URE_RCR_AAP;
+ rxmode |= URE_RCR_AM;
+ hashes[0] = hashes[1] = 0xffffffff;
+ goto done;
+ }
+
+ rxmode |= URE_RCR_AM;
+ if_maddr_rlock(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));
+ }
+ if_maddr_runlock(ifp);
+
+ h = bswap32(hashes[0]);
+ hashes[0] = bswap32(hashes[1]);
+ hashes[1] = h;
+ rxmode |= URE_RCR_AM;
+
+done:
+ ure_write_4(sc, URE_PLA_MAR0, URE_MCU_TYPE_PLA, hashes[0]);
+ ure_write_4(sc, URE_PLA_MAR4, URE_MCU_TYPE_PLA, hashes[1]);
+ ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA, rxmode);
+}
+
+static void
+ure_start(struct usb_ether *ue)
+{
+ struct ure_softc *sc = uether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usbd_transfer_start(sc->sc_xfer[URE_BULK_DT_RD]);
+ usbd_transfer_start(sc->sc_xfer[URE_BULK_DT_WR]);
+}
+
+static void
+ure_reset(struct ure_softc *sc)
+{
+ int i;
+
+ ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA, URE_CR_RST);
+
+ for (i = 0; i < URE_TIMEOUT; i++) {
+ if (!(ure_read_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA) &
+ URE_CR_RST))
+ break;
+ uether_pause(&sc->sc_ue, hz / 100);
+ }
+ if (i == URE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "reset never completed\n");
+}
+
+/*
+ * Set media options.
+ */
+static int
+ure_ifmedia_upd(struct ifnet *ifp)
+{
+ struct ure_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+ struct mii_softc *miisc;
+ int error;
+
+ URE_LOCK_ASSERT(sc, MA_OWNED);
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ error = mii_mediachg(mii);
+ return (error);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+ure_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct ure_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ mii = GET_MII(sc);
+
+ URE_LOCK(sc);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ URE_UNLOCK(sc);
+}
+
+static int
+ure_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct usb_ether *ue = ifp->if_softc;
+ struct ure_softc *sc;
+ struct ifreq *ifr;
+ int error, mask, reinit;
+
+ sc = uether_getsc(ue);
+ ifr = (struct ifreq *)data;
+ error = 0;
+ reinit = 0;
+ if (cmd == SIOCSIFCAP) {
+ URE_LOCK(sc);
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+ if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ else
+ reinit = 0;
+ URE_UNLOCK(sc);
+ if (reinit > 0)
+ uether_init(ue);
+ } else
+ error = uether_ioctl(ifp, cmd, data);
+
+ return (error);
+}
+
+static void
+ure_rtl8152_init(struct ure_softc *sc)
+{
+ uint32_t pwrctrl;
+
+ /* Disable ALDPS. */
+ ure_ocp_reg_write(sc, URE_OCP_ALDPS_CONFIG, URE_ENPDNPS | URE_LINKENA |
+ URE_DIS_SDSAVE);
+ uether_pause(&sc->sc_ue, hz / 50);
+
+ if (sc->sc_chip & URE_CHIP_VER_4C00) {
+ ure_write_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA) &
+ ~URE_LED_MODE_MASK);
+ }
+
+ ure_write_2(sc, URE_USB_UPS_CTRL, URE_MCU_TYPE_USB,
+ ure_read_2(sc, URE_USB_UPS_CTRL, URE_MCU_TYPE_USB) &
+ ~URE_POWER_CUT);
+ ure_write_2(sc, URE_USB_PM_CTRL_STATUS, URE_MCU_TYPE_USB,
+ ure_read_2(sc, URE_USB_PM_CTRL_STATUS, URE_MCU_TYPE_USB) &
+ ~URE_RESUME_INDICATE);
+
+ ure_write_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA) |
+ URE_TX_10M_IDLE_EN | URE_PFM_PWM_SWITCH);
+ pwrctrl = ure_read_4(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA);
+ pwrctrl &= ~URE_MCU_CLK_RATIO_MASK;
+ pwrctrl |= URE_MCU_CLK_RATIO | URE_D3_CLK_GATED_EN;
+ ure_write_4(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA, pwrctrl);
+ ure_write_2(sc, URE_PLA_GPHY_INTR_IMR, URE_MCU_TYPE_PLA,
+ URE_GPHY_STS_MSK | URE_SPEED_DOWN_MSK | URE_SPDWN_RXDV_MSK |
+ URE_SPDWN_LINKCHG_MSK);
+
+ /* Disable Rx aggregation. */
+ ure_write_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB,
+ ure_read_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB) |
+ URE_RX_AGG_DISABLE);
+
+ /* Disable ALDPS. */
+ ure_ocp_reg_write(sc, URE_OCP_ALDPS_CONFIG, URE_ENPDNPS | URE_LINKENA |
+ URE_DIS_SDSAVE);
+ uether_pause(&sc->sc_ue, hz / 50);
+
+ ure_init_fifo(sc);
+
+ ure_write_1(sc, URE_USB_TX_AGG, URE_MCU_TYPE_USB,
+ URE_TX_AGG_MAX_THRESHOLD);
+ ure_write_4(sc, URE_USB_RX_BUF_TH, URE_MCU_TYPE_USB, URE_RX_THR_HIGH);
+ ure_write_4(sc, URE_USB_TX_DMA, URE_MCU_TYPE_USB,
+ URE_TEST_MODE_DISABLE | URE_TX_SIZE_ADJUST1);
+}
+
+static void
+ure_rtl8153_init(struct ure_softc *sc)
+{
+ uint16_t val;
+ uint8_t u1u2[8];
+ int i;
+
+ /* Disable ALDPS. */
+ ure_ocp_reg_write(sc, URE_OCP_POWER_CFG,
+ ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) & ~URE_EN_ALDPS);
+ uether_pause(&sc->sc_ue, hz / 50);
+
+ memset(u1u2, 0x00, sizeof(u1u2));
+ ure_write_mem(sc, URE_USB_TOLERANCE,
+ URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2));
+
+ for (i = 0; i < URE_TIMEOUT; i++) {
+ if (ure_read_2(sc, URE_PLA_BOOT_CTRL, URE_MCU_TYPE_PLA) &
+ URE_AUTOLOAD_DONE)
+ break;
+ uether_pause(&sc->sc_ue, hz / 100);
+ }
+ if (i == URE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev,
+ "timeout waiting for chip autoload\n");
+
+ for (i = 0; i < URE_TIMEOUT; i++) {
+ val = ure_ocp_reg_read(sc, URE_OCP_PHY_STATUS) &
+ URE_PHY_STAT_MASK;
+ if (val == URE_PHY_STAT_LAN_ON || val == URE_PHY_STAT_PWRDN)
+ break;
+ uether_pause(&sc->sc_ue, hz / 100);
+ }
+ if (i == URE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev,
+ "timeout waiting for phy to stabilize\n");
+
+ ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB,
+ ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB) &
+ ~URE_U2P3_ENABLE);
+
+ if (sc->sc_chip & URE_CHIP_VER_5C10) {
+ val = ure_read_2(sc, URE_USB_SSPHYLINK2, URE_MCU_TYPE_USB);
+ val &= ~URE_PWD_DN_SCALE_MASK;
+ val |= URE_PWD_DN_SCALE(96);
+ ure_write_2(sc, URE_USB_SSPHYLINK2, URE_MCU_TYPE_USB, val);
+
+ ure_write_1(sc, URE_USB_USB2PHY, URE_MCU_TYPE_USB,
+ ure_read_1(sc, URE_USB_USB2PHY, URE_MCU_TYPE_USB) |
+ URE_USB2PHY_L1 | URE_USB2PHY_SUSPEND);
+ } else if (sc->sc_chip & URE_CHIP_VER_5C20) {
+ ure_write_1(sc, URE_PLA_DMY_REG0, URE_MCU_TYPE_PLA,
+ ure_read_1(sc, URE_PLA_DMY_REG0, URE_MCU_TYPE_PLA) &
+ ~URE_ECM_ALDPS);
+ }
+ if (sc->sc_chip & (URE_CHIP_VER_5C20 | URE_CHIP_VER_5C30)) {
+ val = ure_read_1(sc, URE_USB_CSR_DUMMY1, URE_MCU_TYPE_USB);
+ if (ure_read_2(sc, URE_USB_BURST_SIZE, URE_MCU_TYPE_USB) ==
+ 0)
+ val &= ~URE_DYNAMIC_BURST;
+ else
+ val |= URE_DYNAMIC_BURST;
+ ure_write_1(sc, URE_USB_CSR_DUMMY1, URE_MCU_TYPE_USB, val);
+ }
+
+ ure_write_1(sc, URE_USB_CSR_DUMMY2, URE_MCU_TYPE_USB,
+ ure_read_1(sc, URE_USB_CSR_DUMMY2, URE_MCU_TYPE_USB) |
+ URE_EP4_FULL_FC);
+
+ ure_write_2(sc, URE_USB_WDT11_CTRL, URE_MCU_TYPE_USB,
+ ure_read_2(sc, URE_USB_WDT11_CTRL, URE_MCU_TYPE_USB) &
+ ~URE_TIMER11_EN);
+
+ ure_write_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA) &
+ ~URE_LED_MODE_MASK);
+
+ if ((sc->sc_chip & URE_CHIP_VER_5C10) &&
+ usbd_get_speed(sc->sc_ue.ue_udev) != USB_SPEED_SUPER)
+ val = URE_LPM_TIMER_500MS;
+ else
+ val = URE_LPM_TIMER_500US;
+ ure_write_1(sc, URE_USB_LPM_CTRL, URE_MCU_TYPE_USB,
+ val | URE_FIFO_EMPTY_1FB | URE_ROK_EXIT_LPM);
+
+ val = ure_read_2(sc, URE_USB_AFE_CTRL2, URE_MCU_TYPE_USB);
+ val &= ~URE_SEN_VAL_MASK;
+ val |= URE_SEN_VAL_NORMAL | URE_SEL_RXIDLE;
+ ure_write_2(sc, URE_USB_AFE_CTRL2, URE_MCU_TYPE_USB, val);
+
+ ure_write_2(sc, URE_USB_CONNECT_TIMER, URE_MCU_TYPE_USB, 0x0001);
+
+ ure_write_2(sc, URE_USB_POWER_CUT, URE_MCU_TYPE_USB,
+ ure_read_2(sc, URE_USB_POWER_CUT, URE_MCU_TYPE_USB) &
+ ~(URE_PWR_EN | URE_PHASE2_EN));
+ ure_write_2(sc, URE_USB_MISC_0, URE_MCU_TYPE_USB,
+ ure_read_2(sc, URE_USB_MISC_0, URE_MCU_TYPE_USB) &
+ ~URE_PCUT_STATUS);
+
+ memset(u1u2, 0xff, sizeof(u1u2));
+ ure_write_mem(sc, URE_USB_TOLERANCE,
+ URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2));
+
+ ure_write_2(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA,
+ URE_ALDPS_SPDWN_RATIO);
+ ure_write_2(sc, URE_PLA_MAC_PWR_CTRL2, URE_MCU_TYPE_PLA,
+ URE_EEE_SPDWN_RATIO);
+ ure_write_2(sc, URE_PLA_MAC_PWR_CTRL3, URE_MCU_TYPE_PLA,
+ URE_PKT_AVAIL_SPDWN_EN | URE_SUSPEND_SPDWN_EN |
+ URE_U1U2_SPDWN_EN | URE_L1_SPDWN_EN);
+ ure_write_2(sc, URE_PLA_MAC_PWR_CTRL4, URE_MCU_TYPE_PLA,
+ URE_PWRSAVE_SPDWN_EN | URE_RXDV_SPDWN_EN | URE_TX10MIDLE_EN |
+ URE_TP100_SPDWN_EN | URE_TP500_SPDWN_EN | URE_TP1000_SPDWN_EN |
+ URE_EEE_SPDWN_EN);
+
+ val = ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB);
+ if (!(sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10)))
+ val |= URE_U2P3_ENABLE;
+ else
+ val &= ~URE_U2P3_ENABLE;
+ ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB, val);
+
+ memset(u1u2, 0x00, sizeof(u1u2));
+ ure_write_mem(sc, URE_USB_TOLERANCE,
+ URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2));
+
+ /* Disable ALDPS. */
+ ure_ocp_reg_write(sc, URE_OCP_POWER_CFG,
+ ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) & ~URE_EN_ALDPS);
+ uether_pause(&sc->sc_ue, hz / 50);
+
+ ure_init_fifo(sc);
+
+ /* Disable Rx aggregation. */
+ ure_write_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB,
+ ure_read_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB) |
+ URE_RX_AGG_DISABLE);
+
+ val = ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB);
+ if (!(sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10)))
+ val |= URE_U2P3_ENABLE;
+ else
+ val &= ~URE_U2P3_ENABLE;
+ ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB, val);
+
+ memset(u1u2, 0xff, sizeof(u1u2));
+ ure_write_mem(sc, URE_USB_TOLERANCE,
+ URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2));
+}
+
+static void
+ure_stop(struct usb_ether *ue)
+{
+ struct ure_softc *sc = uether_getsc(ue);
+ struct ifnet *ifp = uether_getifp(ue);
+
+ URE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ sc->sc_flags &= ~URE_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usbd_transfer_stop(sc->sc_xfer[URE_BULK_DT_WR]);
+ usbd_transfer_stop(sc->sc_xfer[URE_BULK_DT_RD]);
+}
+
+static void
+ure_disable_teredo(struct ure_softc *sc)
+{
+
+ ure_write_4(sc, URE_PLA_TEREDO_CFG, URE_MCU_TYPE_PLA,
+ ure_read_4(sc, URE_PLA_TEREDO_CFG, URE_MCU_TYPE_PLA) &
+ ~(URE_TEREDO_SEL | URE_TEREDO_RS_EVENT_MASK | URE_OOB_TEREDO_EN));
+ ure_write_2(sc, URE_PLA_WDT6_CTRL, URE_MCU_TYPE_PLA,
+ URE_WDT6_SET_MODE);
+ ure_write_2(sc, URE_PLA_REALWOW_TIMER, URE_MCU_TYPE_PLA, 0);
+ ure_write_4(sc, URE_PLA_TEREDO_TIMER, URE_MCU_TYPE_PLA, 0);
+}
+
+static void
+ure_init_fifo(struct ure_softc *sc)
+{
+ uint32_t rx_fifo1, rx_fifo2;
+ int i;
+
+ ure_write_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) |
+ URE_RXDY_GATED_EN);
+
+ ure_disable_teredo(sc);
+
+ ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA,
+ ure_read_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA) &
+ ~URE_RCR_ACPT_ALL);
+
+ if (!(sc->sc_flags & URE_FLAG_8152)) {
+ if (sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10 |
+ URE_CHIP_VER_5C20)) {
+ ure_ocp_reg_write(sc, URE_OCP_ADC_CFG,
+ URE_CKADSEL_L | URE_ADC_EN | URE_EN_EMI_L);
+ }
+ if (sc->sc_chip & URE_CHIP_VER_5C00) {
+ ure_ocp_reg_write(sc, URE_OCP_EEE_CFG,
+ ure_ocp_reg_read(sc, URE_OCP_EEE_CFG) &
+ ~URE_CTAP_SHORT_EN);
+ }
+ ure_ocp_reg_write(sc, URE_OCP_POWER_CFG,
+ ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) |
+ URE_EEE_CLKDIV_EN);
+ ure_ocp_reg_write(sc, URE_OCP_DOWN_SPEED,
+ ure_ocp_reg_read(sc, URE_OCP_DOWN_SPEED) |
+ URE_EN_10M_BGOFF);
+ ure_ocp_reg_write(sc, URE_OCP_POWER_CFG,
+ ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) |
+ URE_EN_10M_PLLOFF);
+ ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_IMPEDANCE);
+ ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x0b13);
+ ure_write_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA) |
+ URE_PFM_PWM_SWITCH);
+
+ /* Enable LPF corner auto tune. */
+ ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_LPF_CFG);
+ ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0xf70f);
+
+ /* Adjust 10M amplitude. */
+ ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_10M_AMP1);
+ ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x00af);
+ ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_10M_AMP2);
+ ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x0208);
+ }
+
+ ure_reset(sc);
+
+ ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA, 0);
+
+ ure_write_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA,
+ ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
+ ~URE_NOW_IS_OOB);
+
+ ure_write_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) &
+ ~URE_MCU_BORW_EN);
+ for (i = 0; i < URE_TIMEOUT; i++) {
+ if (ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
+ URE_LINK_LIST_READY)
+ break;
+ uether_pause(&sc->sc_ue, hz / 100);
+ }
+ if (i == URE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev,
+ "timeout waiting for OOB control\n");
+ ure_write_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) |
+ URE_RE_INIT_LL);
+ for (i = 0; i < URE_TIMEOUT; i++) {
+ if (ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) &
+ URE_LINK_LIST_READY)
+ break;
+ uether_pause(&sc->sc_ue, hz / 100);
+ }
+ if (i == URE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev,
+ "timeout waiting for OOB control\n");
+
+ ure_write_2(sc, URE_PLA_CPCR, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_CPCR, URE_MCU_TYPE_PLA) &
+ ~URE_CPCR_RX_VLAN);
+ ure_write_2(sc, URE_PLA_TCR0, URE_MCU_TYPE_PLA,
+ ure_read_2(sc, URE_PLA_TCR0, URE_MCU_TYPE_PLA) |
+ URE_TCR0_AUTO_FIFO);
+
+ /* Configure Rx FIFO threshold. */
+ ure_write_4(sc, URE_PLA_RXFIFO_CTRL0, URE_MCU_TYPE_PLA,
+ URE_RXFIFO_THR1_NORMAL);
+ if (usbd_get_speed(sc->sc_ue.ue_udev) == USB_SPEED_FULL) {
+ rx_fifo1 = URE_RXFIFO_THR2_FULL;
+ rx_fifo2 = URE_RXFIFO_THR3_FULL;
+ } else {
+ rx_fifo1 = URE_RXFIFO_THR2_HIGH;
+ rx_fifo2 = URE_RXFIFO_THR3_HIGH;
+ }
+ ure_write_4(sc, URE_PLA_RXFIFO_CTRL1, URE_MCU_TYPE_PLA, rx_fifo1);
+ ure_write_4(sc, URE_PLA_RXFIFO_CTRL2, URE_MCU_TYPE_PLA, rx_fifo2);
+
+ /* Configure Tx FIFO threshold. */
+ ure_write_4(sc, URE_PLA_TXFIFO_CTRL, URE_MCU_TYPE_PLA,
+ URE_TXFIFO_THR_NORMAL);
+}
diff --git a/freebsd/sys/dev/usb/net/if_urereg.h b/freebsd/sys/dev/usb/net/if_urereg.h
new file mode 100644
index 00000000..8eff1c25
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/if_urereg.h
@@ -0,0 +1,440 @@
+/*-
+ * Copyright (c) 2015-2016 Kevin Lo <kevlo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define URE_CONFIG_IDX 0 /* config number 1 */
+#define URE_IFACE_IDX 0
+
+#define URE_CTL_READ 0x01
+#define URE_CTL_WRITE 0x02
+
+#define URE_TIMEOUT 1000
+#define URE_PHY_TIMEOUT 2000
+
+#define URE_BYTE_EN_DWORD 0xff
+#define URE_BYTE_EN_WORD 0x33
+#define URE_BYTE_EN_BYTE 0x11
+#define URE_BYTE_EN_SIX_BYTES 0x3f
+
+#define URE_MAX_FRAMELEN (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN)
+
+#define URE_PLA_IDR 0xc000
+#define URE_PLA_RCR 0xc010
+#define URE_PLA_RMS 0xc016
+#define URE_PLA_RXFIFO_CTRL0 0xc0a0
+#define URE_PLA_RXFIFO_CTRL1 0xc0a4
+#define URE_PLA_RXFIFO_CTRL2 0xc0a8
+#define URE_PLA_DMY_REG0 0xc0b0
+#define URE_PLA_FMC 0xc0b4
+#define URE_PLA_CFG_WOL 0xc0b6
+#define URE_PLA_TEREDO_CFG 0xc0bc
+#define URE_PLA_MAR0 0xcd00
+#define URE_PLA_MAR4 0xcd04
+#define URE_PLA_BACKUP 0xd000
+#define URE_PAL_BDC_CR 0xd1a0
+#define URE_PLA_TEREDO_TIMER 0xd2cc
+#define URE_PLA_REALWOW_TIMER 0xd2e8
+#define URE_PLA_LEDSEL 0xdd90
+#define URE_PLA_LED_FEATURE 0xdd92
+#define URE_PLA_PHYAR 0xde00
+#define URE_PLA_BOOT_CTRL 0xe004
+#define URE_PLA_GPHY_INTR_IMR 0xe022
+#define URE_PLA_EEE_CR 0xe040
+#define URE_PLA_EEEP_CR 0xe080
+#define URE_PLA_MAC_PWR_CTRL 0xe0c0
+#define URE_PLA_MAC_PWR_CTRL2 0xe0ca
+#define URE_PLA_MAC_PWR_CTRL3 0xe0cc
+#define URE_PLA_MAC_PWR_CTRL4 0xe0ce
+#define URE_PLA_WDT6_CTRL 0xe428
+#define URE_PLA_TCR0 0xe610
+#define URE_PLA_TCR1 0xe612
+#define URE_PLA_MTPS 0xe615
+#define URE_PLA_TXFIFO_CTRL 0xe618
+#define URE_PLA_RSTTELLY 0xe800
+#define URE_PLA_CR 0xe813
+#define URE_PLA_CRWECR 0xe81c
+#define URE_PLA_CONFIG5 0xe822
+#define URE_PLA_PHY_PWR 0xe84c
+#define URE_PLA_OOB_CTRL 0xe84f
+#define URE_PLA_CPCR 0xe854
+#define URE_PLA_MISC_0 0xe858
+#define URE_PLA_MISC_1 0xe85a
+#define URE_PLA_OCP_GPHY_BASE 0xe86c
+#define URE_PLA_TELLYCNT 0xe890
+#define URE_PLA_SFF_STS_7 0xe8de
+#define URE_GMEDIASTAT 0xe908
+
+#define URE_USB_USB2PHY 0xb41e
+#define URE_USB_SSPHYLINK2 0xb428
+#define URE_USB_U2P3_CTRL 0xb460
+#define URE_USB_CSR_DUMMY1 0xb464
+#define URE_USB_CSR_DUMMY2 0xb466
+#define URE_USB_DEV_STAT 0xb808
+#define URE_USB_CONNECT_TIMER 0xcbf8
+#define URE_USB_BURST_SIZE 0xcfc0
+#define URE_USB_USB_CTRL 0xd406
+#define URE_USB_PHY_CTRL 0xd408
+#define URE_USB_TX_AGG 0xd40a
+#define URE_USB_RX_BUF_TH 0xd40c
+#define URE_USB_USB_TIMER 0xd428
+#define URE_USB_RX_EARLY_AGG 0xd42c
+#define URE_USB_PM_CTRL_STATUS 0xd432
+#define URE_USB_TX_DMA 0xd434
+#define URE_USB_TOLERANCE 0xd490
+#define URE_USB_LPM_CTRL 0xd41a
+#define URE_USB_UPS_CTRL 0xd800
+#define URE_USB_MISC_0 0xd81a
+#define URE_USB_POWER_CUT 0xd80a
+#define URE_USB_AFE_CTRL2 0xd824
+#define URE_USB_WDT11_CTRL 0xe43c
+
+/* OCP Registers. */
+#define URE_OCP_ALDPS_CONFIG 0x2010
+#define URE_OCP_EEE_CONFIG1 0x2080
+#define URE_OCP_EEE_CONFIG2 0x2092
+#define URE_OCP_EEE_CONFIG3 0x2094
+#define URE_OCP_BASE_MII 0xa400
+#define URE_OCP_EEE_AR 0xa41a
+#define URE_OCP_EEE_DATA 0xa41c
+#define URE_OCP_PHY_STATUS 0xa420
+#define URE_OCP_POWER_CFG 0xa430
+#define URE_OCP_EEE_CFG 0xa432
+#define URE_OCP_SRAM_ADDR 0xa436
+#define URE_OCP_SRAM_DATA 0xa438
+#define URE_OCP_DOWN_SPEED 0xa442
+#define URE_OCP_EEE_ABLE 0xa5c4
+#define URE_OCP_EEE_ADV 0xa5d0
+#define URE_OCP_EEE_LPABLE 0xa5d2
+#define URE_OCP_PHY_STATE 0xa708
+#define URE_OCP_ADC_CFG 0xbc06
+
+/* SRAM Register. */
+#define URE_SRAM_LPF_CFG 0x8012
+#define URE_SRAM_10M_AMP1 0x8080
+#define URE_SRAM_10M_AMP2 0x8082
+#define URE_SRAM_IMPEDANCE 0x8084
+
+/* PLA_RCR */
+#define URE_RCR_AAP 0x00000001
+#define URE_RCR_APM 0x00000002
+#define URE_RCR_AM 0x00000004
+#define URE_RCR_AB 0x00000008
+#define URE_RCR_ACPT_ALL \
+ (URE_RCR_AAP | URE_RCR_APM | URE_RCR_AM | URE_RCR_AB)
+
+/* PLA_RXFIFO_CTRL0 */
+#define URE_RXFIFO_THR1_NORMAL 0x00080002
+#define URE_RXFIFO_THR1_OOB 0x01800003
+
+/* PLA_RXFIFO_CTRL1 */
+#define URE_RXFIFO_THR2_FULL 0x00000060
+#define URE_RXFIFO_THR2_HIGH 0x00000038
+#define URE_RXFIFO_THR2_OOB 0x0000004a
+#define URE_RXFIFO_THR2_NORMAL 0x00a0
+
+/* PLA_RXFIFO_CTRL2 */
+#define URE_RXFIFO_THR3_FULL 0x00000078
+#define URE_RXFIFO_THR3_HIGH 0x00000048
+#define URE_RXFIFO_THR3_OOB 0x0000005a
+#define URE_RXFIFO_THR3_NORMAL 0x0110
+
+/* PLA_TXFIFO_CTRL */
+#define URE_TXFIFO_THR_NORMAL 0x00400008
+#define URE_TXFIFO_THR_NORMAL2 0x01000008
+
+/* PLA_DMY_REG0 */
+#define URE_ECM_ALDPS 0x0002
+
+/* PLA_FMC */
+#define URE_FMC_FCR_MCU_EN 0x0001
+
+/* PLA_EEEP_CR */
+#define URE_EEEP_CR_EEEP_TX 0x0002
+
+/* PLA_WDT6_CTRL */
+#define URE_WDT6_SET_MODE 0x001
+
+/* PLA_TCR0 */
+#define URE_TCR0_TX_EMPTY 0x0800
+#define URE_TCR0_AUTO_FIFO 0x0080
+
+/* PLA_TCR1 */
+#define URE_VERSION_MASK 0x7cf0
+
+/* PLA_CR */
+#define URE_CR_RST 0x10
+#define URE_CR_RE 0x08
+#define URE_CR_TE 0x04
+
+/* PLA_CRWECR */
+#define URE_CRWECR_NORAML 0x00
+#define URE_CRWECR_CONFIG 0xc0
+
+/* PLA_OOB_CTRL */
+#define URE_NOW_IS_OOB 0x80
+#define URE_TXFIFO_EMPTY 0x20
+#define URE_RXFIFO_EMPTY 0x10
+#define URE_LINK_LIST_READY 0x02
+#define URE_DIS_MCU_CLROOB 0x01
+#define URE_FIFO_EMPTY (URE_TXFIFO_EMPTY | URE_RXFIFO_EMPTY)
+
+/* PLA_MISC_1 */
+#define URE_RXDY_GATED_EN 0x0008
+
+/* PLA_SFF_STS_7 */
+#define URE_RE_INIT_LL 0x8000
+#define URE_MCU_BORW_EN 0x4000
+
+/* PLA_CPCR */
+#define URE_CPCR_RX_VLAN 0x0040
+
+/* PLA_TEREDO_CFG */
+#define URE_TEREDO_SEL 0x8000
+#define URE_TEREDO_WAKE_MASK 0x7f00
+#define URE_TEREDO_RS_EVENT_MASK 0x00fe
+#define URE_OOB_TEREDO_EN 0x0001
+
+/* PAL_BDC_CR */
+#define URE_ALDPS_PROXY_MODE 0x0001
+
+/* PLA_CONFIG5 */
+#define URE_LAN_WAKE_EN 0x0002
+
+/* PLA_LED_FEATURE */
+#define URE_LED_MODE_MASK 0x0700
+
+/* PLA_PHY_PWR */
+#define URE_TX_10M_IDLE_EN 0x0080
+#define URE_PFM_PWM_SWITCH 0x0040
+
+/* PLA_MAC_PWR_CTRL */
+#define URE_D3_CLK_GATED_EN 0x00004000
+#define URE_MCU_CLK_RATIO 0x07010f07
+#define URE_MCU_CLK_RATIO_MASK 0x0f0f0f0f
+#define URE_ALDPS_SPDWN_RATIO 0x0f87
+
+/* PLA_MAC_PWR_CTRL2 */
+#define URE_EEE_SPDWN_RATIO 0x8007
+
+/* PLA_MAC_PWR_CTRL3 */
+#define URE_PKT_AVAIL_SPDWN_EN 0x0100
+#define URE_SUSPEND_SPDWN_EN 0x0004
+#define URE_U1U2_SPDWN_EN 0x0002
+#define URE_L1_SPDWN_EN 0x0001
+
+/* PLA_MAC_PWR_CTRL4 */
+#define URE_PWRSAVE_SPDWN_EN 0x1000
+#define URE_RXDV_SPDWN_EN 0x0800
+#define URE_TX10MIDLE_EN 0x0100
+#define URE_TP100_SPDWN_EN 0x0020
+#define URE_TP500_SPDWN_EN 0x0010
+#define URE_TP1000_SPDWN_EN 0x0008
+#define URE_EEE_SPDWN_EN 0x0001
+
+/* PLA_GPHY_INTR_IMR */
+#define URE_GPHY_STS_MSK 0x0001
+#define URE_SPEED_DOWN_MSK 0x0002
+#define URE_SPDWN_RXDV_MSK 0x0004
+#define URE_SPDWN_LINKCHG_MSK 0x0008
+
+/* PLA_PHYAR */
+#define URE_PHYAR_PHYDATA 0x0000ffff
+#define URE_PHYAR_BUSY 0x80000000
+
+/* PLA_EEE_CR */
+#define URE_EEE_RX_EN 0x0001
+#define URE_EEE_TX_EN 0x0002
+
+/* PLA_BOOT_CTRL */
+#define URE_AUTOLOAD_DONE 0x0002
+
+/* USB_USB2PHY */
+#define URE_USB2PHY_SUSPEND 0x0001
+#define URE_USB2PHY_L1 0x0002
+
+/* USB_SSPHYLINK2 */
+#define URE_PWD_DN_SCALE_MASK 0x3ffe
+#define URE_PWD_DN_SCALE(x) ((x) << 1)
+
+/* USB_CSR_DUMMY1 */
+#define URE_DYNAMIC_BURST 0x0001
+
+/* USB_CSR_DUMMY2 */
+#define URE_EP4_FULL_FC 0x0001
+
+/* USB_DEV_STAT */
+#define URE_STAT_SPEED_MASK 0x0006
+#define URE_STAT_SPEED_HIGH 0x0000
+#define URE_STAT_SPEED_FULL 0x0001
+
+/* USB_TX_AGG */
+#define URE_TX_AGG_MAX_THRESHOLD 0x03
+
+/* USB_RX_BUF_TH */
+#define URE_RX_THR_SUPER 0x0c350180
+#define URE_RX_THR_HIGH 0x7a120180
+#define URE_RX_THR_SLOW 0xffff0180
+
+/* USB_TX_DMA */
+#define URE_TEST_MODE_DISABLE 0x00000001
+#define URE_TX_SIZE_ADJUST1 0x00000100
+
+/* USB_UPS_CTRL */
+#define URE_POWER_CUT 0x0100
+
+/* USB_PM_CTRL_STATUS */
+#define URE_RESUME_INDICATE 0x0001
+
+/* USB_USB_CTRL */
+#define URE_RX_AGG_DISABLE 0x0010
+#define URE_RX_ZERO_EN 0x0080
+
+/* USB_U2P3_CTRL */
+#define URE_U2P3_ENABLE 0x0001
+
+/* USB_POWER_CUT */
+#define URE_PWR_EN 0x0001
+#define URE_PHASE2_EN 0x0008
+
+/* USB_MISC_0 */
+#define URE_PCUT_STATUS 0x0001
+
+/* USB_RX_EARLY_TIMEOUT */
+#define URE_COALESCE_SUPER 85000U
+#define URE_COALESCE_HIGH 250000U
+#define URE_COALESCE_SLOW 524280U
+
+/* USB_WDT11_CTRL */
+#define URE_TIMER11_EN 0x0001
+
+/* USB_LPM_CTRL */
+#define URE_FIFO_EMPTY_1FB 0x30
+#define URE_LPM_TIMER_MASK 0x0c
+#define URE_LPM_TIMER_500MS 0x04
+#define URE_LPM_TIMER_500US 0x0c
+#define URE_ROK_EXIT_LPM 0x02
+
+/* USB_AFE_CTRL2 */
+#define URE_SEN_VAL_MASK 0xf800
+#define URE_SEN_VAL_NORMAL 0xa000
+#define URE_SEL_RXIDLE 0x0100
+
+/* OCP_ALDPS_CONFIG */
+#define URE_ENPWRSAVE 0x8000
+#define URE_ENPDNPS 0x0200
+#define URE_LINKENA 0x0100
+#define URE_DIS_SDSAVE 0x0010
+
+/* OCP_PHY_STATUS */
+#define URE_PHY_STAT_MASK 0x0007
+#define URE_PHY_STAT_LAN_ON 3
+#define URE_PHY_STAT_PWRDN 5
+
+/* OCP_POWER_CFG */
+#define URE_EEE_CLKDIV_EN 0x8000
+#define URE_EN_ALDPS 0x0004
+#define URE_EN_10M_PLLOFF 0x0001
+
+/* OCP_EEE_CFG */
+#define URE_CTAP_SHORT_EN 0x0040
+#define URE_EEE10_EN 0x0010
+
+/* OCP_DOWN_SPEED */
+#define URE_EN_10M_BGOFF 0x0080
+
+/* OCP_PHY_STATE */
+#define URE_TXDIS_STATE 0x01
+#define URE_ABD_STATE 0x02
+
+/* OCP_ADC_CFG */
+#define URE_CKADSEL_L 0x0100
+#define URE_ADC_EN 0x0080
+#define URE_EN_EMI_L 0x0040
+
+#define URE_MCU_TYPE_PLA 0x0100
+#define URE_MCU_TYPE_USB 0x0000
+
+#define GET_MII(sc) uether_getmii(&(sc)->sc_ue)
+
+struct ure_intrpkt {
+ uint8_t ure_tsr;
+ uint8_t ure_rsr;
+ uint8_t ure_gep_msr;
+ uint8_t ure_waksr;
+ uint8_t ure_txok_cnt;
+ uint8_t ure_rxlost_cnt;
+ uint8_t ure_crcerr_cnt;
+ uint8_t ure_col_cnt;
+} __packed;
+
+struct ure_rxpkt {
+ uint32_t ure_pktlen;
+#define URE_RXPKT_LEN_MASK 0x7fff
+ uint32_t ure_rsvd0;
+ uint32_t ure_rsvd1;
+ uint32_t ure_rsvd2;
+ uint32_t ure_rsvd3;
+ uint32_t ure_rsvd4;
+} __packed;
+
+struct ure_txpkt {
+ uint32_t ure_pktlen;
+#define URE_TKPKT_TX_FS (1 << 31)
+#define URE_TKPKT_TX_LS (1 << 30)
+#define URE_TXPKT_LEN_MASK 0xffff
+ uint32_t ure_rsvd0;
+} __packed;
+
+enum {
+ URE_BULK_DT_WR,
+ URE_BULK_DT_RD,
+ URE_N_TRANSFER,
+};
+
+struct ure_softc {
+ struct usb_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb_xfer *sc_xfer[URE_N_TRANSFER];
+
+ int sc_phyno;
+
+ u_int sc_flags;
+#define URE_FLAG_LINK 0x0001
+#define URE_FLAG_8152 0x1000 /* RTL8152 */
+
+ u_int sc_chip;
+#define URE_CHIP_VER_4C00 0x01
+#define URE_CHIP_VER_4C10 0x02
+#define URE_CHIP_VER_5C00 0x04
+#define URE_CHIP_VER_5C10 0x08
+#define URE_CHIP_VER_5C20 0x10
+#define URE_CHIP_VER_5C30 0x20
+};
+
+#define URE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define URE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define URE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/freebsd/sys/dev/usb/net/ruephy.c b/freebsd/sys/dev/usb/net/ruephy.c
new file mode 100644
index 00000000..d6db6a04
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/ruephy.c
@@ -0,0 +1,222 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * driver for RealTek RTL8150 internal PHY
+ */
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/bus.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <rtems/bsd/local/miidevs.h>
+
+#include <dev/usb/net/ruephyreg.h>
+
+#include <rtems/bsd/local/miibus_if.h>
+
+static int ruephy_probe(device_t);
+static int ruephy_attach(device_t);
+
+static device_method_t ruephy_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ruephy_probe),
+ DEVMETHOD(device_attach, ruephy_attach),
+ DEVMETHOD(device_detach, mii_phy_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD_END
+};
+
+static devclass_t ruephy_devclass;
+
+static driver_t ruephy_driver = {
+ .name = "ruephy",
+ .methods = ruephy_methods,
+ .size = sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(ruephy, miibus, ruephy_driver, ruephy_devclass, 0, 0);
+
+static int ruephy_service(struct mii_softc *, struct mii_data *, int);
+static void ruephy_reset(struct mii_softc *);
+static void ruephy_status(struct mii_softc *);
+
+/*
+ * The RealTek RTL8150 internal PHY doesn't have vendor/device ID
+ * registers; rue(4) fakes up a return value of all zeros.
+ */
+static const struct mii_phydesc ruephys[] = {
+ { 0, 0, "RealTek RTL8150 internal media interface" },
+ MII_PHY_END
+};
+
+static const struct mii_phy_funcs ruephy_funcs = {
+ ruephy_service,
+ ruephy_status,
+ ruephy_reset
+};
+
+static int
+ruephy_probe(device_t dev)
+{
+
+ if (strcmp(device_get_name(device_get_parent(device_get_parent(dev))),
+ "rue") == 0)
+ return (mii_phy_dev_probe(dev, ruephys, BUS_PROBE_DEFAULT));
+ return (ENXIO);
+}
+
+static int
+ruephy_attach(device_t dev)
+{
+
+ mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE,
+ &ruephy_funcs, 1);
+ return (0);
+}
+
+static int
+ruephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
+{
+ struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
+ int reg;
+
+ switch (cmd) {
+ case MII_POLLSTAT:
+ break;
+
+ case MII_MEDIACHG:
+ mii_phy_setmedia(sc);
+ break;
+
+ case MII_TICK:
+ /*
+ * Only used for autonegotiation.
+ */
+ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
+ break;
+
+ /*
+ * Check to see if we have link. If we do, we don't
+ * need to restart the autonegotiation process. Read
+ * the MSR twice in case it's latched.
+ */
+ reg = PHY_READ(sc, RUEPHY_MII_MSR) |
+ PHY_READ(sc, RUEPHY_MII_MSR);
+ if (reg & RUEPHY_MSR_LINK)
+ break;
+
+ /* Only retry autonegotiation every mii_anegticks seconds. */
+ if (sc->mii_ticks <= sc->mii_anegticks)
+ break;
+
+ sc->mii_ticks = 0;
+ PHY_RESET(sc);
+ if (mii_phy_auto(sc) == EJUSTRETURN)
+ return (0);
+ break;
+ }
+
+ /* Update the media status. */
+ PHY_STATUS(sc);
+
+ /* Callback if something changed. */
+ mii_phy_update(sc, cmd);
+
+ return (0);
+}
+
+static void
+ruephy_reset(struct mii_softc *sc)
+{
+
+ mii_phy_reset(sc);
+
+ /*
+ * XXX RealTek RTL8150 PHY doesn't set the BMCR properly after
+ * XXX reset, which breaks autonegotiation.
+ */
+ PHY_WRITE(sc, MII_BMCR, (BMCR_S100 | BMCR_AUTOEN | BMCR_FDX));
+}
+
+static void
+ruephy_status(struct mii_softc *phy)
+{
+ struct mii_data *mii = phy->mii_pdata;
+ struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
+ int bmsr, bmcr, msr;
+
+ mii->mii_media_status = IFM_AVALID;
+ mii->mii_media_active = IFM_ETHER;
+
+ msr = PHY_READ(phy, RUEPHY_MII_MSR) | PHY_READ(phy, RUEPHY_MII_MSR);
+ if (msr & RUEPHY_MSR_LINK)
+ mii->mii_media_status |= IFM_ACTIVE;
+
+ bmcr = PHY_READ(phy, MII_BMCR);
+ if (bmcr & BMCR_ISO) {
+ mii->mii_media_active |= IFM_NONE;
+ mii->mii_media_status = 0;
+ return;
+ }
+
+ bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
+ if (bmcr & BMCR_AUTOEN) {
+ if ((bmsr & BMSR_ACOMP) == 0) {
+ /* Erg, still trying, I guess... */
+ mii->mii_media_active |= IFM_NONE;
+ return;
+ }
+
+ if (msr & RUEPHY_MSR_SPEED100)
+ mii->mii_media_active |= IFM_100_TX;
+ else
+ mii->mii_media_active |= IFM_10_T;
+
+ if (msr & RUEPHY_MSR_DUPLEX)
+ mii->mii_media_active |=
+ IFM_FDX | mii_phy_flowstatus(phy);
+ else
+ mii->mii_media_active |= IFM_HDX;
+ } else
+ mii->mii_media_active = ife->ifm_media;
+}
diff --git a/freebsd/sys/dev/usb/net/ruephyreg.h b/freebsd/sys/dev/usb/net/ruephyreg.h
new file mode 100644
index 00000000..01d3cc17
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/ruephyreg.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _RUEPHYREG_H_
+#define _RUEPHYREG_H_
+
+#define RUEPHY_MII_MSR 0x0137 /* B, R/W */
+#define RUEPHY_MSR_RXFCE 0x40
+#define RUEPHY_MSR_DUPLEX 0x10
+#define RUEPHY_MSR_SPEED100 0x08
+#define RUEPHY_MSR_LINK 0x04
+
+#endif /* _RUEPHYREG_H_ */
diff --git a/freebsd/sys/dev/usb/net/usb_ethernet.c b/freebsd/sys/dev/usb/net/usb_ethernet.c
new file mode 100644
index 00000000..7b9bfa19
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/usb_ethernet.c
@@ -0,0 +1,651 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/if_vlan_var.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_process.h>
+#include <dev/usb/net/usb_ethernet.h>
+
+static 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 usb_proc_callback_t ue_attach_post_task;
+static usb_proc_callback_t ue_promisc_task;
+static usb_proc_callback_t ue_setmulti_task;
+static usb_proc_callback_t ue_ifmedia_task;
+static usb_proc_callback_t ue_tick_task;
+static usb_proc_callback_t ue_start_task;
+static usb_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
+uether_pause(struct usb_ether *ue, unsigned int _ticks)
+{
+ if (usb_proc_is_gone(&ue->ue_tq)) {
+ /* nothing to do */
+ return (1);
+ }
+ usb_pause_mtx(ue->ue_mtx, _ticks);
+ return (0);
+}
+
+static void
+ue_queue_command(struct usb_ether *ue,
+ usb_proc_callback_t *fn,
+ struct usb_proc_msg *t0, struct usb_proc_msg *t1)
+{
+ struct usb_ether_cfg_task *task;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ if (usb_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 usb_ether_cfg_task *)
+ usb_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))
+ usb_proc_mwait(&ue->ue_tq, t0, t1);
+}
+
+struct ifnet *
+uether_getifp(struct usb_ether *ue)
+{
+ return (ue->ue_ifp);
+}
+
+struct mii_data *
+uether_getmii(struct usb_ether *ue)
+{
+ return (device_get_softc(ue->ue_miibus));
+}
+
+void *
+uether_getsc(struct usb_ether *ue)
+{
+ return (ue->ue_sc);
+}
+
+static int
+ue_sysctl_parent(SYSCTL_HANDLER_ARGS)
+{
+ struct usb_ether *ue = arg1;
+ const char *name;
+
+ name = device_get_nameunit(ue->ue_dev);
+ return SYSCTL_OUT_STR(req, name);
+}
+
+int
+uether_ifattach(struct usb_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 = usb_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 usb_proc_msg *_task)
+{
+ struct usb_ether_cfg_task *task =
+ (struct usb_ether_cfg_task *)_task;
+ struct usb_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);
+ usb_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0);
+ sysctl_ctx_init(&ue->ue_sysctl_ctx);
+
+ error = 0;
+ CURVNET_SET_QUIET(vnet0);
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(ue->ue_dev, "could not allocate ifnet\n");
+ goto fail;
+ }
+
+ ifp->if_softc = ue;
+ if_initname(ifp, "ue", ue->ue_unit);
+ if (ue->ue_methods->ue_attach_post_sub != NULL) {
+ ue->ue_ifp = ifp;
+ error = ue->ue_methods->ue_attach_post_sub(ue);
+ } else {
+ 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 = uether_ioctl;
+ ifp->if_start = ue_start;
+ ifp->if_init = ue_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+ ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+ 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) {
+ /* device_xxx() depends on this */
+ mtx_lock(&Giant);
+ error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
+ ue_ifmedia_upd, ue->ue_methods->ue_mii_sts,
+ BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+ mtx_unlock(&Giant);
+ }
+ }
+
+ if (error) {
+ device_printf(ue->ue_dev, "attaching PHYs failed\n");
+ goto fail;
+ }
+
+ if_printf(ifp, "<USB Ethernet> on %s\n", device_get_nameunit(ue->ue_dev));
+ ether_ifattach(ifp, ue->ue_eaddr);
+ /* Tell upper layer we support VLAN oversized frames. */
+ if (ifp->if_capabilities & IFCAP_VLAN_MTU)
+ ifp->if_hdrlen = sizeof(struct ether_vlan_header);
+
+ CURVNET_RESTORE();
+
+ 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", CTLTYPE_STRING | CTLFLAG_RD, ue, 0,
+ ue_sysctl_parent, "A", "parent device");
+
+ UE_LOCK(ue);
+ return;
+
+fail:
+ CURVNET_RESTORE();
+ 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
+uether_ifdetach(struct usb_ether *ue)
+{
+ struct ifnet *ifp;
+
+ /* wait for any post attach or other command to complete */
+ usb_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 */
+ usb_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 */
+ usb_proc_free(&ue->ue_tq);
+}
+
+uint8_t
+uether_is_gone(struct usb_ether *ue)
+{
+ return (usb_proc_is_gone(&ue->ue_tq));
+}
+
+void
+uether_init(void *arg)
+{
+
+ ue_init(arg);
+}
+
+static void
+ue_init(void *arg)
+{
+ struct usb_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 usb_proc_msg *_task)
+{
+ struct usb_ether_cfg_task *task =
+ (struct usb_ether_cfg_task *)_task;
+ struct usb_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)
+ usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue);
+}
+
+static void
+ue_stop_task(struct usb_proc_msg *_task)
+{
+ struct usb_ether_cfg_task *task =
+ (struct usb_ether_cfg_task *)_task;
+ struct usb_ether *ue = task->ue;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ usb_callout_stop(&ue->ue_watchdog);
+
+ ue->ue_methods->ue_stop(ue);
+}
+
+void
+uether_start(struct ifnet *ifp)
+{
+
+ ue_start(ifp);
+}
+
+static void
+ue_start(struct ifnet *ifp)
+{
+ struct usb_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 usb_proc_msg *_task)
+{
+ struct usb_ether_cfg_task *task =
+ (struct usb_ether_cfg_task *)_task;
+ struct usb_ether *ue = task->ue;
+
+ ue->ue_methods->ue_setpromisc(ue);
+}
+
+static void
+ue_setmulti_task(struct usb_proc_msg *_task)
+{
+ struct usb_ether_cfg_task *task =
+ (struct usb_ether_cfg_task *)_task;
+ struct usb_ether *ue = task->ue;
+
+ ue->ue_methods->ue_setmulti(ue);
+}
+
+int
+uether_ifmedia_upd(struct ifnet *ifp)
+{
+
+ return (ue_ifmedia_upd(ifp));
+}
+
+static int
+ue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct usb_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 usb_proc_msg *_task)
+{
+ struct usb_ether_cfg_task *task =
+ (struct usb_ether_cfg_task *)_task;
+ struct usb_ether *ue = task->ue;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ ue->ue_methods->ue_mii_upd(ifp);
+}
+
+static void
+ue_watchdog(void *arg)
+{
+ struct usb_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);
+
+ usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue);
+}
+
+static void
+ue_tick_task(struct usb_proc_msg *_task)
+{
+ struct usb_ether_cfg_task *task =
+ (struct usb_ether_cfg_task *)_task;
+ struct usb_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
+uether_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct usb_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
+uether_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 uether_mod = {
+ "uether",
+ uether_modevent,
+ 0
+};
+
+struct mbuf *
+uether_newbuf(void)
+{
+ struct mbuf *m_new;
+
+ m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m_new == NULL)
+ return (NULL);
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+
+ m_adj(m_new, ETHER_ALIGN);
+ return (m_new);
+}
+
+int
+uether_rxmbuf(struct usb_ether *ue, struct mbuf *m,
+ unsigned int len)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ /* finalize mbuf */
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ 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
+uether_rxbuf(struct usb_ether *ue, struct usb_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 - ETHER_ALIGN)
+ return (1);
+
+ m = uether_newbuf();
+ if (m == NULL) {
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ return (ENOMEM);
+ }
+
+ usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
+
+ /* finalize mbuf */
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ 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
+uether_rxflush(struct usb_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);
+ }
+}
+
+/*
+ * USB net drivers are run by DRIVER_MODULE() thus SI_SUB_DRIVERS,
+ * SI_ORDER_MIDDLE. Run uether after that.
+ */
+DECLARE_MODULE(uether, uether_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
+MODULE_VERSION(uether, 1);
diff --git a/freebsd/sys/dev/usb/net/usb_ethernet.h b/freebsd/sys/dev/usb/net/usb_ethernet.h
new file mode 100644
index 00000000..09a48e27
--- /dev/null
+++ b/freebsd/sys/dev/usb/net/usb_ethernet.h
@@ -0,0 +1,127 @@
+/* $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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _USB_ETHERNET_H_
+#define _USB_ETHERNET_H_
+
+#include <rtems/bsd/local/opt_inet.h>
+
+#include <rtems/bsd/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 <rtems/bsd/local/miibus_if.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+struct usb_ether;
+struct usb_device_request;
+
+typedef void (uether_fn_t)(struct usb_ether *);
+
+struct usb_ether_methods {
+ uether_fn_t *ue_attach_post;
+ uether_fn_t *ue_start;
+ uether_fn_t *ue_init;
+ uether_fn_t *ue_stop;
+ uether_fn_t *ue_setmulti;
+ uether_fn_t *ue_setpromisc;
+ uether_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);
+ int (*ue_attach_post_sub)(struct usb_ether *);
+};
+
+struct usb_ether_cfg_task {
+ struct usb_proc_msg hdr;
+ struct usb_ether *ue;
+};
+
+struct usb_ether {
+ /* NOTE: the "ue_ifp" pointer must be first --hps */
+ struct ifnet *ue_ifp;
+ struct mtx *ue_mtx;
+ const struct usb_ether_methods *ue_methods;
+ struct sysctl_oid *ue_sysctl_oid;
+ void *ue_sc;
+ struct usb_device *ue_udev; /* used by uether_do_request() */
+ device_t ue_dev;
+ device_t ue_miibus;
+
+ struct usb_process ue_tq;
+ struct sysctl_ctx_list ue_sysctl_ctx;
+ struct ifqueue ue_rxq;
+ struct usb_callout ue_watchdog;
+ struct usb_ether_cfg_task ue_sync_task[2];
+ struct usb_ether_cfg_task ue_media_task[2];
+ struct usb_ether_cfg_task ue_multi_task[2];
+ struct usb_ether_cfg_task ue_promisc_task[2];
+ struct usb_ether_cfg_task ue_tick_task[2];
+
+ int ue_unit;
+
+ /* ethernet address from eeprom */
+ uint8_t ue_eaddr[ETHER_ADDR_LEN];
+};
+
+#define uether_do_request(ue,req,data,timo) \
+ usbd_do_request_proc((ue)->ue_udev,&(ue)->ue_tq,req,data,0,NULL,timo)
+
+uint8_t uether_pause(struct usb_ether *, unsigned int);
+struct ifnet *uether_getifp(struct usb_ether *);
+struct mii_data *uether_getmii(struct usb_ether *);
+void *uether_getsc(struct usb_ether *);
+int uether_ifattach(struct usb_ether *);
+void uether_ifdetach(struct usb_ether *);
+int uether_ifmedia_upd(struct ifnet *);
+void uether_init(void *);
+int uether_ioctl(struct ifnet *, u_long, caddr_t);
+struct mbuf *uether_newbuf(void);
+int uether_rxmbuf(struct usb_ether *, struct mbuf *,
+ unsigned int);
+int uether_rxbuf(struct usb_ether *,
+ struct usb_page_cache *,
+ unsigned int, unsigned int);
+void uether_rxflush(struct usb_ether *);
+uint8_t uether_is_gone(struct usb_ether *);
+void uether_start(struct ifnet *);
+#endif /* _USB_ETHERNET_H_ */
diff --git a/freebsd/sys/dev/usb/wlan/if_rsu.c b/freebsd/sys/dev/usb/wlan/if_rsu.c
new file mode 100644
index 00000000..b429a082
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_rsu.c
@@ -0,0 +1,3796 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $OpenBSD: if_rsu.c,v 1.17 2013/04/15 09:23:01 mglocker Exp $ */
+
+/*-
+ * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU.
+ *
+ * TODO:
+ * o tx a-mpdu
+ * o hostap / ibss / mesh
+ * o power-save operation
+ */
+
+#include <rtems/bsd/local/opt_wlan.h>
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/endian.h>
+#include <sys/sockio.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/firmware.h>
+#include <sys/module.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#include <dev/usb/wlan/if_rsureg.h>
+
+#ifdef USB_DEBUG
+static int rsu_debug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu");
+SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RWTUN, &rsu_debug, 0,
+ "Debug level");
+#define RSU_DPRINTF(_sc, _flg, ...) \
+ do \
+ if (((_flg) == (RSU_DEBUG_ANY)) || (rsu_debug & (_flg))) \
+ device_printf((_sc)->sc_dev, __VA_ARGS__); \
+ while (0)
+#else
+#define RSU_DPRINTF(_sc, _flg, ...)
+#endif
+
+static int rsu_enable_11n = 1;
+TUNABLE_INT("hw.usb.rsu.enable_11n", &rsu_enable_11n);
+
+#define RSU_DEBUG_ANY 0xffffffff
+#define RSU_DEBUG_TX 0x00000001
+#define RSU_DEBUG_RX 0x00000002
+#define RSU_DEBUG_RESET 0x00000004
+#define RSU_DEBUG_CALIB 0x00000008
+#define RSU_DEBUG_STATE 0x00000010
+#define RSU_DEBUG_SCAN 0x00000020
+#define RSU_DEBUG_FWCMD 0x00000040
+#define RSU_DEBUG_TXDONE 0x00000080
+#define RSU_DEBUG_FW 0x00000100
+#define RSU_DEBUG_FWDBG 0x00000200
+#define RSU_DEBUG_AMPDU 0x00000400
+#define RSU_DEBUG_KEY 0x00000800
+#define RSU_DEBUG_USB 0x00001000
+
+static const STRUCT_USB_HOST_ID rsu_devs[] = {
+#define RSU_HT_NOT_SUPPORTED 0
+#define RSU_HT_SUPPORTED 1
+#define RSU_DEV_HT(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
+ RSU_HT_SUPPORTED) }
+#define RSU_DEV(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
+ RSU_HT_NOT_SUPPORTED) }
+ RSU_DEV(ASUS, RTL8192SU),
+ RSU_DEV(AZUREWAVE, RTL8192SU_4),
+ RSU_DEV_HT(ACCTON, RTL8192SU),
+ RSU_DEV_HT(ASUS, USBN10),
+ RSU_DEV_HT(AZUREWAVE, RTL8192SU_1),
+ RSU_DEV_HT(AZUREWAVE, RTL8192SU_2),
+ RSU_DEV_HT(AZUREWAVE, RTL8192SU_3),
+ RSU_DEV_HT(AZUREWAVE, RTL8192SU_5),
+ RSU_DEV_HT(BELKIN, RTL8192SU_1),
+ RSU_DEV_HT(BELKIN, RTL8192SU_2),
+ RSU_DEV_HT(BELKIN, RTL8192SU_3),
+ RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_1),
+ RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_2),
+ RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_3),
+ RSU_DEV_HT(COREGA, RTL8192SU),
+ RSU_DEV_HT(DLINK2, DWA131A1),
+ RSU_DEV_HT(DLINK2, RTL8192SU_1),
+ RSU_DEV_HT(DLINK2, RTL8192SU_2),
+ RSU_DEV_HT(EDIMAX, RTL8192SU_1),
+ RSU_DEV_HT(EDIMAX, RTL8192SU_2),
+ RSU_DEV_HT(EDIMAX, EW7622UMN),
+ RSU_DEV_HT(GUILLEMOT, HWGUN54),
+ RSU_DEV_HT(GUILLEMOT, HWNUM300),
+ RSU_DEV_HT(HAWKING, RTL8192SU_1),
+ RSU_DEV_HT(HAWKING, RTL8192SU_2),
+ RSU_DEV_HT(PLANEX2, GWUSNANO),
+ RSU_DEV_HT(REALTEK, RTL8171),
+ RSU_DEV_HT(REALTEK, RTL8172),
+ RSU_DEV_HT(REALTEK, RTL8173),
+ RSU_DEV_HT(REALTEK, RTL8174),
+ RSU_DEV_HT(REALTEK, RTL8192SU),
+ RSU_DEV_HT(REALTEK, RTL8712),
+ RSU_DEV_HT(REALTEK, RTL8713),
+ RSU_DEV_HT(SENAO, RTL8192SU_1),
+ RSU_DEV_HT(SENAO, RTL8192SU_2),
+ RSU_DEV_HT(SITECOMEU, WL349V1),
+ RSU_DEV_HT(SITECOMEU, WL353),
+ RSU_DEV_HT(SWEEX2, LW154),
+ RSU_DEV_HT(TRENDNET, TEW646UBH),
+#undef RSU_DEV_HT
+#undef RSU_DEV
+};
+
+static device_probe_t rsu_match;
+static device_attach_t rsu_attach;
+static device_detach_t rsu_detach;
+static usb_callback_t rsu_bulk_tx_callback_be_bk;
+static usb_callback_t rsu_bulk_tx_callback_vi_vo;
+static usb_callback_t rsu_bulk_tx_callback_h2c;
+static usb_callback_t rsu_bulk_rx_callback;
+static usb_error_t rsu_do_request(struct rsu_softc *,
+ struct usb_device_request *, void *);
+static struct ieee80211vap *
+ rsu_vap_create(struct ieee80211com *, const char name[],
+ int, enum ieee80211_opmode, int, const uint8_t bssid[],
+ const uint8_t mac[]);
+static void rsu_vap_delete(struct ieee80211vap *);
+static void rsu_scan_start(struct ieee80211com *);
+static void rsu_scan_end(struct ieee80211com *);
+static void rsu_getradiocaps(struct ieee80211com *, int, int *,
+ struct ieee80211_channel[]);
+static void rsu_set_channel(struct ieee80211com *);
+static void rsu_scan_curchan(struct ieee80211_scan_state *, unsigned long);
+static void rsu_scan_mindwell(struct ieee80211_scan_state *);
+static void rsu_update_promisc(struct ieee80211com *);
+static uint8_t rsu_get_multi_pos(const uint8_t[]);
+static void rsu_set_multi(struct rsu_softc *);
+static void rsu_update_mcast(struct ieee80211com *);
+static int rsu_alloc_rx_list(struct rsu_softc *);
+static void rsu_free_rx_list(struct rsu_softc *);
+static int rsu_alloc_tx_list(struct rsu_softc *);
+static void rsu_free_tx_list(struct rsu_softc *);
+static void rsu_free_list(struct rsu_softc *, struct rsu_data [], int);
+static struct rsu_data *_rsu_getbuf(struct rsu_softc *);
+static struct rsu_data *rsu_getbuf(struct rsu_softc *);
+static void rsu_freebuf(struct rsu_softc *, struct rsu_data *);
+static int rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *,
+ int);
+static void rsu_write_1(struct rsu_softc *, uint16_t, uint8_t);
+static void rsu_write_2(struct rsu_softc *, uint16_t, uint16_t);
+static void rsu_write_4(struct rsu_softc *, uint16_t, uint32_t);
+static int rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *,
+ int);
+static uint8_t rsu_read_1(struct rsu_softc *, uint16_t);
+static uint16_t rsu_read_2(struct rsu_softc *, uint16_t);
+static uint32_t rsu_read_4(struct rsu_softc *, uint16_t);
+static int rsu_fw_iocmd(struct rsu_softc *, uint32_t);
+static uint8_t rsu_efuse_read_1(struct rsu_softc *, uint16_t);
+static int rsu_read_rom(struct rsu_softc *);
+static int rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int);
+static void rsu_calib_task(void *, int);
+static void rsu_tx_task(void *, int);
+static void rsu_set_led(struct rsu_softc *, int);
+static int rsu_monitor_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static int rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int rsu_key_alloc(struct ieee80211vap *, struct ieee80211_key *,
+ ieee80211_keyix *, ieee80211_keyix *);
+static int rsu_process_key(struct ieee80211vap *,
+ const struct ieee80211_key *, int);
+static int rsu_key_set(struct ieee80211vap *,
+ const struct ieee80211_key *);
+static int rsu_key_delete(struct ieee80211vap *,
+ const struct ieee80211_key *);
+static int rsu_cam_read(struct rsu_softc *, uint8_t, uint32_t *);
+static void rsu_cam_write(struct rsu_softc *, uint8_t, uint32_t);
+static int rsu_key_check(struct rsu_softc *, ieee80211_keyix, int);
+static uint8_t rsu_crypto_mode(struct rsu_softc *, u_int, int);
+static int rsu_set_key_group(struct rsu_softc *,
+ const struct ieee80211_key *);
+static int rsu_set_key_pair(struct rsu_softc *,
+ const struct ieee80211_key *);
+static int rsu_reinit_static_keys(struct rsu_softc *);
+static int rsu_delete_key(struct rsu_softc *sc, ieee80211_keyix);
+static void rsu_delete_key_pair_cb(void *, int);
+static int rsu_site_survey(struct rsu_softc *,
+ struct ieee80211_scan_ssid *);
+static int rsu_join_bss(struct rsu_softc *, struct ieee80211_node *);
+static int rsu_disconnect(struct rsu_softc *);
+static int rsu_hwrssi_to_rssi(struct rsu_softc *, int hw_rssi);
+static void rsu_event_survey(struct rsu_softc *, uint8_t *, int);
+static void rsu_event_join_bss(struct rsu_softc *, uint8_t *, int);
+static void rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int);
+static void rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int);
+static int8_t rsu_get_rssi(struct rsu_softc *, int, void *);
+static struct mbuf * rsu_rx_copy_to_mbuf(struct rsu_softc *,
+ struct r92s_rx_stat *, int);
+static uint32_t rsu_get_tsf_low(struct rsu_softc *);
+static uint32_t rsu_get_tsf_high(struct rsu_softc *);
+static struct ieee80211_node * rsu_rx_frame(struct rsu_softc *, struct mbuf *);
+static struct mbuf * rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int);
+static struct mbuf *
+ rsu_rxeof(struct usb_xfer *, struct rsu_data *);
+static void rsu_txeof(struct usb_xfer *, struct rsu_data *);
+static int rsu_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void rsu_rxfilter_init(struct rsu_softc *);
+static void rsu_rxfilter_set(struct rsu_softc *, uint32_t, uint32_t);
+static void rsu_rxfilter_refresh(struct rsu_softc *);
+static int rsu_init(struct rsu_softc *);
+static int rsu_tx_start(struct rsu_softc *, struct ieee80211_node *,
+ struct mbuf *, struct rsu_data *);
+static int rsu_transmit(struct ieee80211com *, struct mbuf *);
+static void rsu_start(struct rsu_softc *);
+static void _rsu_start(struct rsu_softc *);
+static int rsu_ioctl_net(struct ieee80211com *, u_long, void *);
+static void rsu_parent(struct ieee80211com *);
+static void rsu_stop(struct rsu_softc *);
+static void rsu_ms_delay(struct rsu_softc *, int);
+
+static device_method_t rsu_methods[] = {
+ DEVMETHOD(device_probe, rsu_match),
+ DEVMETHOD(device_attach, rsu_attach),
+ DEVMETHOD(device_detach, rsu_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t rsu_driver = {
+ .name = "rsu",
+ .methods = rsu_methods,
+ .size = sizeof(struct rsu_softc)
+};
+
+static devclass_t rsu_devclass;
+
+DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0);
+MODULE_DEPEND(rsu, wlan, 1, 1, 1);
+MODULE_DEPEND(rsu, usb, 1, 1, 1);
+MODULE_DEPEND(rsu, firmware, 1, 1, 1);
+MODULE_VERSION(rsu, 1);
+USB_PNP_HOST_INFO(rsu_devs);
+
+static const uint8_t rsu_chan_2ghz[] =
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+
+static uint8_t rsu_wme_ac_xfer_map[4] = {
+ [WME_AC_BE] = RSU_BULK_TX_BE_BK,
+ [WME_AC_BK] = RSU_BULK_TX_BE_BK,
+ [WME_AC_VI] = RSU_BULK_TX_VI_VO,
+ [WME_AC_VO] = RSU_BULK_TX_VI_VO,
+};
+
+/* XXX hard-coded */
+#define RSU_H2C_ENDPOINT 3
+
+static const struct usb_config rsu_config[RSU_N_TRANSFER] = {
+ [RSU_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = RSU_RXBUFSZ,
+ .flags = {
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = rsu_bulk_rx_callback
+ },
+ [RSU_BULK_TX_BE_BK] = {
+ .type = UE_BULK,
+ .endpoint = 0x06,
+ .direction = UE_DIR_OUT,
+ .bufsize = RSU_TXBUFSZ,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .force_short_xfer = 1
+ },
+ .callback = rsu_bulk_tx_callback_be_bk,
+ .timeout = RSU_TX_TIMEOUT
+ },
+ [RSU_BULK_TX_VI_VO] = {
+ .type = UE_BULK,
+ .endpoint = 0x04,
+ .direction = UE_DIR_OUT,
+ .bufsize = RSU_TXBUFSZ,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .force_short_xfer = 1
+ },
+ .callback = rsu_bulk_tx_callback_vi_vo,
+ .timeout = RSU_TX_TIMEOUT
+ },
+ [RSU_BULK_TX_H2C] = {
+ .type = UE_BULK,
+ .endpoint = 0x0d,
+ .direction = UE_DIR_OUT,
+ .bufsize = RSU_TXBUFSZ,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = rsu_bulk_tx_callback_h2c,
+ .timeout = RSU_TX_TIMEOUT
+ },
+};
+
+static int
+rsu_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb_mode != USB_MODE_HOST ||
+ uaa->info.bIfaceIndex != 0 ||
+ uaa->info.bConfigIndex != 0)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa));
+}
+
+static int
+rsu_send_mgmt(struct ieee80211_node *ni, int type, int arg)
+{
+
+ return (ENOTSUP);
+}
+
+static void
+rsu_update_chw(struct ieee80211com *ic)
+{
+
+}
+
+/*
+ * notification from net80211 that it'd like to do A-MPDU on the given TID.
+ *
+ * Note: this actually hangs traffic at the present moment, so don't use it.
+ * The firmware debug does indiciate it's sending and establishing a TX AMPDU
+ * session, but then no traffic flows.
+ */
+static int
+rsu_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+#if 0
+ struct rsu_softc *sc = ni->ni_ic->ic_softc;
+ struct r92s_add_ba_req req;
+
+ /* Don't enable if it's requested or running */
+ if (IEEE80211_AMPDU_REQUESTED(tap))
+ return (0);
+ if (IEEE80211_AMPDU_RUNNING(tap))
+ return (0);
+
+ /* We've decided to send addba; so send it */
+ req.tid = htole32(tap->txa_tid);
+
+ /* Attempt net80211 state */
+ if (ieee80211_ampdu_tx_request_ext(ni, tap->txa_tid) != 1)
+ return (0);
+
+ /* Send the firmware command */
+ RSU_DPRINTF(sc, RSU_DEBUG_AMPDU, "%s: establishing AMPDU TX for TID %d\n",
+ __func__,
+ tap->txa_tid);
+
+ RSU_LOCK(sc);
+ if (rsu_fw_cmd(sc, R92S_CMD_ADDBA_REQ, &req, sizeof(req)) != 1) {
+ RSU_UNLOCK(sc);
+ /* Mark failure */
+ (void) ieee80211_ampdu_tx_request_active_ext(ni, tap->txa_tid, 0);
+ return (0);
+ }
+ RSU_UNLOCK(sc);
+
+ /* Mark success; we don't get any further notifications */
+ (void) ieee80211_ampdu_tx_request_active_ext(ni, tap->txa_tid, 1);
+#endif
+ /* Return 0, we're driving this ourselves */
+ return (0);
+}
+
+static int
+rsu_wme_update(struct ieee80211com *ic)
+{
+
+ /* Firmware handles this; not our problem */
+ return (0);
+}
+
+static int
+rsu_attach(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct rsu_softc *sc = device_get_softc(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+ int error;
+ uint8_t iface_index;
+ struct usb_interface *iface;
+ const char *rft;
+
+ device_set_usb_desc(self);
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+ sc->sc_rx_checksum_enable = 1;
+ if (rsu_enable_11n)
+ sc->sc_ht = !! (USB_GET_DRIVER_INFO(uaa) & RSU_HT_SUPPORTED);
+
+ /* Get number of endpoints */
+ iface = usbd_get_iface(sc->sc_udev, 0);
+ sc->sc_nendpoints = iface->idesc->bNumEndpoints;
+
+ /* Endpoints are hard-coded for now, so enforce 4-endpoint only */
+ if (sc->sc_nendpoints != 4) {
+ device_printf(sc->sc_dev,
+ "the driver currently only supports 4-endpoint devices\n");
+ return (ENXIO);
+ }
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
+ MTX_DEF);
+ RSU_DELKEY_BMAP_LOCK_INIT(sc);
+ TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0,
+ rsu_calib_task, sc);
+ TASK_INIT(&sc->del_key_task, 0, rsu_delete_key_pair_cb, sc);
+ TASK_INIT(&sc->tx_task, 0, rsu_tx_task, sc);
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ /* Allocate Tx/Rx buffers. */
+ error = rsu_alloc_rx_list(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not allocate Rx buffers\n");
+ goto fail_usb;
+ }
+
+ error = rsu_alloc_tx_list(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not allocate Tx buffers\n");
+ rsu_free_rx_list(sc);
+ goto fail_usb;
+ }
+
+ iface_index = 0;
+ error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not allocate USB transfers, err=%s\n",
+ usbd_errstr(error));
+ goto fail_usb;
+ }
+ RSU_LOCK(sc);
+ /* Read chip revision. */
+ sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT);
+ if (sc->cut != 3)
+ sc->cut = (sc->cut >> 1) + 1;
+ error = rsu_read_rom(sc);
+ RSU_UNLOCK(sc);
+ if (error != 0) {
+ device_printf(self, "could not read ROM\n");
+ goto fail_rom;
+ }
+
+ /* Figure out TX/RX streams */
+ switch (sc->rom[84]) {
+ case 0x0:
+ sc->sc_rftype = RTL8712_RFCONFIG_1T1R;
+ sc->sc_nrxstream = 1;
+ sc->sc_ntxstream = 1;
+ rft = "1T1R";
+ break;
+ case 0x1:
+ sc->sc_rftype = RTL8712_RFCONFIG_1T2R;
+ sc->sc_nrxstream = 2;
+ sc->sc_ntxstream = 1;
+ rft = "1T2R";
+ break;
+ case 0x2:
+ sc->sc_rftype = RTL8712_RFCONFIG_2T2R;
+ sc->sc_nrxstream = 2;
+ sc->sc_ntxstream = 2;
+ rft = "2T2R";
+ break;
+ default:
+ device_printf(sc->sc_dev,
+ "%s: unknown board type (rfconfig=0x%02x)\n",
+ __func__,
+ sc->rom[84]);
+ goto fail_rom;
+ }
+
+ IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->rom[0x12]);
+ device_printf(self, "MAC/BB RTL8712 cut %d %s\n", sc->cut, rft);
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(self);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* Not only, but not used. */
+ ic->ic_opmode = IEEE80211_M_STA; /* Default to BSS mode. */
+
+ /* Set device capabilities. */
+ ic->ic_caps =
+ IEEE80211_C_STA | /* station mode */
+ IEEE80211_C_MONITOR | /* monitor mode supported */
+#if 0
+ IEEE80211_C_BGSCAN | /* Background scan. */
+#endif
+ IEEE80211_C_SHPREAMBLE | /* Short preamble supported. */
+ IEEE80211_C_WME | /* WME/QoS */
+ IEEE80211_C_SHSLOT | /* Short slot time supported. */
+ IEEE80211_C_WPA; /* WPA/RSN. */
+
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_TKIP |
+ IEEE80211_CRYPTO_AES_CCM;
+
+ /* Check if HT support is present. */
+ if (sc->sc_ht) {
+ device_printf(sc->sc_dev, "%s: enabling 11n\n", __func__);
+
+ /* Enable basic HT */
+ ic->ic_htcaps = IEEE80211_HTC_HT |
+#if 0
+ IEEE80211_HTC_AMPDU |
+#endif
+ IEEE80211_HTC_AMSDU |
+ IEEE80211_HTCAP_MAXAMSDU_3839 |
+ IEEE80211_HTCAP_SMPS_OFF;
+ ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40;
+
+ /* set number of spatial streams */
+ ic->ic_txstream = sc->sc_ntxstream;
+ ic->ic_rxstream = sc->sc_nrxstream;
+ }
+ ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
+
+ rsu_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
+ ic->ic_channels);
+
+ ieee80211_ifattach(ic);
+ ic->ic_raw_xmit = rsu_raw_xmit;
+ ic->ic_scan_start = rsu_scan_start;
+ ic->ic_scan_end = rsu_scan_end;
+ ic->ic_getradiocaps = rsu_getradiocaps;
+ ic->ic_set_channel = rsu_set_channel;
+ ic->ic_scan_curchan = rsu_scan_curchan;
+ ic->ic_scan_mindwell = rsu_scan_mindwell;
+ ic->ic_vap_create = rsu_vap_create;
+ ic->ic_vap_delete = rsu_vap_delete;
+ ic->ic_update_promisc = rsu_update_promisc;
+ ic->ic_update_mcast = rsu_update_mcast;
+ ic->ic_ioctl = rsu_ioctl_net;
+ ic->ic_parent = rsu_parent;
+ ic->ic_transmit = rsu_transmit;
+ ic->ic_send_mgmt = rsu_send_mgmt;
+ ic->ic_update_chw = rsu_update_chw;
+ ic->ic_ampdu_enable = rsu_ampdu_enable;
+ ic->ic_wme.wme_update = rsu_wme_update;
+
+ ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr,
+ sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ RSU_RX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return (0);
+
+fail_rom:
+ usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
+fail_usb:
+ mtx_destroy(&sc->sc_mtx);
+ return (ENXIO);
+}
+
+static int
+rsu_detach(device_t self)
+{
+ struct rsu_softc *sc = device_get_softc(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ rsu_stop(sc);
+
+ usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
+
+ /*
+ * Free buffers /before/ we detach from net80211, else node
+ * references to destroyed vaps will lead to a panic.
+ */
+ /* Free Tx/Rx buffers. */
+ RSU_LOCK(sc);
+ rsu_free_tx_list(sc);
+ rsu_free_rx_list(sc);
+ RSU_UNLOCK(sc);
+
+ /* Frames are freed; detach from net80211 */
+ ieee80211_ifdetach(ic);
+
+ taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
+ taskqueue_drain(taskqueue_thread, &sc->del_key_task);
+ taskqueue_drain(taskqueue_thread, &sc->tx_task);
+
+ RSU_DELKEY_BMAP_LOCK_DESTROY(sc);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static usb_error_t
+rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req,
+ void *data)
+{
+ usb_error_t err;
+ int ntries = 10;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ while (ntries--) {
+ err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+ req, data, 0, NULL, 250 /* ms */);
+ if (err == 0 || err == USB_ERR_NOT_CONFIGURED)
+ break;
+ RSU_DPRINTF(sc, RSU_DEBUG_USB,
+ "Control request failed, %s (retries left: %d)\n",
+ usbd_errstr(err), ntries);
+ rsu_ms_delay(sc, 10);
+ }
+
+ return (err);
+}
+
+static struct ieee80211vap *
+rsu_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct rsu_softc *sc = ic->ic_softc;
+ struct rsu_vap *uvp;
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return (NULL);
+
+ uvp = malloc(sizeof(struct rsu_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &uvp->vap;
+
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags, bssid) != 0) {
+ /* out of memory */
+ free(uvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ ifp = vap->iv_ifp;
+ ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6;
+ RSU_LOCK(sc);
+ if (sc->sc_rx_checksum_enable)
+ ifp->if_capenable |= IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6;
+ RSU_UNLOCK(sc);
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ if (opmode == IEEE80211_M_MONITOR)
+ vap->iv_newstate = rsu_monitor_newstate;
+ else
+ vap->iv_newstate = rsu_newstate;
+ vap->iv_key_alloc = rsu_key_alloc;
+ vap->iv_key_set = rsu_key_set;
+ vap->iv_key_delete = rsu_key_delete;
+
+ /* Limits from the r92su driver */
+ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16;
+ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status, mac);
+ ic->ic_opmode = opmode;
+
+ return (vap);
+}
+
+static void
+rsu_vap_delete(struct ieee80211vap *vap)
+{
+ struct rsu_vap *uvp = RSU_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static void
+rsu_scan_start(struct ieee80211com *ic)
+{
+ struct rsu_softc *sc = ic->ic_softc;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ int error;
+
+ /* Scanning is done by the firmware. */
+ RSU_LOCK(sc);
+ sc->sc_active_scan = !!(ss->ss_flags & IEEE80211_SCAN_ACTIVE);
+ /* XXX TODO: force awake if in network-sleep? */
+ error = rsu_site_survey(sc, ss->ss_nssid > 0 ? &ss->ss_ssid[0] : NULL);
+ RSU_UNLOCK(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not send site survey command\n");
+ ieee80211_cancel_scan(vap);
+ }
+}
+
+static void
+rsu_scan_end(struct ieee80211com *ic)
+{
+ /* Nothing to do here. */
+}
+
+static void
+rsu_getradiocaps(struct ieee80211com *ic,
+ int maxchans, int *nchans, struct ieee80211_channel chans[])
+{
+ struct rsu_softc *sc = ic->ic_softc;
+ uint8_t bands[IEEE80211_MODE_BYTES];
+
+ /* Set supported .11b and .11g rates. */
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ if (sc->sc_ht)
+ setbit(bands, IEEE80211_MODE_11NG);
+ ieee80211_add_channel_list_2ghz(chans, maxchans, nchans,
+ rsu_chan_2ghz, nitems(rsu_chan_2ghz), bands,
+ (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) != 0);
+}
+
+static void
+rsu_set_channel(struct ieee80211com *ic)
+{
+ struct rsu_softc *sc = ic->ic_softc;
+
+ /*
+ * Only need to set the channel in Monitor mode. AP scanning and auth
+ * are already taken care of by their respective firmware commands.
+ */
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ struct r92s_set_channel cmd;
+ int error;
+
+ cmd.channel = IEEE80211_CHAN2IEEE(ic->ic_curchan);
+
+ RSU_LOCK(sc);
+ error = rsu_fw_cmd(sc, R92S_CMD_SET_CHANNEL, &cmd,
+ sizeof(cmd));
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "%s: error %d setting channel\n", __func__,
+ error);
+ }
+ RSU_UNLOCK(sc);
+ }
+}
+
+static void
+rsu_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
+{
+ /* Scan is done in rsu_scan_start(). */
+}
+
+/**
+ * Called by the net80211 framework to indicate
+ * the minimum dwell time has been met, terminate the scan.
+ * We don't actually terminate the scan as the firmware will notify
+ * us when it's finished and we have no way to interrupt it.
+ */
+static void
+rsu_scan_mindwell(struct ieee80211_scan_state *ss)
+{
+ /* NB: don't try to abort scan; wait for firmware to finish */
+}
+
+static void
+rsu_update_promisc(struct ieee80211com *ic)
+{
+ struct rsu_softc *sc = ic->ic_softc;
+
+ RSU_LOCK(sc);
+ if (sc->sc_running)
+ rsu_rxfilter_refresh(sc);
+ RSU_UNLOCK(sc);
+}
+
+/*
+ * The same as rtwn_get_multi_pos() / rtwn_set_multi().
+ */
+static uint8_t
+rsu_get_multi_pos(const uint8_t maddr[])
+{
+ uint64_t mask = 0x00004d101df481b4;
+ uint8_t pos = 0x27; /* initial value */
+ int i, j;
+
+ for (i = 0; i < IEEE80211_ADDR_LEN; i++)
+ for (j = (i == 0) ? 1 : 0; j < 8; j++)
+ if ((maddr[i] >> j) & 1)
+ pos ^= (mask >> (i * 8 + j - 1));
+
+ pos &= 0x3f;
+
+ return (pos);
+}
+
+static void
+rsu_set_multi(struct rsu_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t mfilt[2];
+
+ RSU_ASSERT_LOCKED(sc);
+
+ /* general structure was copied from ath(4). */
+ if (ic->ic_allmulti == 0) {
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+
+ /*
+ * Merge multicast addresses to form the hardware filter.
+ */
+ mfilt[0] = mfilt[1] = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ ifp = vap->iv_ifp;
+ if_maddr_rlock(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ caddr_t dl;
+ uint8_t pos;
+
+ dl = LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr);
+ pos = rsu_get_multi_pos(dl);
+
+ mfilt[pos / 32] |= (1 << (pos % 32));
+ }
+ if_maddr_runlock(ifp);
+ }
+ } else
+ mfilt[0] = mfilt[1] = ~0;
+
+ rsu_write_4(sc, R92S_MAR + 0, mfilt[0]);
+ rsu_write_4(sc, R92S_MAR + 4, mfilt[1]);
+
+ RSU_DPRINTF(sc, RSU_DEBUG_STATE, "%s: MC filter %08x:%08x\n",
+ __func__, mfilt[0], mfilt[1]);
+}
+
+static void
+rsu_update_mcast(struct ieee80211com *ic)
+{
+ struct rsu_softc *sc = ic->ic_softc;
+
+ RSU_LOCK(sc);
+ if (sc->sc_running)
+ rsu_set_multi(sc);
+ RSU_UNLOCK(sc);
+}
+
+static int
+rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[],
+ int ndata, int maxsz)
+{
+ int i, error;
+
+ for (i = 0; i < ndata; i++) {
+ struct rsu_data *dp = &data[i];
+ dp->sc = sc;
+ dp->m = NULL;
+ dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
+ if (dp->buf == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate buffer\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ dp->ni = NULL;
+ }
+
+ return (0);
+fail:
+ rsu_free_list(sc, data, ndata);
+ return (error);
+}
+
+static int
+rsu_alloc_rx_list(struct rsu_softc *sc)
+{
+ int error, i;
+
+ error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT,
+ RSU_RXBUFSZ);
+ if (error != 0)
+ return (error);
+
+ STAILQ_INIT(&sc->sc_rx_active);
+ STAILQ_INIT(&sc->sc_rx_inactive);
+
+ for (i = 0; i < RSU_RX_LIST_COUNT; i++)
+ STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next);
+
+ return (0);
+}
+
+static int
+rsu_alloc_tx_list(struct rsu_softc *sc)
+{
+ int error, i;
+
+ error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT,
+ RSU_TXBUFSZ);
+ if (error != 0)
+ return (error);
+
+ STAILQ_INIT(&sc->sc_tx_inactive);
+
+ for (i = 0; i != RSU_N_TRANSFER; i++) {
+ STAILQ_INIT(&sc->sc_tx_active[i]);
+ STAILQ_INIT(&sc->sc_tx_pending[i]);
+ }
+
+ for (i = 0; i < RSU_TX_LIST_COUNT; i++) {
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next);
+ }
+
+ return (0);
+}
+
+static void
+rsu_free_tx_list(struct rsu_softc *sc)
+{
+ int i;
+
+ /* prevent further allocations from TX list(s) */
+ STAILQ_INIT(&sc->sc_tx_inactive);
+
+ for (i = 0; i != RSU_N_TRANSFER; i++) {
+ STAILQ_INIT(&sc->sc_tx_active[i]);
+ STAILQ_INIT(&sc->sc_tx_pending[i]);
+ }
+
+ rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT);
+}
+
+static void
+rsu_free_rx_list(struct rsu_softc *sc)
+{
+ /* prevent further allocations from RX list(s) */
+ STAILQ_INIT(&sc->sc_rx_inactive);
+ STAILQ_INIT(&sc->sc_rx_active);
+
+ rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT);
+}
+
+static void
+rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata)
+{
+ int i;
+
+ for (i = 0; i < ndata; i++) {
+ struct rsu_data *dp = &data[i];
+
+ if (dp->buf != NULL) {
+ free(dp->buf, M_USBDEV);
+ dp->buf = NULL;
+ }
+ if (dp->ni != NULL) {
+ ieee80211_free_node(dp->ni);
+ dp->ni = NULL;
+ }
+ }
+}
+
+static struct rsu_data *
+_rsu_getbuf(struct rsu_softc *sc)
+{
+ struct rsu_data *bf;
+
+ bf = STAILQ_FIRST(&sc->sc_tx_inactive);
+ if (bf != NULL)
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
+ else
+ bf = NULL;
+ return (bf);
+}
+
+static struct rsu_data *
+rsu_getbuf(struct rsu_softc *sc)
+{
+ struct rsu_data *bf;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ bf = _rsu_getbuf(sc);
+ if (bf == NULL) {
+ RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: no buffers\n", __func__);
+ }
+ return (bf);
+}
+
+static void
+rsu_freebuf(struct rsu_softc *sc, struct rsu_data *bf)
+{
+
+ RSU_ASSERT_LOCKED(sc);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, bf, next);
+}
+
+static int
+rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
+ int len)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = R92S_REQ_REGS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ return (rsu_do_request(sc, &req, buf));
+}
+
+static void
+rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val)
+{
+ rsu_write_region_1(sc, addr, &val, 1);
+}
+
+static void
+rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val)
+{
+ val = htole16(val);
+ rsu_write_region_1(sc, addr, (uint8_t *)&val, 2);
+}
+
+static void
+rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val)
+{
+ val = htole32(val);
+ rsu_write_region_1(sc, addr, (uint8_t *)&val, 4);
+}
+
+static int
+rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
+ int len)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = R92S_REQ_REGS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ return (rsu_do_request(sc, &req, buf));
+}
+
+static uint8_t
+rsu_read_1(struct rsu_softc *sc, uint16_t addr)
+{
+ uint8_t val;
+
+ if (rsu_read_region_1(sc, addr, &val, 1) != 0)
+ return (0xff);
+ return (val);
+}
+
+static uint16_t
+rsu_read_2(struct rsu_softc *sc, uint16_t addr)
+{
+ uint16_t val;
+
+ if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0)
+ return (0xffff);
+ return (le16toh(val));
+}
+
+static uint32_t
+rsu_read_4(struct rsu_softc *sc, uint16_t addr)
+{
+ uint32_t val;
+
+ if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0)
+ return (0xffffffff);
+ return (le32toh(val));
+}
+
+static int
+rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd)
+{
+ int ntries;
+
+ rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd);
+ rsu_ms_delay(sc, 1);
+ for (ntries = 0; ntries < 50; ntries++) {
+ if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0)
+ return (0);
+ rsu_ms_delay(sc, 1);
+ }
+ return (ETIMEDOUT);
+}
+
+static uint8_t
+rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr)
+{
+ uint32_t reg;
+ int ntries;
+
+ reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
+ reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr);
+ reg &= ~R92S_EFUSE_CTRL_VALID;
+ rsu_write_4(sc, R92S_EFUSE_CTRL, reg);
+ /* Wait for read operation to complete. */
+ for (ntries = 0; ntries < 100; ntries++) {
+ reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
+ if (reg & R92S_EFUSE_CTRL_VALID)
+ return (MS(reg, R92S_EFUSE_CTRL_DATA));
+ rsu_ms_delay(sc, 1);
+ }
+ device_printf(sc->sc_dev,
+ "could not read efuse byte at address 0x%x\n", addr);
+ return (0xff);
+}
+
+static int
+rsu_read_rom(struct rsu_softc *sc)
+{
+ uint8_t *rom = sc->rom;
+ uint16_t addr = 0;
+ uint32_t reg;
+ uint8_t off, msk;
+ int i;
+
+ /* Make sure that ROM type is eFuse and that autoload succeeded. */
+ reg = rsu_read_1(sc, R92S_EE_9346CR);
+ if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN)
+ return (EIO);
+
+ /* Turn on 2.5V to prevent eFuse leakage. */
+ reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3);
+ rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80);
+ rsu_ms_delay(sc, 1);
+ rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80);
+
+ /* Read full ROM image. */
+ memset(&sc->rom, 0xff, sizeof(sc->rom));
+ while (addr < 512) {
+ reg = rsu_efuse_read_1(sc, addr);
+ if (reg == 0xff)
+ break;
+ addr++;
+ off = reg >> 4;
+ msk = reg & 0xf;
+ for (i = 0; i < 4; i++) {
+ if (msk & (1 << i))
+ continue;
+ rom[off * 8 + i * 2 + 0] =
+ rsu_efuse_read_1(sc, addr);
+ addr++;
+ rom[off * 8 + i * 2 + 1] =
+ rsu_efuse_read_1(sc, addr);
+ addr++;
+ }
+ }
+#ifdef USB_DEBUG
+ if (rsu_debug & RSU_DEBUG_RESET) {
+ /* Dump ROM content. */
+ printf("\n");
+ for (i = 0; i < sizeof(sc->rom); i++)
+ printf("%02x:", rom[i]);
+ printf("\n");
+ }
+#endif
+ return (0);
+}
+
+static int
+rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len)
+{
+ const uint8_t which = RSU_H2C_ENDPOINT;
+ struct rsu_data *data;
+ struct r92s_tx_desc *txd;
+ struct r92s_fw_cmd_hdr *cmd;
+ int cmdsz;
+ int xferlen;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ data = rsu_getbuf(sc);
+ if (data == NULL)
+ return (ENOMEM);
+
+ /* Blank the entire payload, just to be safe */
+ memset(data->buf, '\0', RSU_TXBUFSZ);
+
+ /* Round-up command length to a multiple of 8 bytes. */
+ /* XXX TODO: is this required? */
+ cmdsz = (len + 7) & ~7;
+
+ xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz;
+ KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__));
+ memset(data->buf, 0, xferlen);
+
+ /* Setup Tx descriptor. */
+ txd = (struct r92s_tx_desc *)data->buf;
+ txd->txdw0 = htole32(
+ SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
+ SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) |
+ R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
+ txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C));
+
+ /* Setup command header. */
+ cmd = (struct r92s_fw_cmd_hdr *)&txd[1];
+ cmd->len = htole16(cmdsz);
+ cmd->code = code;
+ cmd->seq = sc->cmd_seq;
+ sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f;
+
+ /* Copy command payload. */
+ memcpy(&cmd[1], buf, len);
+
+ RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FWCMD,
+ "%s: Tx cmd code=0x%x len=0x%x\n",
+ __func__, code, cmdsz);
+ data->buflen = xferlen;
+ STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
+ usbd_transfer_start(sc->sc_xfer[which]);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+rsu_calib_task(void *arg, int pending __unused)
+{
+ struct rsu_softc *sc = arg;
+#ifdef notyet
+ uint32_t reg;
+#endif
+
+ RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: running calibration task\n",
+ __func__);
+
+ RSU_LOCK(sc);
+#ifdef notyet
+ /* Read WPS PBC status. */
+ rsu_write_1(sc, R92S_MAC_PINMUX_CTRL,
+ R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG));
+ rsu_write_1(sc, R92S_GPIO_IO_SEL,
+ rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS);
+ reg = rsu_read_1(sc, R92S_GPIO_CTRL);
+ if (reg != 0xff && (reg & R92S_GPIO_WPS))
+ RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "WPS PBC is pushed\n");
+#endif
+ /* Read current signal level. */
+ if (rsu_fw_iocmd(sc, 0xf4000001) == 0) {
+ sc->sc_currssi = rsu_read_4(sc, R92S_IOCMD_DATA);
+ RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: RSSI=%d (%d)\n",
+ __func__, sc->sc_currssi,
+ rsu_hwrssi_to_rssi(sc, sc->sc_currssi));
+ }
+ if (sc->sc_calibrating)
+ taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz);
+ RSU_UNLOCK(sc);
+}
+
+static void
+rsu_tx_task(void *arg, int pending __unused)
+{
+ struct rsu_softc *sc = arg;
+
+ RSU_LOCK(sc);
+ _rsu_start(sc);
+ RSU_UNLOCK(sc);
+}
+
+#define RSU_PWR_UNKNOWN 0x0
+#define RSU_PWR_ACTIVE 0x1
+#define RSU_PWR_OFF 0x2
+#define RSU_PWR_SLEEP 0x3
+
+/*
+ * Set the current power state.
+ *
+ * The rtlwifi code doesn't do this so aggressively; it
+ * waits for an idle period after association with
+ * no traffic before doing this.
+ *
+ * For now - it's on in all states except RUN, and
+ * in RUN it'll transition to allow sleep.
+ */
+
+struct r92s_pwr_cmd {
+ uint8_t mode;
+ uint8_t smart_ps;
+ uint8_t bcn_pass_time;
+};
+
+static int
+rsu_set_fw_power_state(struct rsu_softc *sc, int state)
+{
+ struct r92s_set_pwr_mode cmd;
+ //struct r92s_pwr_cmd cmd;
+ int error;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ /* only change state if required */
+ if (sc->sc_curpwrstate == state)
+ return (0);
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ switch (state) {
+ case RSU_PWR_ACTIVE:
+ /* Force the hardware awake */
+ rsu_write_1(sc, R92S_USB_HRPWM,
+ R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON);
+ cmd.mode = R92S_PS_MODE_ACTIVE;
+ break;
+ case RSU_PWR_SLEEP:
+ cmd.mode = R92S_PS_MODE_DTIM; /* XXX configurable? */
+ cmd.smart_ps = 1; /* XXX 2 if doing p2p */
+ cmd.bcn_pass_time = 5; /* in 100mS usb.c, linux/rtlwifi */
+ break;
+ case RSU_PWR_OFF:
+ cmd.mode = R92S_PS_MODE_RADIOOFF;
+ break;
+ default:
+ device_printf(sc->sc_dev, "%s: unknown ps mode (%d)\n",
+ __func__,
+ state);
+ return (ENXIO);
+ }
+
+ RSU_DPRINTF(sc, RSU_DEBUG_RESET,
+ "%s: setting ps mode to %d (mode %d)\n",
+ __func__, state, cmd.mode);
+ error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd));
+ if (error == 0)
+ sc->sc_curpwrstate = state;
+
+ return (error);
+}
+
+static void
+rsu_set_led(struct rsu_softc *sc, int on)
+{
+ rsu_write_1(sc, R92S_LEDCFG,
+ (rsu_read_1(sc, R92S_LEDCFG) & 0xf0) | (!on << 3));
+}
+
+static int
+rsu_monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate,
+ int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rsu_softc *sc = ic->ic_softc;
+ struct rsu_vap *uvp = RSU_VAP(vap);
+
+ if (vap->iv_state != nstate) {
+ IEEE80211_UNLOCK(ic);
+ RSU_LOCK(sc);
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ sc->sc_vap_is_running = 0;
+ rsu_set_led(sc, 0);
+ break;
+ case IEEE80211_S_RUN:
+ sc->sc_vap_is_running = 1;
+ rsu_set_led(sc, 1);
+ break;
+ default:
+ /* NOTREACHED */
+ break;
+ }
+ rsu_rxfilter_refresh(sc);
+
+ RSU_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ }
+
+ return (uvp->newstate(vap, nstate, arg));
+}
+
+static int
+rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct rsu_vap *uvp = RSU_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rsu_softc *sc = ic->ic_softc;
+ struct ieee80211_node *ni;
+ struct ieee80211_rateset *rs;
+ enum ieee80211_state ostate;
+ int error, startcal = 0;
+
+ ostate = vap->iv_state;
+ RSU_DPRINTF(sc, RSU_DEBUG_STATE, "%s: %s -> %s\n",
+ __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+
+ IEEE80211_UNLOCK(ic);
+ if (ostate == IEEE80211_S_RUN) {
+ RSU_LOCK(sc);
+ /* Stop calibration. */
+ sc->sc_calibrating = 0;
+
+ /* Pause Tx for AC queues. */
+ rsu_write_1(sc, R92S_TXPAUSE, R92S_TXPAUSE_AC);
+ usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10));
+
+ RSU_UNLOCK(sc);
+ taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
+ taskqueue_drain(taskqueue_thread, &sc->tx_task);
+ RSU_LOCK(sc);
+ /* Disassociate from our current BSS. */
+ rsu_disconnect(sc);
+ usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10));
+
+ /* Refresh Rx filter (may be modified by firmware). */
+ sc->sc_vap_is_running = 0;
+ rsu_rxfilter_refresh(sc);
+
+ /* Reinstall static keys. */
+ if (sc->sc_running)
+ rsu_reinit_static_keys(sc);
+ } else
+ RSU_LOCK(sc);
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ (void) rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE);
+ break;
+ case IEEE80211_S_AUTH:
+ ni = ieee80211_ref_node(vap->iv_bss);
+ (void) rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE);
+ error = rsu_join_bss(sc, ni);
+ ieee80211_free_node(ni);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not send join command\n");
+ }
+ break;
+ case IEEE80211_S_RUN:
+ /* Flush all AC queues. */
+ rsu_write_1(sc, R92S_TXPAUSE, 0);
+
+ ni = ieee80211_ref_node(vap->iv_bss);
+ rs = &ni->ni_rates;
+ /* Indicate highest supported rate. */
+ ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1];
+ (void) rsu_set_fw_power_state(sc, RSU_PWR_SLEEP);
+ ieee80211_free_node(ni);
+ startcal = 1;
+ break;
+ default:
+ break;
+ }
+ if (startcal != 0) {
+ sc->sc_calibrating = 1;
+ /* Start periodic calibration. */
+ taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task,
+ hz);
+ }
+ RSU_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (uvp->newstate(vap, nstate, arg));
+}
+
+static int
+rsu_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
+ ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
+{
+ struct rsu_softc *sc = vap->iv_ic->ic_softc;
+ int is_checked = 0;
+
+ if (&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) {
+ *keyix = ieee80211_crypto_get_key_wepidx(vap, k);
+ } else {
+ if (vap->iv_opmode != IEEE80211_M_STA) {
+ *keyix = 0;
+ /* TODO: obtain keyix from node id */
+ is_checked = 1;
+ k->wk_flags |= IEEE80211_KEY_SWCRYPT;
+ } else
+ *keyix = R92S_MACID_BSS;
+ }
+
+ if (!is_checked) {
+ RSU_LOCK(sc);
+ if (isset(sc->keys_bmap, *keyix)) {
+ device_printf(sc->sc_dev,
+ "%s: key slot %d is already used!\n",
+ __func__, *keyix);
+ RSU_UNLOCK(sc);
+ return (0);
+ }
+ setbit(sc->keys_bmap, *keyix);
+ RSU_UNLOCK(sc);
+ }
+
+ *rxkeyix = *keyix;
+
+ return (1);
+}
+
+static int
+rsu_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k,
+ int set)
+{
+ struct rsu_softc *sc = vap->iv_ic->ic_softc;
+ int ret;
+
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+ /* Not for us. */
+ return (1);
+ }
+
+ /* Handle group keys. */
+ if (&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) {
+ KASSERT(k->wk_keyix < nitems(sc->group_keys),
+ ("keyix %u > %zu\n", k->wk_keyix, nitems(sc->group_keys)));
+
+ RSU_LOCK(sc);
+ sc->group_keys[k->wk_keyix] = (set ? k : NULL);
+ if (!sc->sc_running) {
+ /* Static keys will be set during device startup. */
+ RSU_UNLOCK(sc);
+ return (1);
+ }
+
+ if (set)
+ ret = rsu_set_key_group(sc, k);
+ else
+ ret = rsu_delete_key(sc, k->wk_keyix);
+ RSU_UNLOCK(sc);
+
+ return (!ret);
+ }
+
+ if (set) {
+ /* wait for pending key removal */
+ taskqueue_drain(taskqueue_thread, &sc->del_key_task);
+
+ RSU_LOCK(sc);
+ ret = rsu_set_key_pair(sc, k);
+ RSU_UNLOCK(sc);
+ } else {
+ RSU_DELKEY_BMAP_LOCK(sc);
+ setbit(sc->free_keys_bmap, k->wk_keyix);
+ RSU_DELKEY_BMAP_UNLOCK(sc);
+
+ /* workaround ieee80211_node_delucastkey() locking */
+ taskqueue_enqueue(taskqueue_thread, &sc->del_key_task);
+ ret = 0; /* fake success */
+ }
+
+ return (!ret);
+}
+
+static int
+rsu_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+ return (rsu_process_key(vap, k, 1));
+}
+
+static int
+rsu_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+ return (rsu_process_key(vap, k, 0));
+}
+
+static int
+rsu_cam_read(struct rsu_softc *sc, uint8_t addr, uint32_t *val)
+{
+ int ntries;
+
+ rsu_write_4(sc, R92S_CAMCMD,
+ R92S_CAMCMD_POLLING | SM(R92S_CAMCMD_ADDR, addr));
+ for (ntries = 0; ntries < 10; ntries++) {
+ if (!(rsu_read_4(sc, R92S_CAMCMD) & R92S_CAMCMD_POLLING))
+ break;
+
+ usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(1));
+ }
+ if (ntries == 10) {
+ device_printf(sc->sc_dev,
+ "%s: cannot read CAM entry at address %02X\n",
+ __func__, addr);
+ return (ETIMEDOUT);
+ }
+
+ *val = rsu_read_4(sc, R92S_CAMREAD);
+
+ return (0);
+}
+
+static void
+rsu_cam_write(struct rsu_softc *sc, uint8_t addr, uint32_t data)
+{
+
+ rsu_write_4(sc, R92S_CAMWRITE, data);
+ rsu_write_4(sc, R92S_CAMCMD,
+ R92S_CAMCMD_POLLING | R92S_CAMCMD_WRITE |
+ SM(R92S_CAMCMD_ADDR, addr));
+}
+
+static int
+rsu_key_check(struct rsu_softc *sc, ieee80211_keyix keyix, int is_valid)
+{
+ uint32_t val;
+ int error, ntries;
+
+ for (ntries = 0; ntries < 20; ntries++) {
+ usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(1));
+
+ error = rsu_cam_read(sc, R92S_CAM_CTL0(keyix), &val);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "%s: cannot check key status!\n", __func__);
+ return (error);
+ }
+ if (((val & R92S_CAM_VALID) == 0) ^ is_valid)
+ break;
+ }
+ if (ntries == 20) {
+ device_printf(sc->sc_dev,
+ "%s: key %d is %s marked as valid, rejecting request\n",
+ __func__, keyix, is_valid ? "not" : "still");
+ return (EIO);
+ }
+
+ return (0);
+}
+
+/*
+ * Map net80211 cipher to RTL8712 security mode.
+ */
+static uint8_t
+rsu_crypto_mode(struct rsu_softc *sc, u_int cipher, int keylen)
+{
+ switch (cipher) {
+ case IEEE80211_CIPHER_WEP:
+ return keylen < 8 ? R92S_KEY_ALGO_WEP40 : R92S_KEY_ALGO_WEP104;
+ case IEEE80211_CIPHER_TKIP:
+ return R92S_KEY_ALGO_TKIP;
+ case IEEE80211_CIPHER_AES_CCM:
+ return R92S_KEY_ALGO_AES;
+ default:
+ device_printf(sc->sc_dev, "unknown cipher %d\n", cipher);
+ return R92S_KEY_ALGO_INVALID;
+ }
+}
+
+static int
+rsu_set_key_group(struct rsu_softc *sc, const struct ieee80211_key *k)
+{
+ struct r92s_fw_cmd_set_key key;
+ uint8_t algo;
+ int error;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ /* Map net80211 cipher to HW crypto algorithm. */
+ algo = rsu_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen);
+ if (algo == R92S_KEY_ALGO_INVALID)
+ return (EINVAL);
+
+ memset(&key, 0, sizeof(key));
+ key.algo = algo;
+ key.cam_id = k->wk_keyix;
+ key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0;
+ memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key)));
+
+ RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD,
+ "%s: keyix %u, group %u, algo %u/%u, flags %04X, len %u, "
+ "macaddr %s\n", __func__, key.cam_id, key.grpkey,
+ k->wk_cipher->ic_cipher, key.algo, k->wk_flags, k->wk_keylen,
+ ether_sprintf(k->wk_macaddr));
+
+ error = rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "%s: cannot send firmware command, error %d\n",
+ __func__, error);
+ return (error);
+ }
+
+ return (rsu_key_check(sc, k->wk_keyix, 1));
+}
+
+static int
+rsu_set_key_pair(struct rsu_softc *sc, const struct ieee80211_key *k)
+{
+ struct r92s_fw_cmd_set_key_mac key;
+ uint8_t algo;
+ int error;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ if (!sc->sc_running)
+ return (ESHUTDOWN);
+
+ /* Map net80211 cipher to HW crypto algorithm. */
+ algo = rsu_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen);
+ if (algo == R92S_KEY_ALGO_INVALID)
+ return (EINVAL);
+
+ memset(&key, 0, sizeof(key));
+ key.algo = algo;
+ memcpy(key.macaddr, k->wk_macaddr, sizeof(key.macaddr));
+ memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key)));
+
+ RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD,
+ "%s: keyix %u, algo %u/%u, flags %04X, len %u, macaddr %s\n",
+ __func__, k->wk_keyix, k->wk_cipher->ic_cipher, key.algo,
+ k->wk_flags, k->wk_keylen, ether_sprintf(key.macaddr));
+
+ error = rsu_fw_cmd(sc, R92S_CMD_SET_STA_KEY, &key, sizeof(key));
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "%s: cannot send firmware command, error %d\n",
+ __func__, error);
+ return (error);
+ }
+
+ return (rsu_key_check(sc, k->wk_keyix, 1));
+}
+
+static int
+rsu_reinit_static_keys(struct rsu_softc *sc)
+{
+ int i, error;
+
+ for (i = 0; i < nitems(sc->group_keys); i++) {
+ if (sc->group_keys[i] != NULL) {
+ error = rsu_set_key_group(sc, sc->group_keys[i]);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "%s: failed to set static key %d, "
+ "error %d\n", __func__, i, error);
+ return (error);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+rsu_delete_key(struct rsu_softc *sc, ieee80211_keyix keyix)
+{
+ struct r92s_fw_cmd_set_key key;
+ uint32_t val;
+ int error;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ if (!sc->sc_running)
+ return (0);
+
+ /* check if it was automatically removed by firmware */
+ error = rsu_cam_read(sc, R92S_CAM_CTL0(keyix), &val);
+ if (error == 0 && (val & R92S_CAM_VALID) == 0) {
+ RSU_DPRINTF(sc, RSU_DEBUG_KEY,
+ "%s: key %u does not exist\n", __func__, keyix);
+ clrbit(sc->keys_bmap, keyix);
+ return (0);
+ }
+
+ memset(&key, 0, sizeof(key));
+ key.cam_id = keyix;
+
+ RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD,
+ "%s: removing key %u\n", __func__, key.cam_id);
+
+ error = rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "%s: cannot send firmware command, error %d\n",
+ __func__, error);
+ goto finish;
+ }
+
+ usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(5));
+
+ /*
+ * Clear 'valid' bit manually (cannot be done via firmware command).
+ * Used for key check + when firmware command cannot be sent.
+ */
+finish:
+ rsu_cam_write(sc, R92S_CAM_CTL0(keyix), 0);
+
+ clrbit(sc->keys_bmap, keyix);
+
+ return (rsu_key_check(sc, keyix, 0));
+}
+
+static void
+rsu_delete_key_pair_cb(void *arg, int pending __unused)
+{
+ struct rsu_softc *sc = arg;
+ int i;
+
+ RSU_DELKEY_BMAP_LOCK(sc);
+ for (i = IEEE80211_WEP_NKID; i < R92S_CAM_ENTRY_LIMIT; i++) {
+ if (isset(sc->free_keys_bmap, i)) {
+ RSU_DELKEY_BMAP_UNLOCK(sc);
+
+ RSU_LOCK(sc);
+ RSU_DPRINTF(sc, RSU_DEBUG_KEY,
+ "%s: calling rsu_delete_key() with keyix = %d\n",
+ __func__, i);
+ (void) rsu_delete_key(sc, i);
+ RSU_UNLOCK(sc);
+
+ RSU_DELKEY_BMAP_LOCK(sc);
+ clrbit(sc->free_keys_bmap, i);
+
+ /* bmap can be changed */
+ i = IEEE80211_WEP_NKID - 1;
+ continue;
+ }
+ }
+ RSU_DELKEY_BMAP_UNLOCK(sc);
+}
+
+static int
+rsu_site_survey(struct rsu_softc *sc, struct ieee80211_scan_ssid *ssid)
+{
+ struct r92s_fw_cmd_sitesurvey cmd;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ memset(&cmd, 0, sizeof(cmd));
+ /* TODO: passive channels? */
+ if (sc->sc_active_scan)
+ cmd.active = htole32(1);
+ cmd.limit = htole32(48);
+
+ if (ssid != NULL) {
+ sc->sc_extra_scan = 1;
+ cmd.ssidlen = htole32(ssid->len);
+ memcpy(cmd.ssid, ssid->ssid, ssid->len);
+ }
+#ifdef USB_DEBUG
+ if (rsu_debug & (RSU_DEBUG_SCAN | RSU_DEBUG_FWCMD)) {
+ device_printf(sc->sc_dev,
+ "sending site survey command, active %d",
+ le32toh(cmd.active));
+ if (ssid != NULL) {
+ printf(", ssid: ");
+ ieee80211_print_essid(cmd.ssid, le32toh(cmd.ssidlen));
+ }
+ printf("\n");
+ }
+#endif
+ return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)));
+}
+
+static int
+rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ndis_wlan_bssid_ex *bss;
+ struct ndis_802_11_fixed_ies *fixed;
+ struct r92s_fw_cmd_auth auth;
+ uint8_t buf[sizeof(*bss) + 128] __aligned(4);
+ uint8_t *frm;
+ uint8_t opmode;
+ int error;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ /* Let the FW decide the opmode based on the capinfo field. */
+ opmode = NDIS802_11AUTOUNKNOWN;
+ RSU_DPRINTF(sc, RSU_DEBUG_RESET,
+ "%s: setting operating mode to %d\n",
+ __func__, opmode);
+ error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode));
+ if (error != 0)
+ return (error);
+
+ memset(&auth, 0, sizeof(auth));
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ auth.mode = R92S_AUTHMODE_WPA;
+ auth.dot1x = (ni->ni_authmode == IEEE80211_AUTH_8021X);
+ } else
+ auth.mode = R92S_AUTHMODE_OPEN;
+ RSU_DPRINTF(sc, RSU_DEBUG_RESET,
+ "%s: setting auth mode to %d\n",
+ __func__, auth.mode);
+ error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth));
+ if (error != 0)
+ return (error);
+
+ memset(buf, 0, sizeof(buf));
+ bss = (struct ndis_wlan_bssid_ex *)buf;
+ IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid);
+ bss->ssid.ssidlen = htole32(ni->ni_esslen);
+ memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen);
+ if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA))
+ bss->privacy = htole32(1);
+ bss->rssi = htole32(ni->ni_avgrssi);
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ bss->networktype = htole32(NDIS802_11DS);
+ else
+ bss->networktype = htole32(NDIS802_11OFDM24);
+ bss->config.len = htole32(sizeof(bss->config));
+ bss->config.bintval = htole32(ni->ni_intval);
+ bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan));
+ bss->inframode = htole32(NDIS802_11INFRASTRUCTURE);
+ /* XXX verify how this is supposed to look! */
+ memcpy(bss->supprates, ni->ni_rates.rs_rates,
+ ni->ni_rates.rs_nrates);
+ /* Write the fixed fields of the beacon frame. */
+ fixed = (struct ndis_802_11_fixed_ies *)&bss[1];
+ memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8);
+ fixed->bintval = htole16(ni->ni_intval);
+ fixed->capabilities = htole16(ni->ni_capinfo);
+ /* Write IEs to be included in the association request. */
+ frm = (uint8_t *)&fixed[1];
+ frm = ieee80211_add_rsn(frm, vap);
+ frm = ieee80211_add_wpa(frm, vap);
+ frm = ieee80211_add_qos(frm, ni);
+ if ((ic->ic_flags & IEEE80211_F_WME) &&
+ (ni->ni_ies.wme_ie != NULL))
+ frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ frm = ieee80211_add_htcap(frm, ni);
+ frm = ieee80211_add_htinfo(frm, ni);
+ }
+ bss->ieslen = htole32(frm - (uint8_t *)fixed);
+ bss->len = htole32(((frm - buf) + 3) & ~3);
+ RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_FWCMD,
+ "%s: sending join bss command to %s chan %d\n",
+ __func__,
+ ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig));
+ return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf)));
+}
+
+static int
+rsu_disconnect(struct rsu_softc *sc)
+{
+ uint32_t zero = 0; /* :-) */
+
+ /* Disassociate from our current BSS. */
+ RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD,
+ "%s: sending disconnect command\n", __func__);
+ return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero)));
+}
+
+/*
+ * Map the hardware provided RSSI value to a signal level.
+ * For the most part it's just something we divide by and cap
+ * so it doesn't overflow the representation by net80211.
+ */
+static int
+rsu_hwrssi_to_rssi(struct rsu_softc *sc, int hw_rssi)
+{
+ int v;
+
+ if (hw_rssi == 0)
+ return (0);
+ v = hw_rssi >> 4;
+ if (v > 80)
+ v = 80;
+ return (v);
+}
+
+static void
+rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame *wh;
+ struct ndis_wlan_bssid_ex *bss;
+ struct ieee80211_rx_stats rxs;
+ struct mbuf *m;
+ int pktlen;
+
+ if (__predict_false(len < sizeof(*bss)))
+ return;
+ bss = (struct ndis_wlan_bssid_ex *)buf;
+ if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen)))
+ return;
+
+ RSU_DPRINTF(sc, RSU_DEBUG_SCAN,
+ "%s: found BSS %s: len=%d chan=%d inframode=%d "
+ "networktype=%d privacy=%d, RSSI=%d\n",
+ __func__,
+ ether_sprintf(bss->macaddr), le32toh(bss->len),
+ le32toh(bss->config.dsconfig), le32toh(bss->inframode),
+ le32toh(bss->networktype), le32toh(bss->privacy),
+ le32toh(bss->rssi));
+
+ /* Build a fake beacon frame to let net80211 do all the parsing. */
+ /* XXX TODO: just call the new scan API methods! */
+ pktlen = sizeof(*wh) + le32toh(bss->ieslen);
+ if (__predict_false(pktlen > MCLBYTES))
+ return;
+ m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (__predict_false(m == NULL))
+ return;
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
+ IEEE80211_FC0_SUBTYPE_BEACON;
+ wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ USETW(wh->i_dur, 0);
+ IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr);
+ *(uint16_t *)wh->i_seq = 0;
+ memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen));
+
+ /* Finalize mbuf. */
+ m->m_pkthdr.len = m->m_len = pktlen;
+
+ /* Set channel flags for input path */
+ bzero(&rxs, sizeof(rxs));
+ rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ;
+ rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
+ rxs.c_ieee = le32toh(bss->config.dsconfig);
+ rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ);
+ /* This is a number from 0..100; so let's just divide it down a bit */
+ rxs.c_rssi = le32toh(bss->rssi) / 2;
+ rxs.c_nf = -96;
+ if (ieee80211_add_rx_params(m, &rxs) == 0)
+ return;
+
+ /* XXX avoid a LOR */
+ RSU_UNLOCK(sc);
+ ieee80211_input_mimo_all(ic, m);
+ RSU_LOCK(sc);
+}
+
+static void
+rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct r92s_event_join_bss *rsp;
+ uint32_t tmp;
+ int res;
+
+ if (__predict_false(len < sizeof(*rsp)))
+ return;
+ rsp = (struct r92s_event_join_bss *)buf;
+ res = (int)le32toh(rsp->join_res);
+
+ RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD,
+ "%s: Rx join BSS event len=%d res=%d\n",
+ __func__, len, res);
+
+ /*
+ * XXX Don't do this; there's likely a better way to tell
+ * the caller we failed.
+ */
+ if (res <= 0) {
+ RSU_UNLOCK(sc);
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
+ RSU_LOCK(sc);
+ return;
+ }
+
+ tmp = le32toh(rsp->associd);
+ if (tmp >= vap->iv_max_aid) {
+ RSU_DPRINTF(sc, RSU_DEBUG_ANY, "Assoc ID overflow\n");
+ tmp = 1;
+ }
+ RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD,
+ "%s: associated with %s associd=%d\n",
+ __func__, ether_sprintf(rsp->bss.macaddr), tmp);
+ /* XXX is this required? What's the top two bits for again? */
+ ni->ni_associd = tmp | 0xc000;
+
+ /* Refresh Rx filter (was changed by firmware). */
+ sc->sc_vap_is_running = 1;
+ rsu_rxfilter_refresh(sc);
+
+ RSU_UNLOCK(sc);
+ ieee80211_new_state(vap, IEEE80211_S_RUN,
+ IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+ RSU_LOCK(sc);
+}
+
+static void
+rsu_event_addba_req_report(struct rsu_softc *sc, uint8_t *buf, int len)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct r92s_add_ba_event *ba = (void *) buf;
+ struct ieee80211_node *ni;
+
+ if (len < sizeof(*ba)) {
+ device_printf(sc->sc_dev, "%s: short read (%d)\n", __func__, len);
+ return;
+ }
+
+ if (vap == NULL)
+ return;
+
+ RSU_DPRINTF(sc, RSU_DEBUG_AMPDU, "%s: mac=%s, tid=%d, ssn=%d\n",
+ __func__,
+ ether_sprintf(ba->mac_addr),
+ (int) ba->tid,
+ (int) le16toh(ba->ssn));
+
+ /* XXX do node lookup; this is STA specific */
+
+ ni = ieee80211_ref_node(vap->iv_bss);
+ ieee80211_ampdu_rx_start_ext(ni, ba->tid, le16toh(ba->ssn) >> 4, 32);
+ ieee80211_free_node(ni);
+}
+
+static void
+rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD,
+ "%s: Rx event code=%d len=%d\n", __func__, code, len);
+ switch (code) {
+ case R92S_EVT_SURVEY:
+ rsu_event_survey(sc, buf, len);
+ break;
+ case R92S_EVT_SURVEY_DONE:
+ RSU_DPRINTF(sc, RSU_DEBUG_SCAN,
+ "%s: %s scan done, found %d BSS\n",
+ __func__, sc->sc_extra_scan ? "direct" : "broadcast",
+ le32toh(*(uint32_t *)buf));
+ if (sc->sc_extra_scan == 1) {
+ /* Send broadcast probe request. */
+ sc->sc_extra_scan = 0;
+ if (vap != NULL && rsu_site_survey(sc, NULL) != 0) {
+ RSU_UNLOCK(sc);
+ ieee80211_cancel_scan(vap);
+ RSU_LOCK(sc);
+ }
+ break;
+ }
+ if (vap != NULL) {
+ RSU_UNLOCK(sc);
+ ieee80211_scan_done(vap);
+ RSU_LOCK(sc);
+ }
+ break;
+ case R92S_EVT_JOIN_BSS:
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ rsu_event_join_bss(sc, buf, len);
+ break;
+ case R92S_EVT_DEL_STA:
+ RSU_DPRINTF(sc, RSU_DEBUG_FWCMD | RSU_DEBUG_STATE,
+ "%s: disassociated from %s\n", __func__,
+ ether_sprintf(buf));
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) {
+ RSU_UNLOCK(sc);
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
+ RSU_LOCK(sc);
+ }
+ break;
+ case R92S_EVT_WPS_PBC:
+ RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD,
+ "%s: WPS PBC pushed.\n", __func__);
+ break;
+ case R92S_EVT_FWDBG:
+ buf[60] = '\0';
+ RSU_DPRINTF(sc, RSU_DEBUG_FWDBG, "FWDBG: %s\n", (char *)buf);
+ break;
+ case R92S_EVT_ADDBA_REQ_REPORT:
+ rsu_event_addba_req_report(sc, buf, len);
+ break;
+ default:
+ device_printf(sc->sc_dev, "%s: unhandled code (%d)\n", __func__, code);
+ break;
+ }
+}
+
+static void
+rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len)
+{
+ struct r92s_fw_cmd_hdr *cmd;
+ int cmdsz;
+
+ RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx events len=%d\n", __func__, len);
+
+ /* Skip Rx status. */
+ buf += sizeof(struct r92s_rx_stat);
+ len -= sizeof(struct r92s_rx_stat);
+
+ /* Process all events. */
+ for (;;) {
+ /* Check that command header fits. */
+ if (__predict_false(len < sizeof(*cmd)))
+ break;
+ cmd = (struct r92s_fw_cmd_hdr *)buf;
+ /* Check that command payload fits. */
+ cmdsz = le16toh(cmd->len);
+ if (__predict_false(len < sizeof(*cmd) + cmdsz))
+ break;
+
+ /* Process firmware event. */
+ rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz);
+
+ if (!(cmd->seq & R92S_FW_CMD_MORE))
+ break;
+ buf += sizeof(*cmd) + cmdsz;
+ len -= sizeof(*cmd) + cmdsz;
+ }
+}
+
+static int8_t
+rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt)
+{
+ static const int8_t cckoff[] = { 14, -2, -20, -40 };
+ struct r92s_rx_phystat *phy;
+ struct r92s_rx_cck *cck;
+ uint8_t rpt;
+ int8_t rssi;
+
+ if (rate <= 3) {
+ cck = (struct r92s_rx_cck *)physt;
+ rpt = (cck->agc_rpt >> 6) & 0x3;
+ rssi = cck->agc_rpt & 0x3e;
+ rssi = cckoff[rpt] - rssi;
+ } else { /* OFDM/HT. */
+ phy = (struct r92s_rx_phystat *)physt;
+ rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106;
+ }
+ return (rssi);
+}
+
+static struct mbuf *
+rsu_rx_copy_to_mbuf(struct rsu_softc *sc, struct r92s_rx_stat *stat,
+ int totlen)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct mbuf *m;
+ uint32_t rxdw0;
+ int pktlen;
+
+ rxdw0 = le32toh(stat->rxdw0);
+ if (__predict_false(rxdw0 & (R92S_RXDW0_CRCERR | R92S_RXDW0_ICVERR))) {
+ RSU_DPRINTF(sc, RSU_DEBUG_RX,
+ "%s: RX flags error (%s)\n", __func__,
+ rxdw0 & R92S_RXDW0_CRCERR ? "CRC" : "ICV");
+ goto fail;
+ }
+
+ pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN);
+ if (__predict_false(pktlen < sizeof (struct ieee80211_frame_ack))) {
+ RSU_DPRINTF(sc, RSU_DEBUG_RX,
+ "%s: frame is too short: %d\n", __func__, pktlen);
+ goto fail;
+ }
+
+ m = m_get2(totlen, M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (__predict_false(m == NULL)) {
+ device_printf(sc->sc_dev,
+ "%s: could not allocate RX mbuf, totlen %d\n",
+ __func__, totlen);
+ goto fail;
+ }
+
+ /* Finalize mbuf. */
+ memcpy(mtod(m, uint8_t *), (uint8_t *)stat, totlen);
+ m->m_pkthdr.len = m->m_len = totlen;
+
+ return (m);
+fail:
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+}
+
+static uint32_t
+rsu_get_tsf_low(struct rsu_softc *sc)
+{
+ return (rsu_read_4(sc, R92S_TSFTR));
+}
+
+static uint32_t
+rsu_get_tsf_high(struct rsu_softc *sc)
+{
+ return (rsu_read_4(sc, R92S_TSFTR + 4));
+}
+
+static struct ieee80211_node *
+rsu_rx_frame(struct rsu_softc *sc, struct mbuf *m)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame_min *wh;
+ struct ieee80211_rx_stats rxs;
+ struct r92s_rx_stat *stat;
+ uint32_t rxdw0, rxdw3;
+ uint8_t cipher, rate;
+ int infosz;
+ int rssi;
+
+ stat = mtod(m, struct r92s_rx_stat *);
+ rxdw0 = le32toh(stat->rxdw0);
+ rxdw3 = le32toh(stat->rxdw3);
+
+ rate = MS(rxdw3, R92S_RXDW3_RATE);
+ cipher = MS(rxdw0, R92S_RXDW0_CIPHER);
+ infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
+
+ /* Get RSSI from PHY status descriptor if present. */
+ if (infosz != 0 && (rxdw0 & R92S_RXDW0_PHYST))
+ rssi = rsu_get_rssi(sc, rate, &stat[1]);
+ else {
+ /* Cheat and get the last calibrated RSSI */
+ rssi = rsu_hwrssi_to_rssi(sc, sc->sc_currssi);
+ }
+
+ if (ieee80211_radiotap_active(ic)) {
+ struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ /* Map HW rate index to 802.11 rate. */
+ tap->wr_flags = 0; /* TODO */
+ tap->wr_tsft = rsu_get_tsf_high(sc);
+ if (le32toh(stat->tsf_low) > rsu_get_tsf_low(sc))
+ tap->wr_tsft--;
+ tap->wr_tsft = (uint64_t)htole32(tap->wr_tsft) << 32;
+ tap->wr_tsft += stat->tsf_low;
+
+ if (rate < 12) {
+ switch (rate) {
+ /* CCK. */
+ case 0: tap->wr_rate = 2; break;
+ case 1: tap->wr_rate = 4; break;
+ case 2: tap->wr_rate = 11; break;
+ case 3: tap->wr_rate = 22; break;
+ /* OFDM. */
+ case 4: tap->wr_rate = 12; break;
+ case 5: tap->wr_rate = 18; break;
+ case 6: tap->wr_rate = 24; break;
+ case 7: tap->wr_rate = 36; break;
+ case 8: tap->wr_rate = 48; break;
+ case 9: tap->wr_rate = 72; break;
+ case 10: tap->wr_rate = 96; break;
+ case 11: tap->wr_rate = 108; break;
+ }
+ } else { /* MCS0~15. */
+ /* Bit 7 set means HT MCS instead of rate. */
+ tap->wr_rate = 0x80 | (rate - 12);
+ }
+
+ tap->wr_dbm_antsignal = rssi;
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ };
+
+ /* Hardware does Rx TCP checksum offload. */
+ /*
+ * This flag can be set for some other
+ * (e.g., EAPOL) frame types, so don't rely on it.
+ */
+ if (rxdw3 & R92S_RXDW3_TCPCHKVALID) {
+ RSU_DPRINTF(sc, RSU_DEBUG_RX,
+ "%s: TCP/IP checksums: %schecked / %schecked\n",
+ __func__,
+ (rxdw3 & R92S_RXDW3_TCPCHKRPT) ? "" : "not ",
+ (rxdw3 & R92S_RXDW3_IPCHKRPT) ? "" : "not ");
+
+ /*
+ * 'IP header checksum valid' bit will not be set if
+ * the frame was not checked / has incorrect checksum /
+ * does not have checksum (IPv6).
+ *
+ * NB: if DF bit is not set then frame will not be checked.
+ */
+ if (rxdw3 & R92S_RXDW3_IPCHKRPT) {
+ m->m_pkthdr.csum_flags = CSUM_IP_CHECKED;
+ m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+ }
+
+ /*
+ * This is independent of the above check.
+ */
+ if (rxdw3 & R92S_RXDW3_TCPCHKRPT) {
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
+ m->m_pkthdr.csum_flags |= CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+
+ /* RX flags */
+
+ /* Set channel flags for input path */
+ bzero(&rxs, sizeof(rxs));
+
+ /* normal RSSI */
+ rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
+ rxs.c_rssi = rssi;
+ rxs.c_nf = -96;
+
+ /* Rate */
+ if (!(rxdw3 & R92S_RXDW3_HTC)) {
+ switch (rate) {
+ /* CCK. */
+ case 0:
+ rxs.c_rate = 2;
+ rxs.c_pktflags |= IEEE80211_RX_F_CCK;
+ break;
+ case 1:
+ rxs.c_rate = 4;
+ rxs.c_pktflags |= IEEE80211_RX_F_CCK;
+ break;
+ case 2:
+ rxs.c_rate = 11;
+ rxs.c_pktflags |= IEEE80211_RX_F_CCK;
+ break;
+ case 3:
+ rxs.c_rate = 22;
+ rxs.c_pktflags |= IEEE80211_RX_F_CCK;
+ break;
+ /* OFDM. */
+ case 4:
+ rxs.c_rate = 12;
+ rxs.c_pktflags |= IEEE80211_RX_F_OFDM;
+ break;
+ case 5:
+ rxs.c_rate = 18;
+ rxs.c_pktflags |= IEEE80211_RX_F_OFDM;
+ break;
+ case 6:
+ rxs.c_rate = 24;
+ rxs.c_pktflags |= IEEE80211_RX_F_OFDM;
+ break;
+ case 7:
+ rxs.c_rate = 36;
+ rxs.c_pktflags |= IEEE80211_RX_F_OFDM;
+ break;
+ case 8:
+ rxs.c_rate = 48;
+ rxs.c_pktflags |= IEEE80211_RX_F_OFDM;
+ break;
+ case 9:
+ rxs.c_rate = 72;
+ rxs.c_pktflags |= IEEE80211_RX_F_OFDM;
+ break;
+ case 10:
+ rxs.c_rate = 96;
+ rxs.c_pktflags |= IEEE80211_RX_F_OFDM;
+ break;
+ case 11:
+ rxs.c_rate = 108;
+ rxs.c_pktflags |= IEEE80211_RX_F_OFDM;
+ break;
+ }
+ } else if (rate >= 12) { /* MCS0~15. */
+ /* Bit 7 set means HT MCS instead of rate. */
+ rxs.c_rate = (rate - 12);
+ rxs.c_pktflags |= IEEE80211_RX_F_HT;
+ }
+
+ (void) ieee80211_add_rx_params(m, &rxs);
+
+ /* Drop descriptor. */
+ m_adj(m, sizeof(*stat) + infosz);
+ wh = mtod(m, struct ieee80211_frame_min *);
+ if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
+ cipher != R92S_KEY_ALGO_NONE) {
+ m->m_flags |= M_WEP;
+ }
+
+ RSU_DPRINTF(sc, RSU_DEBUG_RX,
+ "%s: Rx frame len %d, rate %d, infosz %d\n",
+ __func__, m->m_len, rate, infosz);
+
+ if (m->m_len >= sizeof(*wh))
+ return (ieee80211_find_rxnode(ic, wh));
+
+ return (NULL);
+}
+
+static struct mbuf *
+rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len)
+{
+ struct r92s_rx_stat *stat;
+ uint32_t rxdw0;
+ int totlen, pktlen, infosz, npkts;
+ struct mbuf *m, *m0 = NULL, *prevm = NULL;
+
+ /*
+ * don't pass packets to the ieee80211 framework if the driver isn't
+ * RUNNING.
+ */
+ if (!sc->sc_running)
+ return (NULL);
+
+ /* Get the number of encapsulated frames. */
+ stat = (struct r92s_rx_stat *)buf;
+ npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT);
+ RSU_DPRINTF(sc, RSU_DEBUG_RX,
+ "%s: Rx %d frames in one chunk\n", __func__, npkts);
+
+ /* Process all of them. */
+ while (npkts-- > 0) {
+ if (__predict_false(len < sizeof(*stat)))
+ break;
+ stat = (struct r92s_rx_stat *)buf;
+ rxdw0 = le32toh(stat->rxdw0);
+
+ pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN);
+ if (__predict_false(pktlen == 0))
+ break;
+
+ infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
+
+ /* Make sure everything fits in xfer. */
+ totlen = sizeof(*stat) + infosz + pktlen;
+ if (__predict_false(totlen > len))
+ break;
+
+ /* Process 802.11 frame. */
+ m = rsu_rx_copy_to_mbuf(sc, stat, totlen);
+ if (m0 == NULL)
+ m0 = m;
+ if (prevm == NULL)
+ prevm = m;
+ else {
+ prevm->m_next = m;
+ prevm = m;
+ }
+ /* Next chunk is 128-byte aligned. */
+ totlen = (totlen + 127) & ~127;
+ buf += totlen;
+ len -= totlen;
+ }
+
+ return (m0);
+}
+
+static struct mbuf *
+rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data)
+{
+ struct rsu_softc *sc = data->sc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct r92s_rx_stat *stat;
+ int len;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ if (__predict_false(len < sizeof(*stat))) {
+ RSU_DPRINTF(sc, RSU_DEBUG_RX, "xfer too short %d\n", len);
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+ }
+ /* Determine if it is a firmware C2H event or an 802.11 frame. */
+ stat = (struct r92s_rx_stat *)data->buf;
+ if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) {
+ rsu_rx_multi_event(sc, data->buf, len);
+ /* No packets to process. */
+ return (NULL);
+ } else
+ return (rsu_rx_multi_frame(sc, data->buf, len));
+}
+
+static void
+rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rsu_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL, *next;
+ struct rsu_data *data;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&sc->sc_rx_active);
+ if (data == NULL)
+ goto tr_setup;
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
+ m = rsu_rxeof(xfer, data);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&sc->sc_rx_inactive);
+ if (data == NULL) {
+ KASSERT(m == NULL, ("mbuf isn't NULL"));
+ return;
+ }
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
+ usbd_xfer_set_frame_data(xfer, 0, data->buf,
+ usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ /*
+ * To avoid LOR we should unlock our private mutex here to call
+ * ieee80211_input() because here is at the end of a USB
+ * callback and safe to unlock.
+ */
+ while (m != NULL) {
+ next = m->m_next;
+ m->m_next = NULL;
+
+ ni = rsu_rx_frame(sc, m);
+ RSU_UNLOCK(sc);
+
+ if (ni != NULL) {
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ m->m_flags |= M_AMPDU;
+ (void)ieee80211_input_mimo(ni, m);
+ ieee80211_free_node(ni);
+ } else
+ (void)ieee80211_input_mimo_all(ic, m);
+
+ RSU_LOCK(sc);
+ m = next;
+ }
+ break;
+ default:
+ /* needs it to the inactive queue due to a error. */
+ data = STAILQ_FIRST(&sc->sc_rx_active);
+ if (data != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ }
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ break;
+ }
+
+}
+
+static void
+rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data)
+{
+#ifdef USB_DEBUG
+ struct rsu_softc *sc = usbd_xfer_softc(xfer);
+#endif
+
+ RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: called; data=%p\n",
+ __func__,
+ data);
+
+ if (data->m) {
+ /* XXX status? */
+ ieee80211_tx_complete(data->ni, data->m, 0);
+ data->m = NULL;
+ data->ni = NULL;
+ }
+}
+
+static void
+rsu_bulk_tx_callback_sub(struct usb_xfer *xfer, usb_error_t error,
+ uint8_t which)
+{
+ struct rsu_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct rsu_data *data;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&sc->sc_tx_active[which]);
+ if (data == NULL)
+ goto tr_setup;
+ RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: transfer done %p\n",
+ __func__, data);
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next);
+ rsu_txeof(xfer, data);
+ rsu_freebuf(sc, data);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&sc->sc_tx_pending[which]);
+ if (data == NULL) {
+ RSU_DPRINTF(sc, RSU_DEBUG_TXDONE,
+ "%s: empty pending queue sc %p\n", __func__, sc);
+ return;
+ }
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next);
+ usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
+ RSU_DPRINTF(sc, RSU_DEBUG_TXDONE,
+ "%s: submitting transfer %p\n",
+ __func__,
+ data);
+ usbd_transfer_submit(xfer);
+ break;
+ default:
+ data = STAILQ_FIRST(&sc->sc_tx_active[which]);
+ if (data != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next);
+ rsu_txeof(xfer, data);
+ rsu_freebuf(sc, data);
+ }
+ counter_u64_add(ic->ic_oerrors, 1);
+
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+
+ /*
+ * XXX TODO: if the queue is low, flush out FF TX frames.
+ * Remember to unlock the driver for now; net80211 doesn't
+ * defer it for us.
+ */
+}
+
+static void
+rsu_bulk_tx_callback_be_bk(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rsu_softc *sc = usbd_xfer_softc(xfer);
+
+ rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_BE_BK);
+
+ /* This kicks the TX taskqueue */
+ rsu_start(sc);
+}
+
+static void
+rsu_bulk_tx_callback_vi_vo(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rsu_softc *sc = usbd_xfer_softc(xfer);
+
+ rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_VI_VO);
+
+ /* This kicks the TX taskqueue */
+ rsu_start(sc);
+}
+
+static void
+rsu_bulk_tx_callback_h2c(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rsu_softc *sc = usbd_xfer_softc(xfer);
+
+ rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_H2C);
+
+ /* This kicks the TX taskqueue */
+ rsu_start(sc);
+}
+
+/*
+ * Transmit the given frame.
+ *
+ * This doesn't free the node or mbuf upon failure.
+ */
+static int
+rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni,
+ struct mbuf *m0, struct rsu_data *data)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k = NULL;
+ struct r92s_tx_desc *txd;
+ uint8_t type, cipher;
+ int prio = 0;
+ uint8_t which;
+ int hasqos;
+ int xferlen;
+ int qid;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+ RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: data=%p, m=%p\n",
+ __func__, data, m0);
+
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ device_printf(sc->sc_dev,
+ "ieee80211_crypto_encap returns NULL.\n");
+ /* XXX we don't expect the fragmented frames */
+ return (ENOBUFS);
+ }
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+ /* If we have QoS then use it */
+ /* XXX TODO: mbuf WME/PRI versus TID? */
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ /* Has QoS */
+ prio = M_WME_GETAC(m0);
+ which = rsu_wme_ac_xfer_map[prio];
+ hasqos = 1;
+ } else {
+ /* Non-QoS TID */
+ /* XXX TODO: tid=0 for non-qos TID? */
+ which = rsu_wme_ac_xfer_map[WME_AC_BE];
+ hasqos = 0;
+ prio = 0;
+ }
+
+ qid = rsu_ac2qid[prio];
+#if 0
+ switch (type) {
+ case IEEE80211_FC0_TYPE_CTL:
+ case IEEE80211_FC0_TYPE_MGT:
+ which = rsu_wme_ac_xfer_map[WME_AC_VO];
+ break;
+ default:
+ which = rsu_wme_ac_xfer_map[M_WME_GETAC(m0)];
+ break;
+ }
+ hasqos = 0;
+#endif
+
+ RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: pri=%d, which=%d, hasqos=%d\n",
+ __func__,
+ prio,
+ which,
+ hasqos);
+
+ /* Fill Tx descriptor. */
+ txd = (struct r92s_tx_desc *)data->buf;
+ memset(txd, 0, sizeof(*txd));
+
+ txd->txdw0 |= htole32(
+ SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) |
+ SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
+ R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
+
+ txd->txdw1 |= htole32(
+ SM(R92S_TXDW1_MACID, R92S_MACID_BSS) | SM(R92S_TXDW1_QSEL, qid));
+ if (!hasqos)
+ txd->txdw1 |= htole32(R92S_TXDW1_NONQOS);
+ if (k != NULL && !(k->wk_flags & IEEE80211_KEY_SWENCRYPT)) {
+ switch (k->wk_cipher->ic_cipher) {
+ case IEEE80211_CIPHER_WEP:
+ cipher = R92S_TXDW1_CIPHER_WEP;
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ cipher = R92S_TXDW1_CIPHER_TKIP;
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ cipher = R92S_TXDW1_CIPHER_AES;
+ break;
+ default:
+ cipher = R92S_TXDW1_CIPHER_NONE;
+ }
+ txd->txdw1 |= htole32(
+ SM(R92S_TXDW1_CIPHER, cipher) |
+ SM(R92S_TXDW1_KEYIDX, k->wk_keyix));
+ }
+ /* XXX todo: set AGGEN bit if appropriate? */
+ txd->txdw2 |= htole32(R92S_TXDW2_BK);
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ txd->txdw2 |= htole32(R92S_TXDW2_BMCAST);
+ /*
+ * Firmware will use and increment the sequence number for the
+ * specified priority.
+ */
+ txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, prio));
+
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct rsu_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ ieee80211_radiotap_tx(vap, m0);
+ }
+
+ xferlen = sizeof(*txd) + m0->m_pkthdr.len;
+ m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]);
+
+ data->buflen = xferlen;
+ data->ni = ni;
+ data->m = m0;
+ STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
+
+ /* start transfer, if any */
+ usbd_transfer_start(sc->sc_xfer[which]);
+ return (0);
+}
+
+static int
+rsu_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct rsu_softc *sc = ic->ic_softc;
+ int error;
+
+ RSU_LOCK(sc);
+ if (!sc->sc_running) {
+ RSU_UNLOCK(sc);
+ return (ENXIO);
+ }
+
+ /*
+ * XXX TODO: ensure that we treat 'm' as a list of frames
+ * to transmit!
+ */
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ RSU_DPRINTF(sc, RSU_DEBUG_TX,
+ "%s: mbufq_enable: failed (%d)\n",
+ __func__,
+ error);
+ RSU_UNLOCK(sc);
+ return (error);
+ }
+ RSU_UNLOCK(sc);
+
+ /* This kicks the TX taskqueue */
+ rsu_start(sc);
+
+ return (0);
+}
+
+static void
+rsu_drain_mbufq(struct rsu_softc *sc)
+{
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+
+ RSU_ASSERT_LOCKED(sc);
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ }
+}
+
+static void
+_rsu_start(struct rsu_softc *sc)
+{
+ struct ieee80211_node *ni;
+ struct rsu_data *bf;
+ struct mbuf *m;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ bf = rsu_getbuf(sc);
+ if (bf == NULL) {
+ RSU_DPRINTF(sc, RSU_DEBUG_TX,
+ "%s: failed to get buffer\n", __func__);
+ mbufq_prepend(&sc->sc_snd, m);
+ break;
+ }
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+
+ if (rsu_tx_start(sc, ni, m, bf) != 0) {
+ RSU_DPRINTF(sc, RSU_DEBUG_TX,
+ "%s: failed to transmit\n", __func__);
+ if_inc_counter(ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ rsu_freebuf(sc, bf);
+ ieee80211_free_node(ni);
+ m_freem(m);
+ break;
+ }
+ }
+}
+
+static void
+rsu_start(struct rsu_softc *sc)
+{
+
+ taskqueue_enqueue(taskqueue_thread, &sc->tx_task);
+}
+
+static int
+rsu_ioctl_net(struct ieee80211com *ic, u_long cmd, void *data)
+{
+ struct rsu_softc *sc = ic->ic_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int error;
+
+ error = 0;
+ switch (cmd) {
+ case SIOCSIFCAP:
+ {
+ struct ieee80211vap *vap;
+ int rxmask;
+
+ rxmask = ifr->ifr_reqcap & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6);
+
+ RSU_LOCK(sc);
+ /* Both RXCSUM bits must be set (or unset). */
+ if (sc->sc_rx_checksum_enable &&
+ rxmask != (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) {
+ rxmask = 0;
+ sc->sc_rx_checksum_enable = 0;
+ rsu_rxfilter_set(sc, R92S_RCR_TCP_OFFLD_EN, 0);
+ } else if (!sc->sc_rx_checksum_enable && rxmask != 0) {
+ rxmask = IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6;
+ sc->sc_rx_checksum_enable = 1;
+ rsu_rxfilter_set(sc, 0, R92S_RCR_TCP_OFFLD_EN);
+ } else {
+ /* Nothing to do. */
+ RSU_UNLOCK(sc);
+ break;
+ }
+ RSU_UNLOCK(sc);
+
+ IEEE80211_LOCK(ic); /* XXX */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+
+ ifp->if_capenable &=
+ ~(IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6);
+ ifp->if_capenable |= rxmask;
+ }
+ IEEE80211_UNLOCK(ic);
+ break;
+ }
+ default:
+ error = ENOTTY; /* for net80211 */
+ break;
+ }
+
+ return (error);
+}
+
+static void
+rsu_parent(struct ieee80211com *ic)
+{
+ struct rsu_softc *sc = ic->ic_softc;
+
+ if (ic->ic_nrunning > 0) {
+ if (rsu_init(sc) == 0)
+ ieee80211_start_all(ic);
+ else {
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ if (vap != NULL)
+ ieee80211_stop(vap);
+ }
+ } else
+ rsu_stop(sc);
+}
+
+/*
+ * Power on sequence for A-cut adapters.
+ */
+static void
+rsu_power_on_acut(struct rsu_softc *sc)
+{
+ uint32_t reg;
+
+ rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
+ rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
+
+ /* Enable AFE macro block's bandgap and Mbias. */
+ rsu_write_1(sc, R92S_AFE_MISC,
+ rsu_read_1(sc, R92S_AFE_MISC) |
+ R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN);
+ /* Enable LDOA15 block. */
+ rsu_write_1(sc, R92S_LDOA15_CTRL,
+ rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
+
+ rsu_write_1(sc, R92S_SPS1_CTRL,
+ rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN);
+ rsu_ms_delay(sc, 2000);
+ /* Enable switch regulator block. */
+ rsu_write_1(sc, R92S_SPS1_CTRL,
+ rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN);
+
+ rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267);
+
+ rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
+ rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
+
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
+ rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
+
+ rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
+ rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90);
+
+ /* Enable AFE clock. */
+ rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
+ rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
+ /* Enable AFE PLL macro block. */
+ rsu_write_1(sc, R92S_AFE_PLL_CTRL,
+ rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11);
+ /* Attach AFE PLL to MACTOP/BB. */
+ rsu_write_1(sc, R92S_SYS_ISO_CTRL,
+ rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
+
+ /* Switch to 40MHz clock instead of 80MHz. */
+ rsu_write_2(sc, R92S_SYS_CLKR,
+ rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL);
+
+ /* Enable MAC clock. */
+ rsu_write_2(sc, R92S_SYS_CLKR,
+ rsu_read_2(sc, R92S_SYS_CLKR) |
+ R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
+
+ rsu_write_1(sc, R92S_PMC_FSM, 0x02);
+
+ /* Enable digital core and IOREG R/W. */
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
+ rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
+
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
+ rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
+
+ /* Switch the control path to firmware. */
+ reg = rsu_read_2(sc, R92S_SYS_CLKR);
+ reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
+ rsu_write_2(sc, R92S_SYS_CLKR, reg);
+
+ rsu_write_2(sc, R92S_CR, 0x37fc);
+
+ /* Fix USB RX FIFO issue. */
+ rsu_write_1(sc, 0xfe5c,
+ rsu_read_1(sc, 0xfe5c) | 0x80);
+ rsu_write_1(sc, 0x00ab,
+ rsu_read_1(sc, 0x00ab) | 0xc0);
+
+ rsu_write_1(sc, R92S_SYS_CLKR,
+ rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
+}
+
+/*
+ * Power on sequence for B-cut and C-cut adapters.
+ */
+static void
+rsu_power_on_bcut(struct rsu_softc *sc)
+{
+ uint32_t reg;
+ int ntries;
+
+ /* Prevent eFuse leakage. */
+ rsu_write_1(sc, 0x37, 0xb0);
+ rsu_ms_delay(sc, 10);
+ rsu_write_1(sc, 0x37, 0x30);
+
+ /* Switch the control path to hardware. */
+ reg = rsu_read_2(sc, R92S_SYS_CLKR);
+ if (reg & R92S_FWHW_SEL) {
+ rsu_write_2(sc, R92S_SYS_CLKR,
+ reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL));
+ }
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
+ rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c);
+ rsu_ms_delay(sc, 1);
+
+ rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
+ rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
+
+ reg = rsu_read_1(sc, R92S_AFE_MISC);
+ rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN);
+ rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN |
+ R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN);
+
+ /* Enable PLL. */
+ rsu_write_1(sc, R92S_LDOA15_CTRL,
+ rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
+
+ rsu_write_1(sc, R92S_LDOV12D_CTRL,
+ rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN);
+
+ rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
+ rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
+
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
+ rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
+
+ /* Support 64KB IMEM. */
+ rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
+ rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97);
+
+ /* Enable AFE clock. */
+ rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
+ rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
+ /* Enable AFE PLL macro block. */
+ reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL);
+ rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
+ rsu_ms_delay(sc, 1);
+ rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51);
+ rsu_ms_delay(sc, 1);
+ rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
+ rsu_ms_delay(sc, 1);
+
+ /* Attach AFE PLL to MACTOP/BB. */
+ rsu_write_1(sc, R92S_SYS_ISO_CTRL,
+ rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
+
+ /* Switch to 40MHz clock. */
+ rsu_write_1(sc, R92S_SYS_CLKR, 0x00);
+ /* Disable CPU clock and 80MHz SSC. */
+ rsu_write_1(sc, R92S_SYS_CLKR,
+ rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0);
+ /* Enable MAC clock. */
+ rsu_write_2(sc, R92S_SYS_CLKR,
+ rsu_read_2(sc, R92S_SYS_CLKR) |
+ R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
+
+ rsu_write_1(sc, R92S_PMC_FSM, 0x02);
+
+ /* Enable digital core and IOREG R/W. */
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
+ rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
+
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
+ rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
+
+ /* Switch the control path to firmware. */
+ reg = rsu_read_2(sc, R92S_SYS_CLKR);
+ reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
+ rsu_write_2(sc, R92S_SYS_CLKR, reg);
+
+ rsu_write_2(sc, R92S_CR, 0x37fc);
+
+ /* Fix USB RX FIFO issue. */
+ rsu_write_1(sc, 0xfe5c,
+ rsu_read_1(sc, 0xfe5c) | 0x80);
+
+ rsu_write_1(sc, R92S_SYS_CLKR,
+ rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
+
+ rsu_write_1(sc, 0xfe1c, 0x80);
+
+ /* Make sure TxDMA is ready to download firmware. */
+ for (ntries = 0; ntries < 20; ntries++) {
+ reg = rsu_read_1(sc, R92S_TCR);
+ if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) ==
+ (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT))
+ break;
+ rsu_ms_delay(sc, 1);
+ }
+ if (ntries == 20) {
+ RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_TX,
+ "%s: TxDMA is not ready\n",
+ __func__);
+ /* Reset TxDMA. */
+ reg = rsu_read_1(sc, R92S_CR);
+ rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN);
+ rsu_ms_delay(sc, 1);
+ rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN);
+ }
+}
+
+static void
+rsu_power_off(struct rsu_softc *sc)
+{
+ /* Turn RF off. */
+ rsu_write_1(sc, R92S_RF_CTRL, 0x00);
+ rsu_ms_delay(sc, 5);
+
+ /* Turn MAC off. */
+ /* Switch control path. */
+ rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38);
+ /* Reset MACTOP. */
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70);
+ rsu_write_1(sc, R92S_PMC_FSM, 0x06);
+ rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9);
+ rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8);
+
+ /* Disable AFE PLL. */
+ rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00);
+ /* Disable A15V. */
+ rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54);
+ /* Disable eFuse 1.2V. */
+ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50);
+ rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24);
+ /* Enable AFE macro block's bandgap and Mbias. */
+ rsu_write_1(sc, R92S_AFE_MISC, 0x30);
+ /* Disable 1.6V LDO. */
+ rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56);
+ rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43);
+
+ /* Firmware - tell it to switch things off */
+ (void) rsu_set_fw_power_state(sc, RSU_PWR_OFF);
+}
+
+static int
+rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len)
+{
+ const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO];
+ struct rsu_data *data;
+ struct r92s_tx_desc *txd;
+ int mlen;
+
+ while (len > 0) {
+ data = rsu_getbuf(sc);
+ if (data == NULL)
+ return (ENOMEM);
+ txd = (struct r92s_tx_desc *)data->buf;
+ memset(txd, 0, sizeof(*txd));
+ if (len <= RSU_TXBUFSZ - sizeof(*txd)) {
+ /* Last chunk. */
+ txd->txdw0 |= htole32(R92S_TXDW0_LINIP);
+ mlen = len;
+ } else
+ mlen = RSU_TXBUFSZ - sizeof(*txd);
+ txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen));
+ memcpy(&txd[1], buf, mlen);
+ data->buflen = sizeof(*txd) + mlen;
+ RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FW | RSU_DEBUG_RESET,
+ "%s: starting transfer %p\n",
+ __func__, data);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
+ buf += mlen;
+ len -= mlen;
+ }
+ usbd_transfer_start(sc->sc_xfer[which]);
+ return (0);
+}
+
+static int
+rsu_load_firmware(struct rsu_softc *sc)
+{
+ const struct r92s_fw_hdr *hdr;
+ struct r92s_fw_priv *dmem;
+ struct ieee80211com *ic = &sc->sc_ic;
+ const uint8_t *imem, *emem;
+ int imemsz, ememsz;
+ const struct firmware *fw;
+ size_t size;
+ uint32_t reg;
+ int ntries, error;
+
+ if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY) {
+ RSU_DPRINTF(sc, RSU_DEBUG_ANY,
+ "%s: Firmware already loaded\n",
+ __func__);
+ return (0);
+ }
+
+ RSU_UNLOCK(sc);
+ /* Read firmware image from the filesystem. */
+ if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) {
+ device_printf(sc->sc_dev,
+ "%s: failed load firmware of file rsu-rtl8712fw\n",
+ __func__);
+ RSU_LOCK(sc);
+ return (ENXIO);
+ }
+ RSU_LOCK(sc);
+ size = fw->datasize;
+ if (size < sizeof(*hdr)) {
+ device_printf(sc->sc_dev, "firmware too short\n");
+ error = EINVAL;
+ goto fail;
+ }
+ hdr = (const struct r92s_fw_hdr *)fw->data;
+ if (hdr->signature != htole16(0x8712) &&
+ hdr->signature != htole16(0x8192)) {
+ device_printf(sc->sc_dev,
+ "invalid firmware signature 0x%x\n",
+ le16toh(hdr->signature));
+ error = EINVAL;
+ goto fail;
+ }
+ RSU_DPRINTF(sc, RSU_DEBUG_FW, "FW V%d %02x-%02x %02x:%02x\n",
+ le16toh(hdr->version), hdr->month, hdr->day, hdr->hour,
+ hdr->minute);
+
+ /* Make sure that driver and firmware are in sync. */
+ if (hdr->privsz != htole32(sizeof(*dmem))) {
+ device_printf(sc->sc_dev, "unsupported firmware image\n");
+ error = EINVAL;
+ goto fail;
+ }
+ /* Get FW sections sizes. */
+ imemsz = le32toh(hdr->imemsz);
+ ememsz = le32toh(hdr->sramsz);
+ /* Check that all FW sections fit in image. */
+ if (size < sizeof(*hdr) + imemsz + ememsz) {
+ device_printf(sc->sc_dev, "firmware too short\n");
+ error = EINVAL;
+ goto fail;
+ }
+ imem = (const uint8_t *)&hdr[1];
+ emem = imem + imemsz;
+
+ /* Load IMEM section. */
+ error = rsu_fw_loadsection(sc, imem, imemsz);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not load firmware section %s\n", "IMEM");
+ goto fail;
+ }
+ /* Wait for load to complete. */
+ for (ntries = 0; ntries != 50; ntries++) {
+ rsu_ms_delay(sc, 10);
+ reg = rsu_read_1(sc, R92S_TCR);
+ if (reg & R92S_TCR_IMEM_CODE_DONE)
+ break;
+ }
+ if (ntries == 50) {
+ device_printf(sc->sc_dev, "timeout waiting for IMEM transfer\n");
+ error = ETIMEDOUT;
+ goto fail;
+ }
+ /* Load EMEM section. */
+ error = rsu_fw_loadsection(sc, emem, ememsz);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not load firmware section %s\n", "EMEM");
+ goto fail;
+ }
+ /* Wait for load to complete. */
+ for (ntries = 0; ntries != 50; ntries++) {
+ rsu_ms_delay(sc, 10);
+ reg = rsu_read_2(sc, R92S_TCR);
+ if (reg & R92S_TCR_EMEM_CODE_DONE)
+ break;
+ }
+ if (ntries == 50) {
+ device_printf(sc->sc_dev, "timeout waiting for EMEM transfer\n");
+ error = ETIMEDOUT;
+ goto fail;
+ }
+ /* Enable CPU. */
+ rsu_write_1(sc, R92S_SYS_CLKR,
+ rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL);
+ if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) {
+ device_printf(sc->sc_dev, "could not enable system clock\n");
+ error = EIO;
+ goto fail;
+ }
+ rsu_write_2(sc, R92S_SYS_FUNC_EN,
+ rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN);
+ if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) {
+ device_printf(sc->sc_dev,
+ "could not enable microcontroller\n");
+ error = EIO;
+ goto fail;
+ }
+ /* Wait for CPU to initialize. */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_IMEM_RDY)
+ break;
+ rsu_ms_delay(sc, 1);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for microcontroller\n");
+ error = ETIMEDOUT;
+ goto fail;
+ }
+
+ /* Update DMEM section before loading. */
+ dmem = __DECONST(struct r92s_fw_priv *, &hdr->priv);
+ memset(dmem, 0, sizeof(*dmem));
+ dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172;
+ dmem->nendpoints = sc->sc_nendpoints;
+ dmem->chip_version = sc->cut;
+ dmem->rf_config = sc->sc_rftype;
+ dmem->vcs_type = R92S_VCS_TYPE_AUTO;
+ dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS;
+ dmem->turbo_mode = 0;
+ dmem->bw40_en = !! (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40);
+ dmem->amsdu2ampdu_en = !! (sc->sc_ht);
+ dmem->ampdu_en = !! (sc->sc_ht);
+ dmem->agg_offload = !! (sc->sc_ht);
+ dmem->qos_en = 1;
+ dmem->ps_offload = 1;
+ dmem->lowpower_mode = 1; /* XXX TODO: configurable? */
+ /* Load DMEM section. */
+ error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem));
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not load firmware section %s\n", "DMEM");
+ goto fail;
+ }
+ /* Wait for load to complete. */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE)
+ break;
+ rsu_ms_delay(sc, 1);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for %s transfer\n",
+ "DMEM");
+ error = ETIMEDOUT;
+ goto fail;
+ }
+ /* Wait for firmware readiness. */
+ for (ntries = 0; ntries < 60; ntries++) {
+ if (!(rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY))
+ break;
+ rsu_ms_delay(sc, 1);
+ }
+ if (ntries == 60) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for firmware readiness\n");
+ error = ETIMEDOUT;
+ goto fail;
+ }
+ fail:
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ return (error);
+}
+
+
+static int
+rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct rsu_softc *sc = ic->ic_softc;
+ struct rsu_data *bf;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!sc->sc_running) {
+ m_freem(m);
+ return (ENETDOWN);
+ }
+ RSU_LOCK(sc);
+ bf = rsu_getbuf(sc);
+ if (bf == NULL) {
+ m_freem(m);
+ RSU_UNLOCK(sc);
+ return (ENOBUFS);
+ }
+ if (rsu_tx_start(sc, ni, m, bf) != 0) {
+ m_freem(m);
+ rsu_freebuf(sc, bf);
+ RSU_UNLOCK(sc);
+ return (EIO);
+ }
+ RSU_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+rsu_rxfilter_init(struct rsu_softc *sc)
+{
+ uint32_t reg;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ /* Setup multicast filter. */
+ rsu_set_multi(sc);
+
+ /* Adjust Rx filter. */
+ reg = rsu_read_4(sc, R92S_RCR);
+ reg &= ~R92S_RCR_AICV;
+ reg |= R92S_RCR_APP_PHYSTS;
+ if (sc->sc_rx_checksum_enable)
+ reg |= R92S_RCR_TCP_OFFLD_EN;
+ rsu_write_4(sc, R92S_RCR, reg);
+
+ /* Update dynamic Rx filter parts. */
+ rsu_rxfilter_refresh(sc);
+}
+
+static void
+rsu_rxfilter_set(struct rsu_softc *sc, uint32_t clear, uint32_t set)
+{
+ /* NB: firmware can touch this register too. */
+ rsu_write_4(sc, R92S_RCR,
+ (rsu_read_4(sc, R92S_RCR) & ~clear) | set);
+}
+
+static void
+rsu_rxfilter_refresh(struct rsu_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t mask_all, mask_min;
+
+ RSU_ASSERT_LOCKED(sc);
+
+ /* NB: RCR_AMF / RXFLTMAP_MGT are used by firmware. */
+ mask_all = R92S_RCR_ACF | R92S_RCR_AAP;
+ mask_min = R92S_RCR_APM;
+ if (sc->sc_vap_is_running)
+ mask_min |= R92S_RCR_CBSSID;
+ else
+ mask_all |= R92S_RCR_ADF;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ uint16_t rxfltmap;
+ if (sc->sc_vap_is_running)
+ rxfltmap = 0;
+ else
+ rxfltmap = R92S_RXFLTMAP_MGT_DEF;
+ rsu_write_2(sc, R92S_RXFLTMAP_MGT, rxfltmap);
+ }
+
+ if (ic->ic_promisc == 0 && ic->ic_opmode != IEEE80211_M_MONITOR)
+ rsu_rxfilter_set(sc, mask_all, mask_min);
+ else
+ rsu_rxfilter_set(sc, mask_min, mask_all);
+}
+
+static int
+rsu_init(struct rsu_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ int error;
+ int i;
+
+ RSU_LOCK(sc);
+
+ if (sc->sc_running) {
+ RSU_UNLOCK(sc);
+ return (0);
+ }
+
+ /* Ensure the mbuf queue is drained */
+ rsu_drain_mbufq(sc);
+
+ /* Reset power management state. */
+ rsu_write_1(sc, R92S_USB_HRPWM, 0);
+
+ /* Power on adapter. */
+ if (sc->cut == 1)
+ rsu_power_on_acut(sc);
+ else
+ rsu_power_on_bcut(sc);
+
+ /* Load firmware. */
+ error = rsu_load_firmware(sc);
+ if (error != 0)
+ goto fail;
+
+ rsu_write_4(sc, R92S_CR,
+ rsu_read_4(sc, R92S_CR) & ~0xff000000);
+
+ /* Use 128 bytes pages. */
+ rsu_write_1(sc, 0x00b5,
+ rsu_read_1(sc, 0x00b5) | 0x01);
+ /* Enable USB Rx aggregation. */
+ rsu_write_1(sc, 0x00bd,
+ rsu_read_1(sc, 0x00bd) | 0x80);
+ /* Set USB Rx aggregation threshold. */
+ rsu_write_1(sc, 0x00d9, 0x01);
+ /* Set USB Rx aggregation timeout (1.7ms/4). */
+ rsu_write_1(sc, 0xfe5b, 0x04);
+ /* Fix USB Rx FIFO issue. */
+ rsu_write_1(sc, 0xfe5c,
+ rsu_read_1(sc, 0xfe5c) | 0x80);
+
+ /* Set MAC address. */
+ IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr);
+ rsu_write_region_1(sc, R92S_MACID, macaddr, IEEE80211_ADDR_LEN);
+
+ /* It really takes 1.5 seconds for the firmware to boot: */
+ usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(2000));
+
+ RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting MAC address to %s\n",
+ __func__,
+ ether_sprintf(macaddr));
+ error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, macaddr,
+ IEEE80211_ADDR_LEN);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not set MAC address\n");
+ goto fail;
+ }
+
+ /* Initialize Rx filter. */
+ rsu_rxfilter_init(sc);
+
+ /* Set PS mode fully active */
+ error = rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not set PS mode\n");
+ goto fail;
+ }
+
+ /* Install static keys (if any). */
+ error = rsu_reinit_static_keys(sc);
+ if (error != 0)
+ goto fail;
+
+ sc->sc_extra_scan = 0;
+ usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]);
+
+ /* We're ready to go. */
+ sc->sc_running = 1;
+ RSU_UNLOCK(sc);
+
+ return (0);
+fail:
+ /* Need to stop all failed transfers, if any */
+ for (i = 0; i != RSU_N_TRANSFER; i++)
+ usbd_transfer_stop(sc->sc_xfer[i]);
+ RSU_UNLOCK(sc);
+
+ return (error);
+}
+
+static void
+rsu_stop(struct rsu_softc *sc)
+{
+ int i;
+
+ RSU_LOCK(sc);
+ if (!sc->sc_running) {
+ RSU_UNLOCK(sc);
+ return;
+ }
+
+ sc->sc_running = 0;
+ sc->sc_vap_is_running = 0;
+ sc->sc_calibrating = 0;
+ taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL);
+ taskqueue_cancel(taskqueue_thread, &sc->tx_task, NULL);
+
+ /* Power off adapter. */
+ rsu_power_off(sc);
+
+ /*
+ * CAM is not accessible after shutdown;
+ * all entries are marked (by firmware?) as invalid.
+ */
+ memset(sc->free_keys_bmap, 0, sizeof(sc->free_keys_bmap));
+ memset(sc->keys_bmap, 0, sizeof(sc->keys_bmap));
+
+ for (i = 0; i < RSU_N_TRANSFER; i++)
+ usbd_transfer_stop(sc->sc_xfer[i]);
+
+ /* Ensure the mbuf queue is drained */
+ rsu_drain_mbufq(sc);
+ RSU_UNLOCK(sc);
+}
+
+/*
+ * Note: usb_pause_mtx() actually releases the mutex before calling pause(),
+ * which breaks any kind of driver serialisation.
+ */
+static void
+rsu_ms_delay(struct rsu_softc *sc, int ms)
+{
+
+ //usb_pause_mtx(&sc->sc_mtx, hz / 1000);
+ DELAY(ms * 1000);
+}
diff --git a/freebsd/sys/dev/usb/wlan/if_rsureg.h b/freebsd/sys/dev/usb/wlan/if_rsureg.h
new file mode 100644
index 00000000..973280cf
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_rsureg.h
@@ -0,0 +1,895 @@
+/*-
+ * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $OpenBSD: if_rsureg.h,v 1.3 2013/04/15 09:23:01 mglocker Exp $
+ * $FreeBSD$
+ */
+
+/* USB Requests. */
+#define R92S_REQ_REGS 0x05
+
+/*
+ * MAC registers.
+ */
+#define R92S_SYSCFG 0x0000
+#define R92S_SYS_ISO_CTRL (R92S_SYSCFG + 0x000)
+#define R92S_SYS_FUNC_EN (R92S_SYSCFG + 0x002)
+#define R92S_PMC_FSM (R92S_SYSCFG + 0x004)
+#define R92S_SYS_CLKR (R92S_SYSCFG + 0x008)
+#define R92S_EE_9346CR (R92S_SYSCFG + 0x00a)
+#define R92S_AFE_MISC (R92S_SYSCFG + 0x010)
+#define R92S_SPS0_CTRL (R92S_SYSCFG + 0x011)
+#define R92S_SPS1_CTRL (R92S_SYSCFG + 0x018)
+#define R92S_RF_CTRL (R92S_SYSCFG + 0x01f)
+#define R92S_LDOA15_CTRL (R92S_SYSCFG + 0x020)
+#define R92S_LDOV12D_CTRL (R92S_SYSCFG + 0x021)
+#define R92S_AFE_XTAL_CTRL (R92S_SYSCFG + 0x026)
+#define R92S_AFE_PLL_CTRL (R92S_SYSCFG + 0x028)
+#define R92S_EFUSE_CTRL (R92S_SYSCFG + 0x030)
+#define R92S_EFUSE_TEST (R92S_SYSCFG + 0x034)
+#define R92S_EFUSE_CLK_CTRL (R92S_SYSCFG + 0x2f8)
+
+#define R92S_CMDCTRL 0x0040
+#define R92S_CR (R92S_CMDCTRL + 0x000)
+#define R92S_TXPAUSE (R92S_CMDCTRL + 0x002)
+#define R92S_TCR (R92S_CMDCTRL + 0x004)
+#define R92S_RCR (R92S_CMDCTRL + 0x008)
+
+#define R92S_MACIDSETTING 0x0050
+#define R92S_MACID (R92S_MACIDSETTING + 0x000)
+#define R92S_MAR (R92S_MACIDSETTING + 0x010)
+
+#define R92S_TIMECTRL 0x0080
+#define R92S_TSFTR (R92S_TIMECTRL + 0x000)
+
+#define R92S_FIFOCTRL 0x00a0
+#define R92S_RXFLTMAP_MGT (R92S_FIFOCTRL + 0x076)
+#define R92S_RXFLTMAP_CTL (R92S_FIFOCTRL + 0x078)
+#define R92S_RXFLTMAP_DATA (R92S_FIFOCTRL + 0x07a)
+#define R92S_RXFLTMAP_MESH (R92S_FIFOCTRL + 0x07c)
+
+#define R92S_SECURITY 0x0240
+#define R92S_CAMCMD (R92S_SECURITY + 0x000)
+#define R92S_CAMWRITE (R92S_SECURITY + 0x004)
+#define R92S_CAMREAD (R92S_SECURITY + 0x008)
+
+#define R92S_GP 0x02e0
+#define R92S_GPIO_CTRL (R92S_GP + 0x00c)
+#define R92S_GPIO_IO_SEL (R92S_GP + 0x00e)
+#define R92S_MAC_PINMUX_CTRL (R92S_GP + 0x011)
+#define R92S_LEDCFG (R92S_GP + 0x012)
+
+#define R92S_IOCMD_CTRL 0x0370
+#define R92S_IOCMD_DATA 0x0374
+
+#define R92S_USB_HRPWM 0xfe58
+
+/* Bits for R92S_SYS_FUNC_EN. */
+#define R92S_FEN_CPUEN 0x0400
+
+/* Bits for R92S_PMC_FSM. */
+#define R92S_PMC_FSM_CUT_M 0x000f8000
+#define R92S_PMC_FSM_CUT_S 15
+
+/* Bits for R92S_SYS_CLKR. */
+#define R92S_SYS_CLKSEL 0x0001
+#define R92S_SYS_PS_CLKSEL 0x0002
+#define R92S_SYS_CPU_CLKSEL 0x0004
+#define R92S_MAC_CLK_EN 0x0800
+#define R92S_SYS_CLK_EN 0x1000
+#define R92S_SWHW_SEL 0x4000
+#define R92S_FWHW_SEL 0x8000
+
+/* Bits for R92S_EE_9346CR. */
+#define R92S_9356SEL 0x10
+#define R92S_EEPROM_EN 0x20
+
+/* Bits for R92S_AFE_MISC. */
+#define R92S_AFE_MISC_BGEN 0x01
+#define R92S_AFE_MISC_MBEN 0x02
+#define R92S_AFE_MISC_I32_EN 0x08
+
+/* Bits for R92S_SPS1_CTRL. */
+#define R92S_SPS1_LDEN 0x01
+#define R92S_SPS1_SWEN 0x02
+
+/* Bits for R92S_LDOA15_CTRL. */
+#define R92S_LDA15_EN 0x01
+
+/* Bits for R92S_LDOV12D_CTRL. */
+#define R92S_LDV12_EN 0x01
+
+/* Bits for R92C_EFUSE_CTRL. */
+#define R92S_EFUSE_CTRL_DATA_M 0x000000ff
+#define R92S_EFUSE_CTRL_DATA_S 0
+#define R92S_EFUSE_CTRL_ADDR_M 0x0003ff00
+#define R92S_EFUSE_CTRL_ADDR_S 8
+#define R92S_EFUSE_CTRL_VALID 0x80000000
+
+/* Bits for R92S_CR. */
+#define R92S_CR_TXDMA_EN 0x10
+
+/* Bits for R92S_TXPAUSE. */
+#define R92S_TXPAUSE_VO 0x01
+#define R92S_TXPAUSE_VI 0x02
+#define R92S_TXPAUSE_BE 0x04
+#define R92S_TXPAUSE_BK 0x08
+#define R92S_TXPAUSE_MGT 0x10
+#define R92S_TXPAUSE_HIGH 0x20
+#define R92S_TXPAUSE_HCCA 0x40
+
+/* Shortcuts. */
+#define R92S_TXPAUSE_AC \
+ (R92S_TXPAUSE_VO | R92S_TXPAUSE_VI | \
+ R92S_TXPAUSE_BE | R92S_TXPAUSE_BK)
+
+#define R92S_TXPAUSE_ALL \
+ (R92S_TXPAUSE_AC | R92S_TXPAUSE_MGT | \
+ R92S_TXPAUSE_HIGH | R92S_TXPAUSE_HCCA | 0x80)
+
+/* Bits for R92S_TCR. */
+#define R92S_TCR_IMEM_CODE_DONE 0x01
+#define R92S_TCR_IMEM_CHK_RPT 0x02
+#define R92S_TCR_EMEM_CODE_DONE 0x04
+#define R92S_TCR_EMEM_CHK_RPT 0x08
+#define R92S_TCR_DMEM_CODE_DONE 0x10
+#define R92S_TCR_IMEM_RDY 0x20
+#define R92S_TCR_FWRDY 0x80
+
+/* Bits for R92S_RCR. */
+#define R92S_RCR_AAP 0x00000001
+#define R92S_RCR_APM 0x00000002
+#define R92S_RCR_AM 0x00000004
+#define R92S_RCR_AB 0x00000008
+#define R92S_RCR_ACRC32 0x00000020
+#define R92S_RCR_AICV 0x00001000
+#define R92S_RCR_APP_ICV 0x00010000
+#define R92S_RCR_APP_MIC 0x00020000
+#define R92S_RCR_ADF 0x00040000
+#define R92S_RCR_ACF 0x00080000
+#define R92S_RCR_AMF 0x00100000
+#define R92S_RCR_ADD3 0x00200000
+#define R92S_RCR_APWRMGT 0x00400000
+#define R92S_RCR_CBSSID 0x00800000
+#define R92S_RCR_APP_PHYSTS 0x02000000
+#define R92S_RCR_TCP_OFFLD_EN 0x04000000
+#define R92S_RCR_ENMBID 0x08000000
+
+/* Bits for R92S_RXFLTMAP*. */
+#define R92S_RXFLTMAP_MGT_DEF 0x3f3f
+#define R92S_RXFLTMAP_FW(subtype) \
+ (1 << ((subtype) >> IEEE80211_FC0_SUBTYPE_SHIFT))
+
+/* Bits for R92S_GPIO_IO_SEL. */
+#define R92S_GPIO_WPS 0x10
+
+/* Bits for R92S_MAC_PINMUX_CTRL. */
+#define R92S_GPIOSEL_GPIO_M 0x03
+#define R92S_GPIOSEL_GPIO_S 0
+#define R92S_GPIOSEL_GPIO_JTAG 0
+#define R92S_GPIOSEL_GPIO_PHYDBG 1
+#define R92S_GPIOSEL_GPIO_BT 2
+#define R92S_GPIOSEL_GPIO_WLANDBG 3
+#define R92S_GPIOMUX_EN 0x08
+
+/* Bits for R92S_CAMCMD. */
+#define R92S_CAMCMD_ADDR_M 0x000000ff
+#define R92S_CAMCMD_ADDR_S 0
+#define R92S_CAMCMD_READ 0x00000000
+#define R92S_CAMCMD_WRITE 0x00010000
+#define R92S_CAMCMD_POLLING 0x80000000
+
+/*
+ * CAM entries.
+ */
+#define R92S_CAM_ENTRY_LIMIT 32
+#define R92S_CAM_ENTRY_BYTES howmany(R92S_CAM_ENTRY_LIMIT, NBBY)
+
+#define R92S_CAM_CTL0(entry) ((entry) * 8 + 0)
+#define R92S_CAM_CTL1(entry) ((entry) * 8 + 1)
+#define R92S_CAM_KEY(entry, i) ((entry) * 8 + 2 + (i))
+
+/* Bits for R92S_CAM_CTL0(i). */
+#define R92S_CAM_KEYID_M 0x00000003
+#define R92S_CAM_KEYID_S 0
+#define R92S_CAM_ALGO_M 0x0000001c
+#define R92S_CAM_ALGO_S 2
+#define R92S_CAM_VALID 0x00008000
+#define R92S_CAM_MACLO_M 0xffff0000
+#define R92S_CAM_MACLO_S 16
+
+/* Bits for R92S_IOCMD_CTRL. */
+#define R92S_IOCMD_CLASS_M 0xff000000
+#define R92S_IOCMD_CLASS_S 24
+#define R92S_IOCMD_CLASS_BB_RF 0xf0
+#define R92S_IOCMD_VALUE_M 0x00ffff00
+#define R92S_IOCMD_VALUE_S 8
+#define R92S_IOCMD_INDEX_M 0x000000ff
+#define R92S_IOCMD_INDEX_S 0
+#define R92S_IOCMD_INDEX_BB_READ 0
+#define R92S_IOCMD_INDEX_BB_WRITE 1
+#define R92S_IOCMD_INDEX_RF_READ 2
+#define R92S_IOCMD_INDEX_RF_WRITE 3
+
+/* Bits for R92S_USB_HRPWM. */
+#define R92S_USB_HRPWM_PS_ALL_ON 0x04
+#define R92S_USB_HRPWM_PS_ST_ACTIVE 0x08
+
+/*
+ * Macros to access subfields in registers.
+ */
+/* Mask and Shift (getter). */
+#define MS(val, field) \
+ (((val) & field##_M) >> field##_S)
+
+/* Shift and Mask (setter). */
+#define SM(field, val) \
+ (((val) << field##_S) & field##_M)
+
+/* Rewrite. */
+#define RW(var, field, val) \
+ (((var) & ~field##_M) | SM(field, val))
+
+/*
+ * ROM field with RF config.
+ */
+enum {
+ RTL8712_RFCONFIG_1T = 0x10,
+ RTL8712_RFCONFIG_2T = 0x20,
+ RTL8712_RFCONFIG_1R = 0x01,
+ RTL8712_RFCONFIG_2R = 0x02,
+ RTL8712_RFCONFIG_1T1R = 0x11,
+ RTL8712_RFCONFIG_1T2R = 0x12,
+ RTL8712_RFCONFIG_TURBO = 0x92,
+ RTL8712_RFCONFIG_2T2R = 0x22
+};
+
+/*
+ * Firmware image header.
+ */
+struct r92s_fw_priv {
+ /* QWORD0 */
+ uint16_t signature;
+ uint8_t hci_sel;
+#define R92S_HCI_SEL_PCIE 0x01
+#define R92S_HCI_SEL_USB 0x02
+#define R92S_HCI_SEL_SDIO 0x04
+#define R92S_HCI_SEL_8172 0x10
+#define R92S_HCI_SEL_AP 0x80
+
+ uint8_t chip_version;
+ uint16_t custid;
+ uint8_t rf_config;
+//0x11: 1T1R, 0x12: 1T2R, 0x92: 1T2R turbo, 0x22: 2T2R
+ uint8_t nendpoints;
+ /* QWORD1 */
+ uint32_t regulatory;
+ uint8_t rfintfs;
+ uint8_t def_nettype;
+ uint8_t turbo_mode;
+ uint8_t lowpower_mode;
+ /* QWORD2 */
+ uint8_t lbk_mode;
+ uint8_t mp_mode;
+ uint8_t vcs_type;
+#define R92S_VCS_TYPE_DISABLE 0
+#define R92S_VCS_TYPE_ENABLE 1
+#define R92S_VCS_TYPE_AUTO 2
+
+ uint8_t vcs_mode;
+#define R92S_VCS_MODE_NONE 0
+#define R92S_VCS_MODE_RTS_CTS 1
+#define R92S_VCS_MODE_CTS2SELF 2
+
+ uint32_t reserved1;
+ /* QWORD3 */
+ uint8_t qos_en;
+ uint8_t bw40_en;
+ uint8_t amsdu2ampdu_en;
+ uint8_t ampdu_en;
+ uint8_t rc_offload;
+ uint8_t agg_offload;
+ uint16_t reserved2;
+ /* QWORD4 */
+ uint8_t beacon_offload;
+ uint8_t mlme_offload;
+ uint8_t hwpc_offload;
+ uint8_t tcpcsum_offload;
+ uint8_t tcp_offload;
+ uint8_t ps_offload;
+ uint8_t wwlan_offload;
+ uint8_t reserved3;
+ /* QWORD5 */
+ uint16_t tcp_tx_len;
+ uint16_t tcp_rx_len;
+ uint32_t reserved4;
+} __packed;
+
+struct r92s_fw_hdr {
+ uint16_t signature;
+ uint16_t version;
+ uint32_t dmemsz;
+ uint32_t imemsz;
+ uint32_t sramsz;
+ uint32_t privsz;
+ uint16_t efuse_addr;
+ uint16_t h2c_resp_addr;
+ uint32_t svnrev;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ struct r92s_fw_priv priv;
+} __packed;
+
+/* Structure for FW commands and FW events notifications. */
+struct r92s_fw_cmd_hdr {
+ uint16_t len;
+ uint8_t code;
+ uint8_t seq;
+#define R92S_FW_CMD_MORE 0x80
+
+ uint32_t reserved;
+} __packed;
+
+/* FW commands codes. */
+#define R92S_CMD_READ_MACREG 0
+#define R92S_CMD_WRITE_MACREG 1
+#define R92S_CMD_READ_BBREG 2
+#define R92S_CMD_WRITE_BBREG 3
+#define R92S_CMD_READ_RFREG 4
+#define R92S_CMD_WRITE_RFREG 5
+#define R92S_CMD_READ_EEPROM 6
+#define R92S_CMD_WRITE_EEPROM 7
+#define R92S_CMD_READ_EFUSE 8
+#define R92S_CMD_WRITE_EFUSE 9
+#define R92S_CMD_READ_CAM 10
+#define R92S_CMD_WRITE_CAM 11
+#define R92S_CMD_SET_BCNITV 12
+#define R92S_CMD_SET_MBIDCFG 13
+#define R92S_CMD_JOIN_BSS 14
+#define R92S_CMD_DISCONNECT 15
+#define R92S_CMD_CREATE_BSS 16
+#define R92S_CMD_SET_OPMODE 17
+#define R92S_CMD_SITE_SURVEY 18
+#define R92S_CMD_SET_AUTH 19
+#define R92S_CMD_SET_KEY 20
+#define R92S_CMD_SET_STA_KEY 21
+#define R92S_CMD_SET_ASSOC_STA 22
+#define R92S_CMD_DEL_ASSOC_STA 23
+#define R92S_CMD_SET_STAPWRSTATE 24
+#define R92S_CMD_SET_BASIC_RATE 25
+#define R92S_CMD_GET_BASIC_RATE 26
+#define R92S_CMD_SET_DATA_RATE 27
+#define R92S_CMD_GET_DATA_RATE 28
+#define R92S_CMD_SET_PHY_INFO 29
+#define R92S_CMD_GET_PHY_INFO 30
+#define R92S_CMD_SET_PHY 31
+#define R92S_CMD_GET_PHY 32
+#define R92S_CMD_READ_RSSI 33
+#define R92S_CMD_READ_GAIN 34
+#define R92S_CMD_SET_ATIM 35
+#define R92S_CMD_SET_PWR_MODE 36
+#define R92S_CMD_JOIN_BSS_RPT 37
+#define R92S_CMD_SET_RA_TABLE 38
+#define R92S_CMD_GET_RA_TABLE 39
+#define R92S_CMD_GET_CCX_REPORT 40
+#define R92S_CMD_GET_DTM_REPORT 41
+#define R92S_CMD_GET_TXRATE_STATS 42
+#define R92S_CMD_SET_USB_SUSPEND 43
+#define R92S_CMD_SET_H2C_LBK 44
+#define R92S_CMD_ADDBA_REQ 45
+#define R92S_CMD_SET_CHANNEL 46
+#define R92S_CMD_SET_TXPOWER 47
+#define R92S_CMD_SWITCH_ANTENNA 48
+#define R92S_CMD_SET_CRYSTAL_CAL 49
+#define R92S_CMD_SET_SINGLE_CARRIER_TX 50
+#define R92S_CMD_SET_SINGLE_TONE_TX 51
+#define R92S_CMD_SET_CARRIER_SUPPR_TX 52
+#define R92S_CMD_SET_CONTINUOUS_TX 53
+#define R92S_CMD_SWITCH_BANDWIDTH 54
+#define R92S_CMD_TX_BEACON 55
+#define R92S_CMD_SET_POWER_TRACKING 56
+#define R92S_CMD_AMSDU_TO_AMPDU 57
+#define R92S_CMD_SET_MAC_ADDRESS 58
+#define R92S_CMD_GET_H2C_LBK 59
+#define R92S_CMD_SET_PBREQ_IE 60
+#define R92S_CMD_SET_ASSOCREQ_IE 61
+#define R92S_CMD_SET_PBRESP_IE 62
+#define R92S_CMD_SET_ASSOCRESP_IE 63
+#define R92S_CMD_GET_CURDATARATE 64
+#define R92S_CMD_GET_TXRETRY_CNT 65
+#define R92S_CMD_GET_RXRETRY_CNT 66
+#define R92S_CMD_GET_BCNOK_CNT 67
+#define R92S_CMD_GET_BCNERR_CNT 68
+#define R92S_CMD_GET_CURTXPWR_LEVEL 69
+#define R92S_CMD_SET_DIG 70
+#define R92S_CMD_SET_RA 71
+#define R92S_CMD_SET_PT 72
+#define R92S_CMD_READ_TSSI 73
+
+/* FW events notifications codes. */
+#define R92S_EVT_READ_MACREG 0
+#define R92S_EVT_READ_BBREG 1
+#define R92S_EVT_READ_RFREG 2
+#define R92S_EVT_READ_EEPROM 3
+#define R92S_EVT_READ_EFUSE 4
+#define R92S_EVT_READ_CAM 5
+#define R92S_EVT_GET_BASICRATE 6
+#define R92S_EVT_GET_DATARATE 7
+#define R92S_EVT_SURVEY 8
+#define R92S_EVT_SURVEY_DONE 9
+#define R92S_EVT_JOIN_BSS 10
+#define R92S_EVT_ADD_STA 11
+#define R92S_EVT_DEL_STA 12
+#define R92S_EVT_ATIM_DONE 13
+#define R92S_EVT_TX_REPORT 14
+#define R92S_EVT_CCX_REPORT 15
+#define R92S_EVT_DTM_REPORT 16
+#define R92S_EVT_TXRATE_STATS 17
+#define R92S_EVT_C2H_LBK 18
+#define R92S_EVT_FWDBG 19
+#define R92S_EVT_C2H_FEEDBACK 20
+#define R92S_EVT_ADDBA 21
+#define R92S_EVT_C2H_BCN 22
+#define R92S_EVT_PWR_STATE 23
+#define R92S_EVT_WPS_PBC 24
+#define R92S_EVT_ADDBA_REQ_REPORT 25
+
+/* Structure for R92S_CMD_SITE_SURVEY. */
+struct r92s_fw_cmd_sitesurvey {
+ uint32_t active;
+ uint32_t limit;
+ uint32_t ssidlen;
+ uint8_t ssid[32 + 1];
+} __packed;
+
+/* Structure for R92S_CMD_SET_AUTH. */
+struct r92s_fw_cmd_auth {
+ uint8_t mode;
+#define R92S_AUTHMODE_OPEN 0
+#define R92S_AUTHMODE_SHARED 1
+#define R92S_AUTHMODE_WPA 2
+
+ uint8_t dot1x;
+} __packed;
+
+/* Structure for R92S_CMD_SET_KEY. */
+struct r92s_fw_cmd_set_key {
+ uint8_t algo;
+#define R92S_KEY_ALGO_NONE 0
+#define R92S_KEY_ALGO_WEP40 1
+#define R92S_KEY_ALGO_TKIP 2
+#define R92S_KEY_ALGO_TKIP_MMIC 3
+#define R92S_KEY_ALGO_AES 4
+#define R92S_KEY_ALGO_WEP104 5
+#define R92S_KEY_ALGO_INVALID 0xff /* for rsu_crypto_mode() only */
+
+ uint8_t cam_id;
+ uint8_t grpkey;
+ uint8_t key[IEEE80211_KEYBUF_SIZE];
+} __packed;
+
+/* Structure for R92S_CMD_SET_STA_KEY. */
+struct r92s_fw_cmd_set_key_mac {
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ uint8_t algo;
+ uint8_t key[IEEE80211_KEYBUF_SIZE];
+} __packed;
+
+/* Structures for R92S_EVENT_SURVEY/R92S_CMD_JOIN_BSS. */
+/* NDIS_802_11_SSID. */
+struct ndis_802_11_ssid {
+ uint32_t ssidlen;
+ uint8_t ssid[32];
+} __packed;
+
+/* NDIS_802_11_CONFIGURATION_FH. */
+struct ndis_802_11_configuration_fh {
+ uint32_t len;
+ uint32_t hoppattern;
+ uint32_t hopset;
+ uint32_t dwelltime;
+} __packed;
+
+/* NDIS_802_11_CONFIGURATION. */
+struct ndis_802_11_configuration {
+ uint32_t len;
+ uint32_t bintval;
+ uint32_t atim;
+ uint32_t dsconfig;
+ struct ndis_802_11_configuration_fh fhconfig;
+} __packed;
+
+/* NDIS_WLAN_BSSID_EX. */
+struct ndis_wlan_bssid_ex {
+ uint32_t len;
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ uint8_t reserved[2];
+ struct ndis_802_11_ssid ssid;
+ uint32_t privacy;
+ int32_t rssi;
+ uint32_t networktype;
+#define NDIS802_11FH 0
+#define NDIS802_11DS 1
+#define NDIS802_11OFDM5 2
+#define NDIS802_11OFDM24 3
+#define NDIS802_11AUTOMODE 4
+
+ struct ndis_802_11_configuration config;
+ uint32_t inframode;
+#define NDIS802_11IBSS 0
+#define NDIS802_11INFRASTRUCTURE 1
+#define NDIS802_11AUTOUNKNOWN 2
+#define NDIS802_11MONITOR 3
+#define NDIS802_11APMODE 4
+
+ uint8_t supprates[16];
+ uint32_t ieslen;
+ /* Followed by ``ieslen'' bytes. */
+} __packed;
+
+/* NDIS_802_11_FIXED_IEs. */
+struct ndis_802_11_fixed_ies {
+ uint8_t tstamp[8];
+ uint16_t bintval;
+ uint16_t capabilities;
+} __packed;
+
+/* Structure for R92S_CMD_SET_PWR_MODE. */
+struct r92s_set_pwr_mode {
+ uint8_t mode;
+#define R92S_PS_MODE_ACTIVE 0
+#define R92S_PS_MODE_MIN 1
+#define R92S_PS_MODE_MAX 2
+#define R92S_PS_MODE_DTIM 3
+#define R92S_PS_MODE_VOIP 4
+#define R92S_PS_MODE_UAPSD_WMM 5
+#define R92S_PS_MODE_UAPSD 6
+#define R92S_PS_MODE_IBSS 7
+#define R92S_PS_MODE_WWLAN 8
+#define R92S_PS_MODE_RADIOOFF 9
+#define R92S_PS_MODE_DISABLE 10
+
+ uint8_t low_traffic_en;
+ uint8_t lpnav_en;
+ uint8_t rf_low_snr_en;
+ uint8_t dps_en;
+ uint8_t bcn_rx_en;
+ uint8_t bcn_pass_cnt;
+ uint8_t bcn_to;
+ uint16_t bcn_itv;
+ uint8_t app_itv;
+ uint8_t awake_bcn_itv;
+ uint8_t smart_ps;
+ uint8_t bcn_pass_time;
+} __packed;
+
+/* Structure for R92S_CMD_SET_CHANNEL. */
+struct r92s_set_channel {
+ uint32_t channel;
+} __packed;
+
+/* Structure for event R92S_EVENT_JOIN_BSS. */
+struct r92s_event_join_bss {
+ uint32_t next;
+ uint32_t prev;
+ uint32_t networktype;
+ uint32_t fixed;
+ uint32_t lastscanned;
+ uint32_t associd;
+ uint32_t join_res;
+ struct ndis_wlan_bssid_ex bss;
+} __packed;
+
+#define R92S_MACID_BSS 5 /* XXX hardcoded somewhere */
+
+/* Rx MAC descriptor. */
+struct r92s_rx_stat {
+ uint32_t rxdw0;
+#define R92S_RXDW0_PKTLEN_M 0x00003fff
+#define R92S_RXDW0_PKTLEN_S 0
+#define R92S_RXDW0_CRCERR 0x00004000
+#define R92S_RXDW0_ICVERR 0x00008000
+#define R92S_RXDW0_INFOSZ_M 0x000f0000
+#define R92S_RXDW0_INFOSZ_S 16
+#define R92S_RXDW0_CIPHER_M 0x00700000
+#define R92S_RXDW0_CIPHER_S 20
+#define R92S_RXDW0_QOS 0x00800000
+#define R92S_RXDW0_SHIFT_M 0x03000000
+#define R92S_RXDW0_SHIFT_S 24
+#define R92S_RXDW0_PHYST 0x04000000
+#define R92S_RXDW0_DECRYPTED 0x08000000
+
+ uint32_t rxdw1;
+#define R92S_RXDW1_MOREFRAG 0x08000000
+
+ uint32_t rxdw2;
+#define R92S_RXDW2_FRAG_M 0x0000f000
+#define R92S_RXDW2_FRAG_S 12
+#define R92S_RXDW2_PKTCNT_M 0x00ff0000
+#define R92S_RXDW2_PKTCNT_S 16
+
+ uint32_t rxdw3;
+#define R92S_RXDW3_RATE_M 0x0000003f
+#define R92S_RXDW3_RATE_S 0
+#define R92S_RXDW3_TCPCHKRPT 0x00000800
+#define R92S_RXDW3_IPCHKRPT 0x00001000
+#define R92S_RXDW3_TCPCHKVALID 0x00002000
+#define R92S_RXDW3_HTC 0x00004000
+
+ uint32_t rxdw4;
+ uint32_t tsf_low;
+} __packed __aligned(4);
+
+/* Rx PHY descriptor. */
+struct r92s_rx_phystat {
+ uint32_t phydw0;
+ uint32_t phydw1;
+ uint32_t phydw2;
+ uint32_t phydw3;
+ uint32_t phydw4;
+ uint32_t phydw5;
+ uint32_t phydw6;
+ uint32_t phydw7;
+} __packed __aligned(4);
+
+/* Rx PHY CCK descriptor. */
+struct r92s_rx_cck {
+ uint8_t adc_pwdb[4];
+ uint8_t sq_rpt;
+ uint8_t agc_rpt;
+} __packed;
+
+/* Tx MAC descriptor. */
+struct r92s_tx_desc {
+ uint32_t txdw0;
+#define R92S_TXDW0_PKTLEN_M 0x0000ffff
+#define R92S_TXDW0_PKTLEN_S 0
+#define R92S_TXDW0_OFFSET_M 0x00ff0000
+#define R92S_TXDW0_OFFSET_S 16
+#define R92S_TXDW0_TYPE_M 0x03000000
+#define R92S_TXDW0_TYPE_S 24
+#define R92S_TXDW0_LSG 0x04000000
+#define R92S_TXDW0_FSG 0x08000000
+#define R92S_TXDW0_LINIP 0x10000000
+#define R92S_TXDW0_OWN 0x80000000
+
+ uint32_t txdw1;
+#define R92S_TXDW1_MACID_M 0x0000001f
+#define R92S_TXDW1_MACID_S 0
+#define R92S_TXDW1_MOREDATA 0x00000020
+#define R92S_TXDW1_MOREFRAG 0x00000040
+#define R92S_TXDW1_QSEL_M 0x00001f00
+#define R92S_TXDW1_QSEL_S 8
+#define R92S_TXDW1_QSEL_BE 0x03
+#define R92S_TXDW1_QSEL_H2C 0x13
+#define R92S_TXDW1_NONQOS 0x00010000
+#define R92S_TXDW1_KEYIDX_M 0x00060000
+#define R92S_TXDW1_KEYIDX_S 17
+#define R92S_TXDW1_CIPHER_M 0x00c00000
+#define R92S_TXDW1_CIPHER_S 22
+#define R92S_TXDW1_CIPHER_NONE 0
+#define R92S_TXDW1_CIPHER_WEP 1
+#define R92S_TXDW1_CIPHER_TKIP 2
+#define R92S_TXDW1_CIPHER_AES 3
+#define R92S_TXDW1_HWPC 0x80000000
+
+ uint32_t txdw2;
+#define R92S_TXDW2_BMCAST 0x00000080
+#define R92S_TXDW2_AGGEN 0x20000000
+#define R92S_TXDW2_BK 0x40000000
+
+ uint32_t txdw3;
+#define R92S_TXDW3_SEQ_M 0x0fff0000
+#define R92S_TXDW3_SEQ_S 16
+#define R92S_TXDW3_FRAG_M 0xf0000000
+#define R92S_TXDW3_FRAG_S 28
+
+ uint32_t txdw4;
+#define R92S_TXDW4_TXBW 0x00040000
+
+ uint32_t txdw5;
+#define R92S_TXDW5_DISFB 0x00008000
+
+ uint16_t ipchksum;
+ uint16_t tcpchksum;
+
+ uint16_t txbufsize;
+ uint16_t reserved1;
+} __packed __aligned(4);
+
+struct r92s_add_ba_event {
+ uint8_t mac_addr[IEEE80211_ADDR_LEN];
+ uint16_t ssn;
+ uint8_t tid;
+};
+
+struct r92s_add_ba_req {
+ uint32_t tid;
+};
+
+/*
+ * Driver definitions.
+ */
+#define RSU_RX_LIST_COUNT 1
+#define RSU_TX_LIST_COUNT 32
+
+#define RSU_RXBUFSZ (30 * 1024)
+#define RSU_TXBUFSZ \
+ ((sizeof(struct r92s_tx_desc) + IEEE80211_MAX_LEN + 3) & ~3)
+
+#define RSU_TX_TIMEOUT 5000 /* ms */
+#define RSU_CMD_TIMEOUT 2000 /* ms */
+
+/* Queue ids (used by soft only). */
+#define RSU_QID_BCN 0
+#define RSU_QID_MGT 1
+#define RSU_QID_BMC 2
+#define RSU_QID_VO 3
+#define RSU_QID_VI 4
+#define RSU_QID_BE 5
+#define RSU_QID_BK 6
+#define RSU_QID_RXOFF 7
+#define RSU_QID_H2C 8
+#define RSU_QID_C2H 9
+
+/* Map AC to queue id. */
+static const uint8_t rsu_ac2qid[WME_NUM_AC] = {
+ RSU_QID_BE,
+ RSU_QID_BK,
+ RSU_QID_VI,
+ RSU_QID_VO
+};
+
+/* Pipe index to endpoint address mapping. */
+static const uint8_t r92s_epaddr[] =
+ { 0x83, 0x04, 0x06, 0x0d,
+ 0x05, 0x07,
+ 0x89, 0x0a, 0x0b, 0x0c };
+
+/* Queue id to pipe index mapping for 4 endpoints configurations. */
+static const uint8_t rsu_qid2idx_4ep[] =
+ { 3, 3, 3, 1, 1, 2, 2, 0, 3, 0 };
+
+/* Queue id to pipe index mapping for 6 endpoints configurations. */
+static const uint8_t rsu_qid2idx_6ep[] =
+ { 3, 3, 3, 1, 4, 2, 5, 0, 3, 0 };
+
+/* Queue id to pipe index mapping for 11 endpoints configurations. */
+static const uint8_t rsu_qid2idx_11ep[] =
+ { 7, 9, 8, 1, 4, 2, 5, 0, 3, 6 };
+
+struct rsu_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint64_t wr_tsft;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ uint8_t wr_dbm_antsignal;
+} __packed __aligned(8);
+
+#define RSU_RX_RADIOTAP_PRESENT \
+ (1 << IEEE80211_RADIOTAP_TSFT | \
+ 1 << IEEE80211_RADIOTAP_FLAGS | \
+ 1 << IEEE80211_RADIOTAP_RATE | \
+ 1 << IEEE80211_RADIOTAP_CHANNEL | \
+ 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL)
+
+struct rsu_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed __aligned(8);
+
+#define RSU_TX_RADIOTAP_PRESENT \
+ (1 << IEEE80211_RADIOTAP_FLAGS | \
+ 1 << IEEE80211_RADIOTAP_CHANNEL)
+
+struct rsu_softc;
+
+enum {
+ RSU_BULK_RX,
+ RSU_BULK_TX_BE_BK, /* = WME_AC_BE/BK */
+ RSU_BULK_TX_VI_VO, /* = WME_AC_VI/VO */
+ RSU_BULK_TX_H2C, /* H2C */
+ RSU_N_TRANSFER,
+};
+
+struct rsu_data {
+ struct rsu_softc *sc;
+ uint8_t *buf;
+ uint16_t buflen;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ STAILQ_ENTRY(rsu_data) next;
+};
+
+struct rsu_vap {
+ struct ieee80211vap vap;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RSU_VAP(vap) ((struct rsu_vap *)(vap))
+
+#define RSU_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RSU_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RSU_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+
+#define RSU_DELKEY_BMAP_LOCK_INIT(_sc) \
+ mtx_init(&(_sc)->free_keys_bmap_mtx, "bmap lock", NULL, MTX_DEF)
+#define RSU_DELKEY_BMAP_LOCK(_sc) mtx_lock(&(_sc)->free_keys_bmap_mtx)
+#define RSU_DELKEY_BMAP_UNLOCK(_sc) mtx_unlock(&(_sc)->free_keys_bmap_mtx)
+#define RSU_DELKEY_BMAP_LOCK_DESTROY(_sc) \
+ mtx_destroy(&(_sc)->free_keys_bmap_mtx)
+
+struct rsu_softc {
+ struct ieee80211com sc_ic;
+ struct mbufq sc_snd;
+ device_t sc_dev;
+ struct usb_device *sc_udev;
+
+ struct timeout_task calib_task;
+ struct task tx_task;
+ struct mtx sc_mtx;
+ int sc_ht;
+ int sc_nendpoints;
+ int sc_curpwrstate;
+ int sc_currssi;
+
+ u_int sc_running:1,
+ sc_vap_is_running:1,
+ sc_rx_checksum_enable:1,
+ sc_calibrating:1,
+ sc_active_scan:1,
+ sc_extra_scan:1;
+ u_int cut;
+ uint8_t sc_rftype;
+ int8_t sc_nrxstream;
+ int8_t sc_ntxstream;
+ struct rsu_data sc_rx[RSU_RX_LIST_COUNT];
+ struct rsu_data sc_tx[RSU_TX_LIST_COUNT];
+ uint8_t cmd_seq;
+ uint8_t rom[128];
+ struct usb_xfer *sc_xfer[RSU_N_TRANSFER];
+
+ STAILQ_HEAD(, rsu_data) sc_rx_active;
+ STAILQ_HEAD(, rsu_data) sc_rx_inactive;
+ STAILQ_HEAD(, rsu_data) sc_tx_active[RSU_N_TRANSFER];
+ STAILQ_HEAD(, rsu_data) sc_tx_inactive;
+ STAILQ_HEAD(, rsu_data) sc_tx_pending[RSU_N_TRANSFER];
+
+ struct task del_key_task;
+ uint8_t keys_bmap[R92S_CAM_ENTRY_BYTES];
+ const struct ieee80211_key *group_keys[IEEE80211_WEP_NKID];
+
+ struct mtx free_keys_bmap_mtx;
+ uint8_t free_keys_bmap[R92S_CAM_ENTRY_BYTES];
+
+ union {
+ struct rsu_rx_radiotap_header th;
+ uint8_t pad[64];
+ } sc_rxtapu;
+#define sc_rxtap sc_rxtapu.th
+
+ union {
+ struct rsu_tx_radiotap_header th;
+ uint8_t pad[64];
+ } sc_txtapu;
+#define sc_txtap sc_txtapu.th
+};
diff --git a/freebsd/sys/dev/usb/wlan/if_rum.c b/freebsd/sys/dev/usb/wlan/if_rum.c
new file mode 100644
index 00000000..19155ec2
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_rum.c
@@ -0,0 +1,3321 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005-2007 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ * Copyright (c) 2007-2008 Hans Petter Selasky <hselasky@FreeBSD.org>
+ * Copyright (c) 2015 Andriy Voskoboinyk <avos@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Ralink Technology RT2501USB/RT2601USB chipset driver
+ * http://www.ralinktech.com.tw/
+ */
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_ratectl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR rum_debug
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/wlan/if_rumreg.h>
+#include <dev/usb/wlan/if_rumvar.h>
+#include <dev/usb/wlan/if_rumfw.h>
+
+#ifdef USB_DEBUG
+static int rum_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum");
+SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RWTUN, &rum_debug, 0,
+ "Debug level");
+#endif
+
+static const STRUCT_USB_HOST_ID rum_devs[] = {
+#define RUM_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+ RUM_DEV(ABOCOM, HWU54DM),
+ RUM_DEV(ABOCOM, RT2573_2),
+ RUM_DEV(ABOCOM, RT2573_3),
+ RUM_DEV(ABOCOM, RT2573_4),
+ RUM_DEV(ABOCOM, WUG2700),
+ RUM_DEV(AMIT, CGWLUSB2GO),
+ RUM_DEV(ASUS, RT2573_1),
+ RUM_DEV(ASUS, RT2573_2),
+ RUM_DEV(BELKIN, F5D7050A),
+ RUM_DEV(BELKIN, F5D9050V3),
+ RUM_DEV(CISCOLINKSYS, WUSB54GC),
+ RUM_DEV(CISCOLINKSYS, WUSB54GR),
+ RUM_DEV(CONCEPTRONIC2, C54RU2),
+ RUM_DEV(COREGA, CGWLUSB2GL),
+ RUM_DEV(COREGA, CGWLUSB2GPX),
+ RUM_DEV(DICKSMITH, CWD854F),
+ RUM_DEV(DICKSMITH, RT2573),
+ RUM_DEV(EDIMAX, EW7318USG),
+ RUM_DEV(DLINK2, DWLG122C1),
+ RUM_DEV(DLINK2, WUA1340),
+ RUM_DEV(DLINK2, DWA111),
+ RUM_DEV(DLINK2, DWA110),
+ RUM_DEV(GIGABYTE, GNWB01GS),
+ RUM_DEV(GIGABYTE, GNWI05GS),
+ RUM_DEV(GIGASET, RT2573),
+ RUM_DEV(GOODWAY, RT2573),
+ RUM_DEV(GUILLEMOT, HWGUSB254LB),
+ RUM_DEV(GUILLEMOT, HWGUSB254V2AP),
+ RUM_DEV(HUAWEI3COM, WUB320G),
+ RUM_DEV(MELCO, G54HP),
+ RUM_DEV(MELCO, SG54HP),
+ RUM_DEV(MELCO, SG54HG),
+ RUM_DEV(MELCO, WLIUCG),
+ RUM_DEV(MELCO, WLRUCG),
+ RUM_DEV(MELCO, WLRUCGAOSS),
+ RUM_DEV(MSI, RT2573_1),
+ RUM_DEV(MSI, RT2573_2),
+ RUM_DEV(MSI, RT2573_3),
+ RUM_DEV(MSI, RT2573_4),
+ RUM_DEV(NOVATECH, RT2573),
+ RUM_DEV(PLANEX2, GWUS54HP),
+ RUM_DEV(PLANEX2, GWUS54MINI2),
+ RUM_DEV(PLANEX2, GWUSMM),
+ RUM_DEV(QCOM, RT2573),
+ RUM_DEV(QCOM, RT2573_2),
+ RUM_DEV(QCOM, RT2573_3),
+ RUM_DEV(RALINK, RT2573),
+ RUM_DEV(RALINK, RT2573_2),
+ RUM_DEV(RALINK, RT2671),
+ RUM_DEV(SITECOMEU, WL113R2),
+ RUM_DEV(SITECOMEU, WL172),
+ RUM_DEV(SPARKLAN, RT2573),
+ RUM_DEV(SURECOM, RT2573),
+#undef RUM_DEV
+};
+
+static device_probe_t rum_match;
+static device_attach_t rum_attach;
+static device_detach_t rum_detach;
+
+static usb_callback_t rum_bulk_read_callback;
+static usb_callback_t rum_bulk_write_callback;
+
+static usb_error_t rum_do_request(struct rum_softc *sc,
+ struct usb_device_request *req, void *data);
+static usb_error_t rum_do_mcu_request(struct rum_softc *sc, int);
+static struct ieee80211vap *rum_vap_create(struct ieee80211com *,
+ const char [IFNAMSIZ], int, enum ieee80211_opmode,
+ int, const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void rum_vap_delete(struct ieee80211vap *);
+static void rum_cmdq_cb(void *, int);
+static int rum_cmd_sleepable(struct rum_softc *, const void *,
+ size_t, uint8_t, CMD_FUNC_PROTO);
+static void rum_tx_free(struct rum_tx_data *, int);
+static void rum_setup_tx_list(struct rum_softc *);
+static void rum_reset_tx_list(struct rum_softc *,
+ struct ieee80211vap *);
+static void rum_unsetup_tx_list(struct rum_softc *);
+static void rum_beacon_miss(struct ieee80211vap *);
+static void rum_sta_recv_mgmt(struct ieee80211_node *,
+ struct mbuf *, int,
+ const struct ieee80211_rx_stats *, int, int);
+static int rum_set_power_state(struct rum_softc *, int);
+static int rum_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static uint8_t rum_crypto_mode(struct rum_softc *, u_int, int);
+static void rum_setup_tx_desc(struct rum_softc *,
+ struct rum_tx_desc *, struct ieee80211_key *,
+ uint32_t, uint8_t, uint8_t, int, int, int);
+static uint32_t rum_tx_crypto_flags(struct rum_softc *,
+ struct ieee80211_node *,
+ const struct ieee80211_key *);
+static int rum_tx_mgt(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int rum_tx_raw(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *,
+ const struct ieee80211_bpf_params *);
+static int rum_tx_data(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int rum_transmit(struct ieee80211com *, struct mbuf *);
+static void rum_start(struct rum_softc *);
+static void rum_parent(struct ieee80211com *);
+static void rum_eeprom_read(struct rum_softc *, uint16_t, void *,
+ int);
+static uint32_t rum_read(struct rum_softc *, uint16_t);
+static void rum_read_multi(struct rum_softc *, uint16_t, void *,
+ int);
+static usb_error_t rum_write(struct rum_softc *, uint16_t, uint32_t);
+static usb_error_t rum_write_multi(struct rum_softc *, uint16_t, void *,
+ size_t);
+static usb_error_t rum_setbits(struct rum_softc *, uint16_t, uint32_t);
+static usb_error_t rum_clrbits(struct rum_softc *, uint16_t, uint32_t);
+static usb_error_t rum_modbits(struct rum_softc *, uint16_t, uint32_t,
+ uint32_t);
+static int rum_bbp_busy(struct rum_softc *);
+static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t);
+static uint8_t rum_bbp_read(struct rum_softc *, uint8_t);
+static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t);
+static void rum_select_antenna(struct rum_softc *);
+static void rum_enable_mrr(struct rum_softc *);
+static void rum_set_txpreamble(struct rum_softc *);
+static void rum_set_basicrates(struct rum_softc *);
+static void rum_select_band(struct rum_softc *,
+ struct ieee80211_channel *);
+static void rum_set_chan(struct rum_softc *,
+ struct ieee80211_channel *);
+static void rum_set_maxretry(struct rum_softc *,
+ struct ieee80211vap *);
+static int rum_enable_tsf_sync(struct rum_softc *);
+static void rum_enable_tsf(struct rum_softc *);
+static void rum_abort_tsf_sync(struct rum_softc *);
+static void rum_get_tsf(struct rum_softc *, uint64_t *);
+static void rum_update_slot_cb(struct rum_softc *,
+ union sec_param *, uint8_t);
+static void rum_update_slot(struct ieee80211com *);
+static int rum_wme_update(struct ieee80211com *);
+static void rum_set_bssid(struct rum_softc *, const uint8_t *);
+static void rum_set_macaddr(struct rum_softc *, const uint8_t *);
+static void rum_update_mcast(struct ieee80211com *);
+static void rum_update_promisc(struct ieee80211com *);
+static void rum_setpromisc(struct rum_softc *);
+static const char *rum_get_rf(int);
+static void rum_read_eeprom(struct rum_softc *);
+static int rum_bbp_wakeup(struct rum_softc *);
+static int rum_bbp_init(struct rum_softc *);
+static void rum_clr_shkey_regs(struct rum_softc *);
+static int rum_init(struct rum_softc *);
+static void rum_stop(struct rum_softc *);
+static void rum_load_microcode(struct rum_softc *, const uint8_t *,
+ size_t);
+static int rum_set_sleep_time(struct rum_softc *, uint16_t);
+static int rum_reset(struct ieee80211vap *, u_long);
+static int rum_set_beacon(struct rum_softc *,
+ struct ieee80211vap *);
+static int rum_alloc_beacon(struct rum_softc *,
+ struct ieee80211vap *);
+static void rum_update_beacon_cb(struct rum_softc *,
+ union sec_param *, uint8_t);
+static void rum_update_beacon(struct ieee80211vap *, int);
+static int rum_common_key_set(struct rum_softc *,
+ struct ieee80211_key *, uint16_t);
+static void rum_group_key_set_cb(struct rum_softc *,
+ union sec_param *, uint8_t);
+static void rum_group_key_del_cb(struct rum_softc *,
+ union sec_param *, uint8_t);
+static void rum_pair_key_set_cb(struct rum_softc *,
+ union sec_param *, uint8_t);
+static void rum_pair_key_del_cb(struct rum_softc *,
+ union sec_param *, uint8_t);
+static int rum_key_alloc(struct ieee80211vap *,
+ struct ieee80211_key *, ieee80211_keyix *,
+ ieee80211_keyix *);
+static int rum_key_set(struct ieee80211vap *,
+ const struct ieee80211_key *);
+static int rum_key_delete(struct ieee80211vap *,
+ const struct ieee80211_key *);
+static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void rum_scan_start(struct ieee80211com *);
+static void rum_scan_end(struct ieee80211com *);
+static void rum_set_channel(struct ieee80211com *);
+static void rum_getradiocaps(struct ieee80211com *, int, int *,
+ struct ieee80211_channel[]);
+static int rum_get_rssi(struct rum_softc *, uint8_t);
+static void rum_ratectl_start(struct rum_softc *,
+ struct ieee80211_node *);
+static void rum_ratectl_timeout(void *);
+static void rum_ratectl_task(void *, int);
+static int rum_pause(struct rum_softc *, int);
+
+static const struct {
+ uint32_t reg;
+ uint32_t val;
+} rum_def_mac[] = {
+ { RT2573_TXRX_CSR0, 0x025fb032 },
+ { RT2573_TXRX_CSR1, 0x9eaa9eaf },
+ { RT2573_TXRX_CSR2, 0x8a8b8c8d },
+ { RT2573_TXRX_CSR3, 0x00858687 },
+ { RT2573_TXRX_CSR7, 0x2e31353b },
+ { RT2573_TXRX_CSR8, 0x2a2a2a2c },
+ { RT2573_TXRX_CSR15, 0x0000000f },
+ { RT2573_MAC_CSR6, 0x00000fff },
+ { RT2573_MAC_CSR8, 0x016c030a },
+ { RT2573_MAC_CSR10, 0x00000718 },
+ { RT2573_MAC_CSR12, 0x00000004 },
+ { RT2573_MAC_CSR13, 0x00007f00 },
+ { RT2573_SEC_CSR2, 0x00000000 },
+ { RT2573_SEC_CSR3, 0x00000000 },
+ { RT2573_SEC_CSR4, 0x00000000 },
+ { RT2573_PHY_CSR1, 0x000023b0 },
+ { RT2573_PHY_CSR5, 0x00040a06 },
+ { RT2573_PHY_CSR6, 0x00080606 },
+ { RT2573_PHY_CSR7, 0x00000408 },
+ { RT2573_AIFSN_CSR, 0x00002273 },
+ { RT2573_CWMIN_CSR, 0x00002344 },
+ { RT2573_CWMAX_CSR, 0x000034aa }
+};
+
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} rum_def_bbp[] = {
+ { 3, 0x80 },
+ { 15, 0x30 },
+ { 17, 0x20 },
+ { 21, 0xc8 },
+ { 22, 0x38 },
+ { 23, 0x06 },
+ { 24, 0xfe },
+ { 25, 0x0a },
+ { 26, 0x0d },
+ { 32, 0x0b },
+ { 34, 0x12 },
+ { 37, 0x07 },
+ { 39, 0xf8 },
+ { 41, 0x60 },
+ { 53, 0x10 },
+ { 54, 0x18 },
+ { 60, 0x10 },
+ { 61, 0x04 },
+ { 62, 0x04 },
+ { 75, 0xfe },
+ { 86, 0xfe },
+ { 88, 0xfe },
+ { 90, 0x0f },
+ { 99, 0x00 },
+ { 102, 0x16 },
+ { 107, 0x04 }
+};
+
+static const uint8_t rum_chan_2ghz[] =
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+
+static const uint8_t rum_chan_5ghz[] =
+ { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64,
+ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140,
+ 149, 153, 157, 161, 165 };
+
+static const struct rfprog {
+ uint8_t chan;
+ uint32_t r1, r2, r3, r4;
+} rum_rf5226[] = {
+ { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 },
+ { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 },
+ { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 },
+ { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 },
+ { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 },
+ { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 },
+ { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 },
+ { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 },
+ { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 },
+ { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 },
+ { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 },
+ { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 },
+ { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 },
+ { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 },
+
+ { 34, 0x00b03, 0x20266, 0x36014, 0x30282 },
+ { 38, 0x00b03, 0x20267, 0x36014, 0x30284 },
+ { 42, 0x00b03, 0x20268, 0x36014, 0x30286 },
+ { 46, 0x00b03, 0x20269, 0x36014, 0x30288 },
+
+ { 36, 0x00b03, 0x00266, 0x26014, 0x30288 },
+ { 40, 0x00b03, 0x00268, 0x26014, 0x30280 },
+ { 44, 0x00b03, 0x00269, 0x26014, 0x30282 },
+ { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 },
+ { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 },
+ { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 },
+ { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 },
+ { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 },
+
+ { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 },
+ { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 },
+ { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 },
+ { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 },
+ { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 },
+ { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 },
+ { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 },
+ { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 },
+ { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 },
+ { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 },
+ { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 },
+
+ { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 },
+ { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 },
+ { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 },
+ { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 },
+ { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 }
+}, rum_rf5225[] = {
+ { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 },
+ { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 },
+ { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 },
+ { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 },
+ { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 },
+ { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 },
+ { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 },
+ { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 },
+ { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 },
+ { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 },
+ { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 },
+ { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 },
+ { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 },
+ { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 },
+
+ { 34, 0x00b33, 0x01266, 0x26014, 0x30282 },
+ { 38, 0x00b33, 0x01267, 0x26014, 0x30284 },
+ { 42, 0x00b33, 0x01268, 0x26014, 0x30286 },
+ { 46, 0x00b33, 0x01269, 0x26014, 0x30288 },
+
+ { 36, 0x00b33, 0x01266, 0x26014, 0x30288 },
+ { 40, 0x00b33, 0x01268, 0x26014, 0x30280 },
+ { 44, 0x00b33, 0x01269, 0x26014, 0x30282 },
+ { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 },
+ { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 },
+ { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 },
+ { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 },
+ { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 },
+
+ { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 },
+ { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 },
+ { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 },
+ { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 },
+ { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 },
+ { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 },
+ { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 },
+ { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 },
+ { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 },
+ { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 },
+ { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 },
+
+ { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 },
+ { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 },
+ { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 },
+ { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 },
+ { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 }
+};
+
+static const struct usb_config rum_config[RUM_N_TRANSFER] = {
+ [RUM_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8),
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = rum_bulk_write_callback,
+ .timeout = 5000, /* ms */
+ },
+ [RUM_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = rum_bulk_read_callback,
+ },
+};
+
+static int
+rum_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != 0)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != RT2573_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa));
+}
+
+static int
+rum_attach(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct rum_softc *sc = device_get_softc(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t tmp;
+ uint8_t iface_index;
+ int error, ntries;
+
+ device_set_usb_desc(self);
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+
+ RUM_LOCK_INIT(sc);
+ RUM_CMDQ_LOCK_INIT(sc);
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ iface_index = RT2573_IFACE_INDEX;
+ error = usbd_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(self, "could not allocate USB transfers, "
+ "err=%s\n", usbd_errstr(error));
+ goto detach;
+ }
+
+ RUM_LOCK(sc);
+ /* retrieve RT2573 rev. no */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0)
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for chip to settle\n");
+ RUM_UNLOCK(sc);
+ goto detach;
+ }
+
+ /* retrieve MAC address and various other things from EEPROM */
+ rum_read_eeprom(sc);
+
+ device_printf(sc->sc_dev, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n",
+ tmp, rum_get_rf(sc->rf_rev));
+
+ rum_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode));
+ RUM_UNLOCK(sc);
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(self);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode supported */
+ | IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_HOSTAP /* HostAp mode supported */
+ | IEEE80211_C_AHDEMO /* adhoc demo mode */
+ | IEEE80211_C_TXPMGT /* tx power management */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* bg scanning supported */
+ | IEEE80211_C_WPA /* 802.11i */
+ | IEEE80211_C_WME /* 802.11e */
+ | IEEE80211_C_PMGT /* Station-side power mgmt */
+ | IEEE80211_C_SWSLEEP /* net80211 managed power mgmt */
+ ;
+
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
+ rum_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
+ ic->ic_channels);
+
+ ieee80211_ifattach(ic);
+ ic->ic_update_promisc = rum_update_promisc;
+ ic->ic_raw_xmit = rum_raw_xmit;
+ ic->ic_scan_start = rum_scan_start;
+ ic->ic_scan_end = rum_scan_end;
+ ic->ic_set_channel = rum_set_channel;
+ ic->ic_getradiocaps = rum_getradiocaps;
+ ic->ic_transmit = rum_transmit;
+ ic->ic_parent = rum_parent;
+ ic->ic_vap_create = rum_vap_create;
+ ic->ic_vap_delete = rum_vap_delete;
+ ic->ic_updateslot = rum_update_slot;
+ ic->ic_wme.wme_update = rum_wme_update;
+ ic->ic_update_mcast = rum_update_mcast;
+
+ ieee80211_radiotap_attach(ic,
+ &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
+ RT2573_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ RT2573_RX_RADIOTAP_PRESENT);
+
+ TASK_INIT(&sc->cmdq_task, 0, rum_cmdq_cb, sc);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return (0);
+
+detach:
+ rum_detach(self);
+ return (ENXIO); /* failure */
+}
+
+static int
+rum_detach(device_t self)
+{
+ struct rum_softc *sc = device_get_softc(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ /* Prevent further ioctls */
+ RUM_LOCK(sc);
+ sc->sc_detached = 1;
+ RUM_UNLOCK(sc);
+
+ /* stop all USB transfers */
+ usbd_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER);
+
+ /* free TX list, if any */
+ RUM_LOCK(sc);
+ rum_unsetup_tx_list(sc);
+ RUM_UNLOCK(sc);
+
+ if (ic->ic_softc == sc) {
+ ieee80211_draintask(ic, &sc->cmdq_task);
+ ieee80211_ifdetach(ic);
+ }
+
+ mbufq_drain(&sc->sc_snd);
+ RUM_CMDQ_LOCK_DESTROY(sc);
+ RUM_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static usb_error_t
+rum_do_request(struct rum_softc *sc,
+ struct usb_device_request *req, void *data)
+{
+ usb_error_t err;
+ int ntries = 10;
+
+ while (ntries--) {
+ err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+ req, data, 0, NULL, 250 /* ms */);
+ if (err == 0)
+ break;
+
+ DPRINTFN(1, "Control request failed, %s (retrying)\n",
+ usbd_errstr(err));
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ return (err);
+}
+
+static usb_error_t
+rum_do_mcu_request(struct rum_softc *sc, int request)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2573_MCU_CNTL;
+ USETW(req.wValue, request);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ return (rum_do_request(sc, &req, NULL));
+}
+
+static struct ieee80211vap *
+rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct rum_softc *sc = ic->ic_softc;
+ struct rum_vap *rvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ rvp = malloc(sizeof(struct rum_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &rvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) {
+ /* out of memory */
+ free(rvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ /* override state transition machine */
+ rvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = rum_newstate;
+ vap->iv_key_alloc = rum_key_alloc;
+ vap->iv_key_set = rum_key_set;
+ vap->iv_key_delete = rum_key_delete;
+ vap->iv_update_beacon = rum_update_beacon;
+ vap->iv_reset = rum_reset;
+ vap->iv_max_aid = RT2573_ADDR_MAX;
+
+ if (opmode == IEEE80211_M_STA) {
+ /*
+ * Move device to the sleep state when
+ * beacon is received and there is no data for us.
+ *
+ * Used only for IEEE80211_S_SLEEP state.
+ */
+ rvp->recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = rum_sta_recv_mgmt;
+
+ /* Ignored while sleeping. */
+ rvp->bmiss = vap->iv_bmiss;
+ vap->iv_bmiss = rum_beacon_miss;
+ }
+
+ usb_callout_init_mtx(&rvp->ratectl_ch, &sc->sc_mtx, 0);
+ TASK_INIT(&rvp->ratectl_task, 0, rum_ratectl_task, rvp);
+ ieee80211_ratectl_init(vap);
+ ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status, mac);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rum_vap_delete(struct ieee80211vap *vap)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rum_softc *sc = ic->ic_softc;
+
+ /* Put vap into INIT state. */
+ ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
+ ieee80211_draintask(ic, &vap->iv_nstate_task);
+
+ RUM_LOCK(sc);
+ /* Cancel any unfinished Tx. */
+ rum_reset_tx_list(sc, vap);
+ RUM_UNLOCK(sc);
+
+ usb_callout_drain(&rvp->ratectl_ch);
+ ieee80211_draintask(ic, &rvp->ratectl_task);
+ ieee80211_ratectl_deinit(vap);
+ ieee80211_vap_detach(vap);
+ m_freem(rvp->bcn_mbuf);
+ free(rvp, M_80211_VAP);
+}
+
+static void
+rum_cmdq_cb(void *arg, int pending)
+{
+ struct rum_softc *sc = arg;
+ struct rum_cmdq *rc;
+
+ RUM_CMDQ_LOCK(sc);
+ while (sc->cmdq[sc->cmdq_first].func != NULL) {
+ rc = &sc->cmdq[sc->cmdq_first];
+ RUM_CMDQ_UNLOCK(sc);
+
+ RUM_LOCK(sc);
+ rc->func(sc, &rc->data, rc->rvp_id);
+ RUM_UNLOCK(sc);
+
+ RUM_CMDQ_LOCK(sc);
+ memset(rc, 0, sizeof (*rc));
+ sc->cmdq_first = (sc->cmdq_first + 1) % RUM_CMDQ_SIZE;
+ }
+ RUM_CMDQ_UNLOCK(sc);
+}
+
+static int
+rum_cmd_sleepable(struct rum_softc *sc, const void *ptr, size_t len,
+ uint8_t rvp_id, CMD_FUNC_PROTO)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ KASSERT(len <= sizeof(union sec_param), ("buffer overflow"));
+
+ RUM_CMDQ_LOCK(sc);
+ if (sc->cmdq[sc->cmdq_last].func != NULL) {
+ device_printf(sc->sc_dev, "%s: cmdq overflow\n", __func__);
+ RUM_CMDQ_UNLOCK(sc);
+
+ return EAGAIN;
+ }
+
+ if (ptr != NULL)
+ memcpy(&sc->cmdq[sc->cmdq_last].data, ptr, len);
+ sc->cmdq[sc->cmdq_last].rvp_id = rvp_id;
+ sc->cmdq[sc->cmdq_last].func = func;
+ sc->cmdq_last = (sc->cmdq_last + 1) % RUM_CMDQ_SIZE;
+ RUM_CMDQ_UNLOCK(sc);
+
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ return 0;
+}
+
+static void
+rum_tx_free(struct rum_tx_data *data, int txerr)
+{
+ struct rum_softc *sc = data->sc;
+
+ if (data->m != NULL) {
+ ieee80211_tx_complete(data->ni, data->m, txerr);
+ data->m = NULL;
+ data->ni = NULL;
+ }
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+}
+
+static void
+rum_setup_tx_list(struct rum_softc *sc)
+{
+ struct rum_tx_data *data;
+ int i;
+
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ for (i = 0; i < RUM_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ data->sc = sc;
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+ }
+}
+
+static void
+rum_reset_tx_list(struct rum_softc *sc, struct ieee80211vap *vap)
+{
+ struct rum_tx_data *data, *tmp;
+
+ KASSERT(vap != NULL, ("%s: vap is NULL\n", __func__));
+
+ STAILQ_FOREACH_SAFE(data, &sc->tx_q, next, tmp) {
+ if (data->ni != NULL && data->ni->ni_vap == vap) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+
+ KASSERT(data->m != NULL, ("%s: m is NULL\n",
+ __func__));
+ m_freem(data->m);
+ data->m = NULL;
+
+ STAILQ_REMOVE(&sc->tx_q, data, rum_tx_data, next);
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+ }
+ }
+}
+
+static void
+rum_unsetup_tx_list(struct rum_softc *sc)
+{
+ struct rum_tx_data *data;
+ int i;
+
+ /* make sure any subsequent use of the queues will fail */
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ /* free up all node references and mbufs */
+ for (i = 0; i < RUM_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static void
+rum_beacon_miss(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rum_softc *sc = ic->ic_softc;
+ struct rum_vap *rvp = RUM_VAP(vap);
+ int sleep;
+
+ RUM_LOCK(sc);
+ if (sc->sc_sleeping && sc->sc_sleep_end < ticks) {
+ DPRINTFN(12, "dropping 'sleeping' bit, "
+ "device must be awake now\n");
+
+ sc->sc_sleeping = 0;
+ }
+
+ sleep = sc->sc_sleeping;
+ RUM_UNLOCK(sc);
+
+ if (!sleep)
+ rvp->bmiss(vap);
+#ifdef USB_DEBUG
+ else
+ DPRINTFN(13, "bmiss event is ignored whilst sleeping\n");
+#endif
+}
+
+static void
+rum_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
+ const struct ieee80211_rx_stats *rxs,
+ int rssi, int nf)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct rum_softc *sc = vap->iv_ic->ic_softc;
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ if (vap->iv_state == IEEE80211_S_SLEEP &&
+ subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ RUM_LOCK(sc);
+ DPRINTFN(12, "beacon, mybss %d (flags %02X)\n",
+ !!(sc->last_rx_flags & RT2573_RX_MYBSS),
+ sc->last_rx_flags);
+
+ if ((sc->last_rx_flags & (RT2573_RX_MYBSS | RT2573_RX_BC)) ==
+ (RT2573_RX_MYBSS | RT2573_RX_BC)) {
+ /*
+ * Put it to sleep here; in case if there is a data
+ * for us, iv_recv_mgmt() will wakeup the device via
+ * SLEEP -> RUN state transition.
+ */
+ rum_set_power_state(sc, 1);
+ }
+ RUM_UNLOCK(sc);
+ }
+
+ rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);
+}
+
+static int
+rum_set_power_state(struct rum_softc *sc, int sleep)
+{
+ usb_error_t uerror;
+
+ RUM_LOCK_ASSERT(sc);
+
+ DPRINTFN(12, "moving to %s state (sleep time %u)\n",
+ sleep ? "sleep" : "awake", sc->sc_sleep_time);
+
+ uerror = rum_do_mcu_request(sc,
+ sleep ? RT2573_MCU_SLEEP : RT2573_MCU_WAKEUP);
+ if (uerror != USB_ERR_NORMAL_COMPLETION) {
+ device_printf(sc->sc_dev,
+ "%s: could not change power state: %s\n",
+ __func__, usbd_errstr(uerror));
+ return (EIO);
+ }
+
+ sc->sc_sleeping = !!sleep;
+ sc->sc_sleep_end = sleep ? ticks + sc->sc_sleep_time : 0;
+
+ return (0);
+}
+
+static int
+rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rum_softc *sc = ic->ic_softc;
+ const struct ieee80211_txparam *tp;
+ enum ieee80211_state ostate;
+ struct ieee80211_node *ni;
+ usb_error_t uerror;
+ int ret = 0;
+
+ ostate = vap->iv_state;
+ DPRINTF("%s -> %s\n",
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+
+ IEEE80211_UNLOCK(ic);
+ RUM_LOCK(sc);
+ usb_callout_stop(&rvp->ratectl_ch);
+
+ if (ostate == IEEE80211_S_SLEEP && vap->iv_opmode == IEEE80211_M_STA) {
+ rum_clrbits(sc, RT2573_TXRX_CSR4, RT2573_ACKCTS_PWRMGT);
+ rum_clrbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP);
+
+ /*
+ * Ignore any errors;
+ * any subsequent TX will wakeup it anyway
+ */
+ (void) rum_set_power_state(sc, 0);
+ }
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_RUN)
+ rum_abort_tsf_sync(sc);
+
+ break;
+
+ case IEEE80211_S_RUN:
+ if (ostate == IEEE80211_S_SLEEP)
+ break; /* already handled */
+
+ ni = ieee80211_ref_node(vap->iv_bss);
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ if (ic->ic_bsschan == IEEE80211_CHAN_ANYC ||
+ ni->ni_chan == IEEE80211_CHAN_ANYC) {
+ ret = EINVAL;
+ goto run_fail;
+ }
+ rum_update_slot_cb(sc, NULL, 0);
+ rum_enable_mrr(sc);
+ rum_set_txpreamble(sc);
+ rum_set_basicrates(sc);
+ rum_set_maxretry(sc, vap);
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ rum_set_bssid(sc, sc->sc_bssid);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ if ((ret = rum_alloc_beacon(sc, vap)) != 0)
+ goto run_fail;
+ }
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR &&
+ vap->iv_opmode != IEEE80211_M_AHDEMO) {
+ if ((ret = rum_enable_tsf_sync(sc)) != 0)
+ goto run_fail;
+ } else
+ rum_enable_tsf(sc);
+
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ rum_ratectl_start(sc, ni);
+run_fail:
+ ieee80211_free_node(ni);
+ break;
+ case IEEE80211_S_SLEEP:
+ /* Implemented for STA mode only. */
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ break;
+
+ uerror = rum_setbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP);
+ if (uerror != USB_ERR_NORMAL_COMPLETION) {
+ ret = EIO;
+ break;
+ }
+
+ uerror = rum_setbits(sc, RT2573_TXRX_CSR4, RT2573_ACKCTS_PWRMGT);
+ if (uerror != USB_ERR_NORMAL_COMPLETION) {
+ ret = EIO;
+ break;
+ }
+
+ ret = rum_set_power_state(sc, 1);
+ if (ret != 0) {
+ device_printf(sc->sc_dev,
+ "%s: could not move to the SLEEP state: %s\n",
+ __func__, usbd_errstr(uerror));
+ }
+ break;
+ default:
+ break;
+ }
+ RUM_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (ret == 0 ? rvp->newstate(vap, nstate, arg) : ret);
+}
+
+static void
+rum_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rum_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211vap *vap;
+ struct rum_tx_data *data;
+ struct mbuf *m;
+ struct usb_page_cache *pc;
+ unsigned int len;
+ int actlen, sumlen;
+
+ usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete, %d bytes\n", actlen);
+
+ /* free resources */
+ data = usbd_xfer_get_priv(xfer);
+ rum_tx_free(data, 0);
+ usbd_xfer_set_priv(xfer, NULL);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&sc->tx_q);
+ if (data) {
+ STAILQ_REMOVE_HEAD(&sc->tx_q, next);
+ m = data->m;
+
+ if (m->m_pkthdr.len > (int)(MCLBYTES + RT2573_TX_DESC_SIZE)) {
+ DPRINTFN(0, "data overflow, %u bytes\n",
+ m->m_pkthdr.len);
+ m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE);
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &data->desc, RT2573_TX_DESC_SIZE);
+ usbd_m_copy_in(pc, RT2573_TX_DESC_SIZE, m, 0,
+ m->m_pkthdr.len);
+
+ vap = data->ni->ni_vap;
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = data->rate;
+ tap->wt_antenna = sc->tx_ant;
+
+ ieee80211_radiotap_tx(vap, m);
+ }
+
+ /* align end on a 4-bytes boundary */
+ len = (RT2573_TX_DESC_SIZE + m->m_pkthdr.len + 3) & ~3;
+ if ((len % 64) == 0)
+ len += 4;
+
+ DPRINTFN(11, "sending frame len=%u xferlen=%u\n",
+ m->m_pkthdr.len, len);
+
+ usbd_xfer_set_frame_len(xfer, 0, len);
+ usbd_xfer_set_priv(xfer, data);
+
+ usbd_transfer_submit(xfer);
+ }
+ rum_start(sc);
+ break;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ counter_u64_add(sc->sc_ic.ic_oerrors, 1);
+ data = usbd_xfer_get_priv(xfer);
+ if (data != NULL) {
+ rum_tx_free(data, error);
+ usbd_xfer_set_priv(xfer, NULL);
+ }
+
+ if (error != USB_ERR_CANCELLED) {
+ if (error == USB_ERR_TIMEOUT)
+ device_printf(sc->sc_dev, "device timeout\n");
+
+ /*
+ * Try to clear stall first, also if other
+ * errors occur, hence clearing stall
+ * introduces a 50 ms delay:
+ */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+rum_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct rum_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame_min *wh;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL;
+ struct usb_page_cache *pc;
+ uint32_t flags;
+ uint8_t rssi = 0;
+ int len;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTFN(15, "rx done, actlen=%d\n", len);
+
+ if (len < RT2573_RX_DESC_SIZE) {
+ DPRINTF("%s: xfer too short %d\n",
+ device_get_nameunit(sc->sc_dev), len);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+
+ len -= RT2573_RX_DESC_SIZE;
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, &sc->sc_rx_desc, RT2573_RX_DESC_SIZE);
+
+ rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi);
+ flags = le32toh(sc->sc_rx_desc.flags);
+ sc->last_rx_flags = flags;
+ if (len < ((flags >> 16) & 0xfff)) {
+ DPRINTFN(5, "%s: frame is truncated from %d to %d "
+ "bytes\n", device_get_nameunit(sc->sc_dev),
+ (flags >> 16) & 0xfff, len);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ len = (flags >> 16) & 0xfff;
+ if (len < sizeof(struct ieee80211_frame_ack)) {
+ DPRINTFN(5, "%s: frame too short %d\n",
+ device_get_nameunit(sc->sc_dev), len);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ if (flags & RT2573_RX_CRC_ERROR) {
+ /*
+ * This should not happen since we did not
+ * request to receive those frames when we
+ * filled RUM_TXRX_CSR2:
+ */
+ DPRINTFN(5, "PHY or CRC error\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ if ((flags & RT2573_RX_DEC_MASK) != RT2573_RX_DEC_OK) {
+ switch (flags & RT2573_RX_DEC_MASK) {
+ case RT2573_RX_IV_ERROR:
+ DPRINTFN(5, "IV/EIV error\n");
+ break;
+ case RT2573_RX_MIC_ERROR:
+ DPRINTFN(5, "MIC error\n");
+ break;
+ case RT2573_RX_KEY_ERROR:
+ DPRINTFN(5, "Key error\n");
+ break;
+ }
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+
+ m = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ DPRINTF("could not allocate mbuf\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ usbd_copy_out(pc, RT2573_RX_DESC_SIZE,
+ mtod(m, uint8_t *), len);
+
+ wh = mtod(m, struct ieee80211_frame_min *);
+
+ if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
+ (flags & RT2573_RX_CIP_MASK) !=
+ RT2573_RX_CIP_MODE(RT2573_MODE_NOSEC)) {
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+ m->m_flags |= M_WEP;
+ }
+
+ /* finalize mbuf */
+ m->m_pkthdr.len = m->m_len = len;
+
+ if (ieee80211_radiotap_active(ic)) {
+ struct rum_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = 0;
+ tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate,
+ (flags & RT2573_RX_OFDM) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ rum_get_tsf(sc, &tap->wr_tsf);
+ tap->wr_antsignal = RT2573_NOISE_FLOOR + rssi;
+ tap->wr_antnoise = RT2573_NOISE_FLOOR;
+ tap->wr_antenna = sc->rx_ant;
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+
+ /*
+ * At the end of a USB callback it is always safe to unlock
+ * the private mutex of a device! That is why we do the
+ * "ieee80211_input" here, and not some lines up!
+ */
+ RUM_UNLOCK(sc);
+ if (m) {
+ if (m->m_len >= sizeof(struct ieee80211_frame_min))
+ ni = ieee80211_find_rxnode(ic, wh);
+ else
+ ni = NULL;
+
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi,
+ RT2573_NOISE_FLOOR);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi,
+ RT2573_NOISE_FLOOR);
+ }
+ RUM_LOCK(sc);
+ rum_start(sc);
+ return;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static uint8_t
+rum_plcp_signal(int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+
+/*
+ * Map net80211 cipher to RT2573 security mode.
+ */
+static uint8_t
+rum_crypto_mode(struct rum_softc *sc, u_int cipher, int keylen)
+{
+ switch (cipher) {
+ case IEEE80211_CIPHER_WEP:
+ return (keylen < 8 ? RT2573_MODE_WEP40 : RT2573_MODE_WEP104);
+ case IEEE80211_CIPHER_TKIP:
+ return RT2573_MODE_TKIP;
+ case IEEE80211_CIPHER_AES_CCM:
+ return RT2573_MODE_AES_CCMP;
+ default:
+ device_printf(sc->sc_dev, "unknown cipher %d\n", cipher);
+ return 0;
+ }
+}
+
+static void
+rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
+ struct ieee80211_key *k, uint32_t flags, uint8_t xflags, uint8_t qid,
+ int hdrlen, int len, int rate)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct wmeParams *wmep = &sc->wme_params[qid];
+ uint16_t plcp_length;
+ int remainder;
+
+ flags |= RT2573_TX_VALID;
+ flags |= len << 16;
+
+ if (k != NULL && !(k->wk_flags & IEEE80211_KEY_SWCRYPT)) {
+ const struct ieee80211_cipher *cip = k->wk_cipher;
+
+ len += cip->ic_header + cip->ic_trailer + cip->ic_miclen;
+
+ desc->eiv = 0; /* for WEP */
+ cip->ic_setiv(k, (uint8_t *)&desc->iv);
+ }
+
+ /* setup PLCP fields */
+ desc->plcp_signal = rum_plcp_signal(rate);
+ desc->plcp_service = 4;
+
+ len += IEEE80211_CRC_LEN;
+ if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) {
+ flags |= RT2573_TX_OFDM;
+
+ plcp_length = len & 0xfff;
+ desc->plcp_length_hi = plcp_length >> 6;
+ desc->plcp_length_lo = plcp_length & 0x3f;
+ } else {
+ if (rate == 0)
+ rate = 2; /* avoid division by zero */
+ plcp_length = howmany(16 * len, rate);
+ if (rate == 22) {
+ remainder = (16 * len) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= RT2573_PLCP_LENGEXT;
+ }
+ desc->plcp_length_hi = plcp_length >> 8;
+ desc->plcp_length_lo = plcp_length & 0xff;
+
+ if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->plcp_signal |= 0x08;
+ }
+
+ desc->flags = htole32(flags);
+ desc->hdrlen = hdrlen;
+ desc->xflags = xflags;
+
+ desc->wme = htole16(RT2573_QID(qid) |
+ RT2573_AIFSN(wmep->wmep_aifsn) |
+ RT2573_LOGCWMIN(wmep->wmep_logcwmin) |
+ RT2573_LOGCWMAX(wmep->wmep_logcwmax));
+}
+
+static int
+rum_sendprot(struct rum_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct rum_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, pktlen, flags, isshort;
+ uint16_t dur;
+
+ RUM_LOCK_ASSERT(sc);
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(ic->ic_rt, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort)
+ + ieee80211_ack_duration(ic->ic_rt, rate, isshort);
+ flags = 0;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort);
+ flags |= RT2573_TX_NEED_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return (ENOBUFS);
+ }
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ data->rate = protrate;
+ rum_setup_tx_desc(sc, &data->desc, NULL, flags, 0, 0, 0,
+ mprot->m_pkthdr.len, protrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
+
+ return 0;
+}
+
+static uint32_t
+rum_tx_crypto_flags(struct rum_softc *sc, struct ieee80211_node *ni,
+ const struct ieee80211_key *k)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ u_int cipher;
+ uint32_t flags = 0;
+ uint8_t mode, pos;
+
+ if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) {
+ cipher = k->wk_cipher->ic_cipher;
+ pos = k->wk_keyix;
+ mode = rum_crypto_mode(sc, cipher, k->wk_keylen);
+ if (mode == 0)
+ return 0;
+
+ flags |= RT2573_TX_CIP_MODE(mode);
+
+ /* Do not trust GROUP flag */
+ if (!(k >= &vap->iv_nw_keys[0] &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]))
+ flags |= RT2573_TX_KEY_PAIR;
+ else
+ pos += 0 * RT2573_SKEY_MAX; /* vap id */
+
+ flags |= RT2573_TX_KEY_ID(pos);
+
+ if (cipher == IEEE80211_CIPHER_TKIP)
+ flags |= RT2573_TX_TKIPMIC;
+ }
+
+ return flags;
+}
+
+static int
+rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct rum_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k = NULL;
+ uint32_t flags = 0;
+ uint16_t dur;
+ uint8_t ac, type, xflags = 0;
+ int hdrlen;
+
+ RUM_LOCK_ASSERT(sc);
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ hdrlen = ieee80211_anyhdrsize(wh);
+ ac = M_WME_GETAC(m0);
+
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_get_txkey(ni, m0);
+ if (k == NULL)
+ return (ENOENT);
+
+ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+ !k->wk_cipher->ic_encap(k, m0))
+ return (ENOBUFS);
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RT2573_TX_NEED_ACK;
+
+ dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ USETW(wh->i_dur, dur);
+
+ /* tell hardware to add timestamp for probe responses */
+ if (type == IEEE80211_FC0_TYPE_MGT &&
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ flags |= RT2573_TX_TIMESTAMP;
+ }
+
+ if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh))
+ xflags |= RT2573_TX_HWSEQ;
+
+ if (k != NULL)
+ flags |= rum_tx_crypto_flags(sc, ni, k);
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = tp->mgmtrate;
+
+ rum_setup_tx_desc(sc, &data->desc, k, flags, xflags, ac, hdrlen,
+ m0->m_pkthdr.len, tp->mgmtrate);
+
+ DPRINTFN(10, "sending mgt frame len=%d rate=%d\n",
+ m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
+
+ return (0);
+}
+
+static int
+rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ struct rum_tx_data *data;
+ uint32_t flags;
+ uint8_t ac, type, xflags = 0;
+ int rate, error;
+
+ RUM_LOCK_ASSERT(sc);
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+ ac = params->ibp_pri & 3;
+
+ rate = params->ibp_rate0;
+ if (!ieee80211_isratevalid(ic->ic_rt, rate))
+ return (EINVAL);
+
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RT2573_TX_NEED_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = rum_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error || sc->tx_nfree == 0)
+ return (ENOBUFS);
+
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
+
+ if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh))
+ xflags |= RT2573_TX_HWSEQ;
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = rate;
+
+ /* XXX need to setup descriptor ourself */
+ rum_setup_tx_desc(sc, &data->desc, NULL, flags, xflags, ac, 0,
+ m0->m_pkthdr.len, rate);
+
+ DPRINTFN(10, "sending raw frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
+
+ return 0;
+}
+
+static int
+rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct rum_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k = NULL;
+ uint32_t flags = 0;
+ uint16_t dur;
+ uint8_t ac, type, qos, xflags = 0;
+ int error, hdrlen, rate;
+
+ RUM_LOCK_ASSERT(sc);
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ hdrlen = ieee80211_anyhdrsize(wh);
+
+ if (IEEE80211_QOS_HAS_SEQ(wh))
+ qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0];
+ else
+ qos = 0;
+ ac = M_WME_GETAC(m0);
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else if (m0->m_flags & M_EAPOL)
+ rate = tp->mgmtrate;
+ else {
+ (void) ieee80211_ratectl_rate(ni, NULL, 0);
+ rate = ni->ni_txrate;
+ }
+
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_get_txkey(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return (ENOENT);
+ }
+ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+ !k->wk_cipher->ic_encap(k, m0)) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh))
+ xflags |= RT2573_TX_HWSEQ;
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rum_sendprot(sc, m0, ni, prot, rate);
+ if (error || sc->tx_nfree == 0) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
+ }
+
+ if (k != NULL)
+ flags |= rum_tx_crypto_flags(sc, ni, k);
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = rate;
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /* Unicast frame, check if an ACK is expected. */
+ if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) !=
+ IEEE80211_QOS_ACKPOLICY_NOACK)
+ flags |= RT2573_TX_NEED_ACK;
+
+ dur = ieee80211_ack_duration(ic->ic_rt, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ USETW(wh->i_dur, dur);
+ }
+
+ rum_setup_tx_desc(sc, &data->desc, k, flags, xflags, ac, hdrlen,
+ m0->m_pkthdr.len, rate);
+
+ DPRINTFN(10, "sending frame len=%d rate=%d\n",
+ m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
+
+ return 0;
+}
+
+static int
+rum_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct rum_softc *sc = ic->ic_softc;
+ int error;
+
+ RUM_LOCK(sc);
+ if (!sc->sc_running) {
+ RUM_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ RUM_UNLOCK(sc);
+ return (error);
+ }
+ rum_start(sc);
+ RUM_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+rum_start(struct rum_softc *sc)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ RUM_LOCK_ASSERT(sc);
+
+ if (!sc->sc_running)
+ return;
+
+ while (sc->tx_nfree >= RUM_TX_MINFREE &&
+ (m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ if (rum_tx_data(sc, m, ni) != 0) {
+ if_inc_counter(ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ ieee80211_free_node(ni);
+ break;
+ }
+ }
+}
+
+static void
+rum_parent(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_softc;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ RUM_LOCK(sc);
+ if (sc->sc_detached) {
+ RUM_UNLOCK(sc);
+ return;
+ }
+ RUM_UNLOCK(sc);
+
+ if (ic->ic_nrunning > 0) {
+ if (rum_init(sc) == 0)
+ ieee80211_start_all(ic);
+ else
+ ieee80211_stop(vap);
+ } else
+ rum_stop(sc);
+}
+
+static void
+rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2573_READ_EEPROM;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ error = rum_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static uint32_t
+rum_read(struct rum_softc *sc, uint16_t reg)
+{
+ uint32_t val;
+
+ rum_read_multi(sc, reg, &val, sizeof val);
+
+ return le32toh(val);
+}
+
+static void
+rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2573_READ_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = rum_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not multi read MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static usb_error_t
+rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val)
+{
+ uint32_t tmp = htole32(val);
+
+ return (rum_write_multi(sc, reg, &tmp, sizeof tmp));
+}
+
+static usb_error_t
+rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+ size_t offset;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2573_WRITE_MULTI_MAC;
+ USETW(req.wValue, 0);
+
+ /* write at most 64 bytes at a time */
+ for (offset = 0; offset < len; offset += 64) {
+ USETW(req.wIndex, reg + offset);
+ USETW(req.wLength, MIN(len - offset, 64));
+
+ error = rum_do_request(sc, &req, (char *)buf + offset);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not multi write MAC register: %s\n",
+ usbd_errstr(error));
+ return (error);
+ }
+ }
+
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
+static usb_error_t
+rum_setbits(struct rum_softc *sc, uint16_t reg, uint32_t mask)
+{
+ return (rum_write(sc, reg, rum_read(sc, reg) | mask));
+}
+
+static usb_error_t
+rum_clrbits(struct rum_softc *sc, uint16_t reg, uint32_t mask)
+{
+ return (rum_write(sc, reg, rum_read(sc, reg) & ~mask));
+}
+
+static usb_error_t
+rum_modbits(struct rum_softc *sc, uint16_t reg, uint32_t set, uint32_t unset)
+{
+ return (rum_write(sc, reg, (rum_read(sc, reg) & ~unset) | set));
+}
+
+static int
+rum_bbp_busy(struct rum_softc *sc)
+{
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY))
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100)
+ return (ETIMEDOUT);
+
+ return (0);
+}
+
+static void
+rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint32_t tmp;
+
+ DPRINTFN(2, "reg=0x%08x\n", reg);
+
+ if (rum_bbp_busy(sc) != 0) {
+ device_printf(sc->sc_dev, "could not write to BBP\n");
+ return;
+ }
+
+ tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val;
+ rum_write(sc, RT2573_PHY_CSR3, tmp);
+}
+
+static uint8_t
+rum_bbp_read(struct rum_softc *sc, uint8_t reg)
+{
+ uint32_t val;
+ int ntries;
+
+ DPRINTFN(2, "reg=0x%08x\n", reg);
+
+ if (rum_bbp_busy(sc) != 0) {
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+ }
+
+ val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8;
+ rum_write(sc, RT2573_PHY_CSR3, val);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ val = rum_read(sc, RT2573_PHY_CSR3);
+ if (!(val & RT2573_BBP_BUSY))
+ return val & 0xff;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+}
+
+static void
+rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val)
+{
+ uint32_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY))
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not write to RF\n");
+ return;
+ }
+
+ tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 |
+ (reg & 3);
+ rum_write(sc, RT2573_PHY_CSR4, tmp);
+
+ /* remember last written value in sc */
+ sc->rf_regs[reg] = val;
+
+ DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff);
+}
+
+static void
+rum_select_antenna(struct rum_softc *sc)
+{
+ uint8_t bbp4, bbp77;
+ uint32_t tmp;
+
+ bbp4 = rum_bbp_read(sc, 4);
+ bbp77 = rum_bbp_read(sc, 77);
+
+ /* TBD */
+
+ /* make sure Rx is disabled before switching antenna */
+ tmp = rum_read(sc, RT2573_TXRX_CSR0);
+ rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
+
+ rum_bbp_write(sc, 4, bbp4);
+ rum_bbp_write(sc, 77, bbp77);
+
+ rum_write(sc, RT2573_TXRX_CSR0, tmp);
+}
+
+/*
+ * Enable multi-rate retries for frames sent at OFDM rates.
+ * In 802.11b/g mode, allow fallback to CCK rates.
+ */
+static void
+rum_enable_mrr(struct rum_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) {
+ rum_setbits(sc, RT2573_TXRX_CSR4,
+ RT2573_MRR_ENABLED | RT2573_MRR_CCK_FALLBACK);
+ } else {
+ rum_modbits(sc, RT2573_TXRX_CSR4,
+ RT2573_MRR_ENABLED, RT2573_MRR_CCK_FALLBACK);
+ }
+}
+
+static void
+rum_set_txpreamble(struct rum_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ rum_setbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_PREAMBLE);
+ else
+ rum_clrbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_PREAMBLE);
+}
+
+static void
+rum_set_basicrates(struct rum_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ /* update basic rate set */
+ if (ic->ic_curmode == IEEE80211_MODE_11B) {
+ /* 11b basic rates: 1, 2Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0x3);
+ } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) {
+ /* 11a basic rates: 6, 12, 24Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0x150);
+ } else {
+ /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0xf);
+ }
+}
+
+/*
+ * Reprogram MAC/BBP to switch to a new band. Values taken from the reference
+ * driver.
+ */
+static void
+rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c)
+{
+ uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104;
+
+ /* update all BBP registers that depend on the band */
+ bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c;
+ bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48;
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c;
+ bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10;
+ }
+ if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
+ (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
+ bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10;
+ }
+
+ sc->bbp17 = bbp17;
+ rum_bbp_write(sc, 17, bbp17);
+ rum_bbp_write(sc, 96, bbp96);
+ rum_bbp_write(sc, 104, bbp104);
+
+ if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
+ (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
+ rum_bbp_write(sc, 75, 0x80);
+ rum_bbp_write(sc, 86, 0x80);
+ rum_bbp_write(sc, 88, 0x80);
+ }
+
+ rum_bbp_write(sc, 35, bbp35);
+ rum_bbp_write(sc, 97, bbp97);
+ rum_bbp_write(sc, 98, bbp98);
+
+ if (IEEE80211_IS_CHAN_2GHZ(c)) {
+ rum_modbits(sc, RT2573_PHY_CSR0, RT2573_PA_PE_2GHZ,
+ RT2573_PA_PE_5GHZ);
+ } else {
+ rum_modbits(sc, RT2573_PHY_CSR0, RT2573_PA_PE_5GHZ,
+ RT2573_PA_PE_2GHZ);
+ }
+}
+
+static void
+rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ const struct rfprog *rfprog;
+ uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT;
+ int8_t power;
+ int i, chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+ return;
+
+ /* select the appropriate RF settings based on what EEPROM says */
+ rfprog = (sc->rf_rev == RT2573_RF_5225 ||
+ sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rfprog[i].chan != chan; i++);
+
+ power = sc->txpow[i];
+ if (power < 0) {
+ bbp94 += power;
+ power = 0;
+ } else if (power > 31) {
+ bbp94 += power - 31;
+ power = 31;
+ }
+
+ /*
+ * If we are switching from the 2GHz band to the 5GHz band or
+ * vice-versa, BBP registers need to be reprogrammed.
+ */
+ if (c->ic_flags != ic->ic_curchan->ic_flags) {
+ rum_select_band(sc, c);
+ rum_select_antenna(sc);
+ }
+ ic->ic_curchan = c;
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ rum_pause(sc, hz / 100);
+
+ /* enable smart mode for MIMO-capable RFs */
+ bbp3 = rum_bbp_read(sc, 3);
+
+ bbp3 &= ~RT2573_SMART_MODE;
+ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527)
+ bbp3 |= RT2573_SMART_MODE;
+
+ rum_bbp_write(sc, 3, bbp3);
+
+ if (bbp94 != RT2573_BBPR94_DEFAULT)
+ rum_bbp_write(sc, 94, bbp94);
+
+ /* give the chip some extra time to do the switchover */
+ rum_pause(sc, hz / 100);
+}
+
+static void
+rum_set_maxretry(struct rum_softc *sc, struct ieee80211vap *vap)
+{
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ rvp->maxretry = tp->maxretry < 0xf ? tp->maxretry : 0xf;
+
+ rum_modbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_RETRY(rvp->maxretry) |
+ RT2573_LONG_RETRY(rvp->maxretry),
+ RT2573_SHORT_RETRY_MASK | RT2573_LONG_RETRY_MASK);
+}
+
+/*
+ * Enable TSF synchronization and tell h/w to start sending beacons for IBSS
+ * and HostAP operating modes.
+ */
+static int
+rum_enable_tsf_sync(struct rum_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+ uint16_t bintval;
+
+ if (vap->iv_opmode != IEEE80211_M_STA) {
+ /*
+ * Change default 16ms TBTT adjustment to 8ms.
+ * Must be done before enabling beacon generation.
+ */
+ if (rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8) != 0)
+ return EIO;
+ }
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000;
+
+ /* set beacon interval (in 1/16ms unit) */
+ bintval = vap->iv_bss->ni_intval;
+ tmp |= bintval * 16;
+ tmp |= RT2573_TSF_TIMER_EN | RT2573_TBTT_TIMER_EN;
+
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_STA:
+ /*
+ * Local TSF is always updated with remote TSF on beacon
+ * reception.
+ */
+ tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_STA);
+ break;
+ case IEEE80211_M_IBSS:
+ /*
+ * Local TSF is updated with remote TSF on beacon reception
+ * only if the remote TSF is greater than local TSF.
+ */
+ tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_IBSS);
+ tmp |= RT2573_BCN_TX_EN;
+ break;
+ case IEEE80211_M_HOSTAP:
+ /* SYNC with nobody */
+ tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_HOSTAP);
+ tmp |= RT2573_BCN_TX_EN;
+ break;
+ default:
+ device_printf(sc->sc_dev,
+ "Enabling TSF failed. undefined opmode %d\n",
+ vap->iv_opmode);
+ return EINVAL;
+ }
+
+ if (rum_write(sc, RT2573_TXRX_CSR9, tmp) != 0)
+ return EIO;
+
+ /* refresh current sleep time */
+ return (rum_set_sleep_time(sc, bintval));
+}
+
+static void
+rum_enable_tsf(struct rum_softc *sc)
+{
+ rum_modbits(sc, RT2573_TXRX_CSR9, RT2573_TSF_TIMER_EN |
+ RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_DIS), 0x00ffffff);
+}
+
+static void
+rum_abort_tsf_sync(struct rum_softc *sc)
+{
+ rum_clrbits(sc, RT2573_TXRX_CSR9, 0x00ffffff);
+}
+
+static void
+rum_get_tsf(struct rum_softc *sc, uint64_t *buf)
+{
+ rum_read_multi(sc, RT2573_TXRX_CSR12, buf, sizeof (*buf));
+}
+
+static void
+rum_update_slot_cb(struct rum_softc *sc, union sec_param *data, uint8_t rvp_id)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t slottime;
+
+ slottime = IEEE80211_GET_SLOTTIME(ic);
+
+ rum_modbits(sc, RT2573_MAC_CSR9, slottime, 0xff);
+
+ DPRINTF("setting slot time to %uus\n", slottime);
+}
+
+static void
+rum_update_slot(struct ieee80211com *ic)
+{
+ rum_cmd_sleepable(ic->ic_softc, NULL, 0, 0, rum_update_slot_cb);
+}
+
+static int
+rum_wme_update(struct ieee80211com *ic)
+{
+ const struct wmeParams *chanp =
+ ic->ic_wme.wme_chanParams.cap_wmeParams;
+ struct rum_softc *sc = ic->ic_softc;
+ int error = 0;
+
+ RUM_LOCK(sc);
+ error = rum_write(sc, RT2573_AIFSN_CSR,
+ chanp[WME_AC_VO].wmep_aifsn << 12 |
+ chanp[WME_AC_VI].wmep_aifsn << 8 |
+ chanp[WME_AC_BK].wmep_aifsn << 4 |
+ chanp[WME_AC_BE].wmep_aifsn);
+ if (error)
+ goto print_err;
+ error = rum_write(sc, RT2573_CWMIN_CSR,
+ chanp[WME_AC_VO].wmep_logcwmin << 12 |
+ chanp[WME_AC_VI].wmep_logcwmin << 8 |
+ chanp[WME_AC_BK].wmep_logcwmin << 4 |
+ chanp[WME_AC_BE].wmep_logcwmin);
+ if (error)
+ goto print_err;
+ error = rum_write(sc, RT2573_CWMAX_CSR,
+ chanp[WME_AC_VO].wmep_logcwmax << 12 |
+ chanp[WME_AC_VI].wmep_logcwmax << 8 |
+ chanp[WME_AC_BK].wmep_logcwmax << 4 |
+ chanp[WME_AC_BE].wmep_logcwmax);
+ if (error)
+ goto print_err;
+ error = rum_write(sc, RT2573_TXOP01_CSR,
+ chanp[WME_AC_BK].wmep_txopLimit << 16 |
+ chanp[WME_AC_BE].wmep_txopLimit);
+ if (error)
+ goto print_err;
+ error = rum_write(sc, RT2573_TXOP23_CSR,
+ chanp[WME_AC_VO].wmep_txopLimit << 16 |
+ chanp[WME_AC_VI].wmep_txopLimit);
+ if (error)
+ goto print_err;
+
+ memcpy(sc->wme_params, chanp, sizeof(*chanp) * WME_NUM_AC);
+
+print_err:
+ RUM_UNLOCK(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "%s: WME update failed, error %d\n",
+ __func__, error);
+ }
+
+ return (error);
+}
+
+static void
+rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid)
+{
+
+ rum_write(sc, RT2573_MAC_CSR4,
+ bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24);
+ rum_write(sc, RT2573_MAC_CSR5,
+ bssid[4] | bssid[5] << 8 | RT2573_NUM_BSSID_MSK(1));
+}
+
+static void
+rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr)
+{
+
+ rum_write(sc, RT2573_MAC_CSR2,
+ addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
+ rum_write(sc, RT2573_MAC_CSR3,
+ addr[4] | addr[5] << 8 | 0xff << 16);
+}
+
+static void
+rum_setpromisc(struct rum_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ if (ic->ic_promisc == 0)
+ rum_setbits(sc, RT2573_TXRX_CSR0, RT2573_DROP_NOT_TO_ME);
+ else
+ rum_clrbits(sc, RT2573_TXRX_CSR0, RT2573_DROP_NOT_TO_ME);
+
+ DPRINTF("%s promiscuous mode\n", ic->ic_promisc > 0 ?
+ "entering" : "leaving");
+}
+
+static void
+rum_update_promisc(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_softc;
+
+ RUM_LOCK(sc);
+ if (sc->sc_running)
+ rum_setpromisc(sc);
+ RUM_UNLOCK(sc);
+}
+
+static void
+rum_update_mcast(struct ieee80211com *ic)
+{
+ /* Ignore. */
+}
+
+static const char *
+rum_get_rf(int rev)
+{
+ switch (rev) {
+ case RT2573_RF_2527: return "RT2527 (MIMO XR)";
+ case RT2573_RF_2528: return "RT2528";
+ case RT2573_RF_5225: return "RT5225 (MIMO XR)";
+ case RT2573_RF_5226: return "RT5226";
+ default: return "unknown";
+ }
+}
+
+static void
+rum_read_eeprom(struct rum_softc *sc)
+{
+ uint16_t val;
+#ifdef RUM_DEBUG
+ int i;
+#endif
+
+ /* read MAC address */
+ rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_ic.ic_macaddr, 6);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2);
+ val = le16toh(val);
+ sc->rf_rev = (val >> 11) & 0x1f;
+ sc->hw_radio = (val >> 10) & 0x1;
+ sc->rx_ant = (val >> 4) & 0x3;
+ sc->tx_ant = (val >> 2) & 0x3;
+ sc->nb_ant = val & 0x3;
+
+ DPRINTF("RF revision=%d\n", sc->rf_rev);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2);
+ val = le16toh(val);
+ sc->ext_5ghz_lna = (val >> 6) & 0x1;
+ sc->ext_2ghz_lna = (val >> 4) & 0x1;
+
+ DPRINTF("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
+ sc->ext_2ghz_lna, sc->ext_5ghz_lna);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */
+
+ /* Only [-10, 10] is valid */
+ if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10)
+ sc->rssi_2ghz_corr = 0;
+
+ rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */
+
+ /* Only [-10, 10] is valid */
+ if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10)
+ sc->rssi_5ghz_corr = 0;
+
+ if (sc->ext_2ghz_lna)
+ sc->rssi_2ghz_corr -= 14;
+ if (sc->ext_5ghz_lna)
+ sc->rssi_5ghz_corr -= 14;
+
+ DPRINTF("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
+ sc->rssi_2ghz_corr, sc->rssi_5ghz_corr);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rffreq = val & 0xff;
+
+ DPRINTF("RF freq=%d\n", sc->rffreq);
+
+ /* read Tx power for all a/b/g channels */
+ rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14);
+ /* XXX default Tx power for 802.11a channels */
+ memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14);
+#ifdef RUM_DEBUG
+ for (i = 0; i < 14; i++)
+ DPRINTF("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i]);
+#endif
+
+ /* read default values for BBP registers */
+ rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16);
+#ifdef RUM_DEBUG
+ for (i = 0; i < 14; i++) {
+ if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff)
+ continue;
+ DPRINTF("BBP R%d=%02x\n", sc->bbp_prom[i].reg,
+ sc->bbp_prom[i].val);
+ }
+#endif
+}
+
+static int
+rum_bbp_wakeup(struct rum_softc *sc)
+{
+ unsigned int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (rum_read(sc, RT2573_MAC_CSR12) & 8)
+ break;
+ rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
+ return (ETIMEDOUT);
+ }
+
+ return (0);
+}
+
+static int
+rum_bbp_init(struct rum_softc *sc)
+{
+ int i, ntries;
+
+ /* wait for BBP to be ready */
+ for (ntries = 0; ntries < 100; ntries++) {
+ const uint8_t val = rum_bbp_read(sc, 0);
+ if (val != 0 && val != 0xff)
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for BBP\n");
+ return EIO;
+ }
+
+ /* initialize BBP registers to default values */
+ for (i = 0; i < nitems(rum_def_bbp); i++)
+ rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val);
+
+ /* write vendor-specific BBP values (from EEPROM) */
+ for (i = 0; i < 16; i++) {
+ if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff)
+ continue;
+ rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
+ }
+
+ return 0;
+}
+
+static void
+rum_clr_shkey_regs(struct rum_softc *sc)
+{
+ rum_write(sc, RT2573_SEC_CSR0, 0);
+ rum_write(sc, RT2573_SEC_CSR1, 0);
+ rum_write(sc, RT2573_SEC_CSR5, 0);
+}
+
+static int
+rum_init(struct rum_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+ int i, ret;
+
+ RUM_LOCK(sc);
+ if (sc->sc_running) {
+ ret = 0;
+ goto end;
+ }
+
+ /* initialize MAC registers to default values */
+ for (i = 0; i < nitems(rum_def_mac); i++)
+ rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val);
+
+ /* reset some WME parameters to default values */
+ sc->wme_params[0].wmep_aifsn = 2;
+ sc->wme_params[0].wmep_logcwmin = 4;
+ sc->wme_params[0].wmep_logcwmax = 10;
+
+ /* set host ready */
+ rum_write(sc, RT2573_MAC_CSR1, RT2573_RESET_ASIC | RT2573_RESET_BBP);
+ rum_write(sc, RT2573_MAC_CSR1, 0);
+
+ /* wait for BBP/RF to wakeup */
+ if ((ret = rum_bbp_wakeup(sc)) != 0)
+ goto end;
+
+ if ((ret = rum_bbp_init(sc)) != 0)
+ goto end;
+
+ /* select default channel */
+ rum_select_band(sc, ic->ic_curchan);
+ rum_select_antenna(sc);
+ rum_set_chan(sc, ic->ic_curchan);
+
+ /* clear STA registers */
+ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ /* clear security registers (if required) */
+ if (sc->sc_clr_shkeys == 0) {
+ rum_clr_shkey_regs(sc);
+ sc->sc_clr_shkeys = 1;
+ }
+
+ rum_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr);
+
+ /* initialize ASIC */
+ rum_write(sc, RT2573_MAC_CSR1, RT2573_HOST_READY);
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ rum_setup_tx_list(sc);
+
+ /* update Rx filter */
+ tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff;
+
+ tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR;
+ if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR |
+ RT2573_DROP_ACKCTS;
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ tmp |= RT2573_DROP_TODS;
+ if (ic->ic_promisc == 0)
+ tmp |= RT2573_DROP_NOT_TO_ME;
+ }
+ rum_write(sc, RT2573_TXRX_CSR0, tmp);
+
+ sc->sc_running = 1;
+ usbd_xfer_set_stall(sc->sc_xfer[RUM_BULK_WR]);
+ usbd_transfer_start(sc->sc_xfer[RUM_BULK_RD]);
+
+end: RUM_UNLOCK(sc);
+
+ if (ret != 0)
+ rum_stop(sc);
+
+ return ret;
+}
+
+static void
+rum_stop(struct rum_softc *sc)
+{
+
+ RUM_LOCK(sc);
+ if (!sc->sc_running) {
+ RUM_UNLOCK(sc);
+ return;
+ }
+ sc->sc_running = 0;
+ RUM_UNLOCK(sc);
+
+ /*
+ * Drain the USB transfers, if not already drained:
+ */
+ usbd_transfer_drain(sc->sc_xfer[RUM_BULK_WR]);
+ usbd_transfer_drain(sc->sc_xfer[RUM_BULK_RD]);
+
+ RUM_LOCK(sc);
+ rum_unsetup_tx_list(sc);
+
+ /* disable Rx */
+ rum_setbits(sc, RT2573_TXRX_CSR0, RT2573_DISABLE_RX);
+
+ /* reset ASIC */
+ rum_write(sc, RT2573_MAC_CSR1, RT2573_RESET_ASIC | RT2573_RESET_BBP);
+ rum_write(sc, RT2573_MAC_CSR1, 0);
+ RUM_UNLOCK(sc);
+}
+
+static void
+rum_load_microcode(struct rum_softc *sc, const uint8_t *ucode, size_t size)
+{
+ uint16_t reg = RT2573_MCU_CODE_BASE;
+ usb_error_t err;
+
+ /* copy firmware image into NIC */
+ for (; size >= 4; reg += 4, ucode += 4, size -= 4) {
+ err = rum_write(sc, reg, UGETDW(ucode));
+ if (err) {
+ /* firmware already loaded ? */
+ device_printf(sc->sc_dev, "Firmware load "
+ "failure! (ignored)\n");
+ break;
+ }
+ }
+
+ err = rum_do_mcu_request(sc, RT2573_MCU_RUN);
+ if (err != USB_ERR_NORMAL_COMPLETION) {
+ device_printf(sc->sc_dev, "could not run firmware: %s\n",
+ usbd_errstr(err));
+ }
+
+ /* give the chip some time to boot */
+ rum_pause(sc, hz / 8);
+}
+
+static int
+rum_set_sleep_time(struct rum_softc *sc, uint16_t bintval)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ usb_error_t uerror;
+ int exp, delay;
+
+ RUM_LOCK_ASSERT(sc);
+
+ exp = ic->ic_lintval / bintval;
+ delay = ic->ic_lintval % bintval;
+
+ if (exp > RT2573_TBCN_EXP_MAX)
+ exp = RT2573_TBCN_EXP_MAX;
+ if (delay > RT2573_TBCN_DELAY_MAX)
+ delay = RT2573_TBCN_DELAY_MAX;
+
+ uerror = rum_modbits(sc, RT2573_MAC_CSR11,
+ RT2573_TBCN_EXP(exp) |
+ RT2573_TBCN_DELAY(delay),
+ RT2573_TBCN_EXP(RT2573_TBCN_EXP_MAX) |
+ RT2573_TBCN_DELAY(RT2573_TBCN_DELAY_MAX));
+
+ if (uerror != USB_ERR_NORMAL_COMPLETION)
+ return (EIO);
+
+ sc->sc_sleep_time = IEEE80211_TU_TO_TICKS(exp * bintval + delay);
+
+ return (0);
+}
+
+static int
+rum_reset(struct ieee80211vap *vap, u_long cmd)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ struct rum_softc *sc = ic->ic_softc;
+ int error;
+
+ switch (cmd) {
+ case IEEE80211_IOC_POWERSAVE:
+ case IEEE80211_IOC_PROTMODE:
+ case IEEE80211_IOC_RTSTHRESHOLD:
+ error = 0;
+ break;
+ case IEEE80211_IOC_POWERSAVESLEEP:
+ ni = ieee80211_ref_node(vap->iv_bss);
+
+ RUM_LOCK(sc);
+ error = rum_set_sleep_time(sc, ni->ni_intval);
+ if (vap->iv_state == IEEE80211_S_SLEEP) {
+ /* Use new values for wakeup timer. */
+ rum_clrbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP);
+ rum_setbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP);
+ }
+ /* XXX send reassoc */
+ RUM_UNLOCK(sc);
+
+ ieee80211_free_node(ni);
+ break;
+ default:
+ error = ENETRESET;
+ break;
+ }
+
+ return (error);
+}
+
+static int
+rum_set_beacon(struct rum_softc *sc, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct mbuf *m = rvp->bcn_mbuf;
+ const struct ieee80211_txparam *tp;
+ struct rum_tx_desc desc;
+
+ RUM_LOCK_ASSERT(sc);
+
+ if (m == NULL)
+ return EINVAL;
+ if (ic->ic_bsschan == IEEE80211_CHAN_ANYC)
+ return EINVAL;
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ rum_setup_tx_desc(sc, &desc, NULL, RT2573_TX_TIMESTAMP,
+ RT2573_TX_HWSEQ, 0, 0, m->m_pkthdr.len, tp->mgmtrate);
+
+ /* copy the Tx descriptor into NIC memory */
+ if (rum_write_multi(sc, RT2573_HW_BCN_BASE(0), (uint8_t *)&desc,
+ RT2573_TX_DESC_SIZE) != 0)
+ return EIO;
+
+ /* copy beacon header and payload into NIC memory */
+ if (rum_write_multi(sc, RT2573_HW_BCN_BASE(0) + RT2573_TX_DESC_SIZE,
+ mtod(m, uint8_t *), m->m_pkthdr.len) != 0)
+ return EIO;
+
+ return 0;
+}
+
+static int
+rum_alloc_beacon(struct rum_softc *sc, struct ieee80211vap *vap)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct mbuf *m;
+
+ if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+ return EINVAL;
+
+ m = ieee80211_beacon_alloc(ni);
+ if (m == NULL)
+ return ENOMEM;
+
+ if (rvp->bcn_mbuf != NULL)
+ m_freem(rvp->bcn_mbuf);
+
+ rvp->bcn_mbuf = m;
+
+ return (rum_set_beacon(sc, vap));
+}
+
+static void
+rum_update_beacon_cb(struct rum_softc *sc, union sec_param *data,
+ uint8_t rvp_id)
+{
+ struct ieee80211vap *vap = data->vap;
+
+ rum_set_beacon(sc, vap);
+}
+
+static void
+rum_update_beacon(struct ieee80211vap *vap, int item)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rum_softc *sc = ic->ic_softc;
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct mbuf *m = rvp->bcn_mbuf;
+ int mcast = 0;
+
+ RUM_LOCK(sc);
+ if (m == NULL) {
+ m = ieee80211_beacon_alloc(ni);
+ if (m == NULL) {
+ device_printf(sc->sc_dev,
+ "%s: could not allocate beacon frame\n", __func__);
+ RUM_UNLOCK(sc);
+ return;
+ }
+ rvp->bcn_mbuf = m;
+ }
+
+ switch (item) {
+ case IEEE80211_BEACON_ERP:
+ rum_update_slot(ic);
+ break;
+ case IEEE80211_BEACON_TIM:
+ mcast = 1; /*TODO*/
+ break;
+ default:
+ break;
+ }
+ RUM_UNLOCK(sc);
+
+ setbit(bo->bo_flags, item);
+ ieee80211_beacon_update(ni, m, mcast);
+
+ rum_cmd_sleepable(sc, &vap, sizeof(vap), 0, rum_update_beacon_cb);
+}
+
+static int
+rum_common_key_set(struct rum_softc *sc, struct ieee80211_key *k,
+ uint16_t base)
+{
+
+ if (rum_write_multi(sc, base, k->wk_key, k->wk_keylen))
+ return EIO;
+
+ if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) {
+ if (rum_write_multi(sc, base + IEEE80211_KEYBUF_SIZE,
+ k->wk_txmic, 8))
+ return EIO;
+ if (rum_write_multi(sc, base + IEEE80211_KEYBUF_SIZE + 8,
+ k->wk_rxmic, 8))
+ return EIO;
+ }
+
+ return 0;
+}
+
+static void
+rum_group_key_set_cb(struct rum_softc *sc, union sec_param *data,
+ uint8_t rvp_id)
+{
+ struct ieee80211_key *k = &data->key;
+ uint8_t mode;
+
+ if (sc->sc_clr_shkeys == 0) {
+ rum_clr_shkey_regs(sc);
+ sc->sc_clr_shkeys = 1;
+ }
+
+ mode = rum_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen);
+ if (mode == 0)
+ goto print_err;
+
+ DPRINTFN(1, "setting group key %d for vap %d, mode %d "
+ "(tx %s, rx %s)\n", k->wk_keyix, rvp_id, mode,
+ (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off",
+ (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off");
+
+ /* Install the key. */
+ if (rum_common_key_set(sc, k, RT2573_SKEY(rvp_id, k->wk_keyix)) != 0)
+ goto print_err;
+
+ /* Set cipher mode. */
+ if (rum_modbits(sc, rvp_id < 2 ? RT2573_SEC_CSR1 : RT2573_SEC_CSR5,
+ mode << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX,
+ RT2573_MODE_MASK << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX)
+ != 0)
+ goto print_err;
+
+ /* Mark this key as valid. */
+ if (rum_setbits(sc, RT2573_SEC_CSR0,
+ 1 << (rvp_id * RT2573_SKEY_MAX + k->wk_keyix)) != 0)
+ goto print_err;
+
+ return;
+
+print_err:
+ device_printf(sc->sc_dev, "%s: cannot set group key %d for vap %d\n",
+ __func__, k->wk_keyix, rvp_id);
+}
+
+static void
+rum_group_key_del_cb(struct rum_softc *sc, union sec_param *data,
+ uint8_t rvp_id)
+{
+ struct ieee80211_key *k = &data->key;
+
+ DPRINTF("%s: removing group key %d for vap %d\n", __func__,
+ k->wk_keyix, rvp_id);
+ rum_clrbits(sc,
+ rvp_id < 2 ? RT2573_SEC_CSR1 : RT2573_SEC_CSR5,
+ RT2573_MODE_MASK << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX);
+ rum_clrbits(sc, RT2573_SEC_CSR0,
+ rvp_id * RT2573_SKEY_MAX + k->wk_keyix);
+}
+
+static void
+rum_pair_key_set_cb(struct rum_softc *sc, union sec_param *data,
+ uint8_t rvp_id)
+{
+ struct ieee80211_key *k = &data->key;
+ uint8_t buf[IEEE80211_ADDR_LEN + 1];
+ uint8_t mode;
+
+ mode = rum_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen);
+ if (mode == 0)
+ goto print_err;
+
+ DPRINTFN(1, "setting pairwise key %d for vap %d, mode %d "
+ "(tx %s, rx %s)\n", k->wk_keyix, rvp_id, mode,
+ (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off",
+ (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off");
+
+ /* Install the key. */
+ if (rum_common_key_set(sc, k, RT2573_PKEY(k->wk_keyix)) != 0)
+ goto print_err;
+
+ IEEE80211_ADDR_COPY(buf, k->wk_macaddr);
+ buf[IEEE80211_ADDR_LEN] = mode;
+
+ /* Set transmitter address and cipher mode. */
+ if (rum_write_multi(sc, RT2573_ADDR_ENTRY(k->wk_keyix),
+ buf, sizeof buf) != 0)
+ goto print_err;
+
+ /* Enable key table lookup for this vap. */
+ if (sc->vap_key_count[rvp_id]++ == 0)
+ if (rum_setbits(sc, RT2573_SEC_CSR4, 1 << rvp_id) != 0)
+ goto print_err;
+
+ /* Mark this key as valid. */
+ if (rum_setbits(sc,
+ k->wk_keyix < 32 ? RT2573_SEC_CSR2 : RT2573_SEC_CSR3,
+ 1 << (k->wk_keyix % 32)) != 0)
+ goto print_err;
+
+ return;
+
+print_err:
+ device_printf(sc->sc_dev,
+ "%s: cannot set pairwise key %d, vap %d\n", __func__, k->wk_keyix,
+ rvp_id);
+}
+
+static void
+rum_pair_key_del_cb(struct rum_softc *sc, union sec_param *data,
+ uint8_t rvp_id)
+{
+ struct ieee80211_key *k = &data->key;
+
+ DPRINTF("%s: removing key %d\n", __func__, k->wk_keyix);
+ rum_clrbits(sc, (k->wk_keyix < 32) ? RT2573_SEC_CSR2 : RT2573_SEC_CSR3,
+ 1 << (k->wk_keyix % 32));
+ sc->keys_bmap &= ~(1ULL << k->wk_keyix);
+ if (--sc->vap_key_count[rvp_id] == 0)
+ rum_clrbits(sc, RT2573_SEC_CSR4, 1 << rvp_id);
+}
+
+static int
+rum_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
+ ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
+{
+ struct rum_softc *sc = vap->iv_ic->ic_softc;
+ uint8_t i;
+
+ if (!(&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
+ if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) {
+ RUM_LOCK(sc);
+ for (i = 0; i < RT2573_ADDR_MAX; i++) {
+ if ((sc->keys_bmap & (1ULL << i)) == 0) {
+ sc->keys_bmap |= (1ULL << i);
+ *keyix = i;
+ break;
+ }
+ }
+ RUM_UNLOCK(sc);
+ if (i == RT2573_ADDR_MAX) {
+ device_printf(sc->sc_dev,
+ "%s: no free space in the key table\n",
+ __func__);
+ return 0;
+ }
+ } else
+ *keyix = 0;
+ } else {
+ *keyix = ieee80211_crypto_get_key_wepidx(vap, k);
+ }
+ *rxkeyix = *keyix;
+ return 1;
+}
+
+static int
+rum_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+ struct rum_softc *sc = vap->iv_ic->ic_softc;
+ int group;
+
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+ /* Not for us. */
+ return 1;
+ }
+
+ group = k >= &vap->iv_nw_keys[0] && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID];
+
+ return !rum_cmd_sleepable(sc, k, sizeof(*k), 0,
+ group ? rum_group_key_set_cb : rum_pair_key_set_cb);
+}
+
+static int
+rum_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+ struct rum_softc *sc = vap->iv_ic->ic_softc;
+ int group;
+
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+ /* Not for us. */
+ return 1;
+ }
+
+ group = k >= &vap->iv_nw_keys[0] && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID];
+
+ return !rum_cmd_sleepable(sc, k, sizeof(*k), 0,
+ group ? rum_group_key_del_cb : rum_pair_key_del_cb);
+}
+
+static int
+rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct rum_softc *sc = ni->ni_ic->ic_softc;
+ int ret;
+
+ RUM_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if (!sc->sc_running) {
+ ret = ENETDOWN;
+ goto bad;
+ }
+ if (sc->tx_nfree < RUM_TX_MINFREE) {
+ ret = EIO;
+ goto bad;
+ }
+
+ if (params == NULL) {
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ */
+ if ((ret = rum_tx_mgt(sc, m, ni)) != 0)
+ goto bad;
+ } else {
+ /*
+ * Caller supplied explicit parameters to use in
+ * sending the frame.
+ */
+ if ((ret = rum_tx_raw(sc, m, ni, params)) != 0)
+ goto bad;
+ }
+ RUM_UNLOCK(sc);
+
+ return 0;
+bad:
+ RUM_UNLOCK(sc);
+ m_freem(m);
+ return ret;
+}
+
+static void
+rum_ratectl_start(struct rum_softc *sc, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR5) */
+ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ usb_callout_reset(&rvp->ratectl_ch, hz, rum_ratectl_timeout, rvp);
+}
+
+static void
+rum_ratectl_timeout(void *arg)
+{
+ struct rum_vap *rvp = arg;
+ struct ieee80211vap *vap = &rvp->vap;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ ieee80211_runtask(ic, &rvp->ratectl_task);
+}
+
+static void
+rum_ratectl_task(void *arg, int pending)
+{
+ struct rum_vap *rvp = arg;
+ struct ieee80211vap *vap = &rvp->vap;
+ struct rum_softc *sc = vap->iv_ic->ic_softc;
+ struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs;
+ int ok[3], fail;
+
+ RUM_LOCK(sc);
+ /* read and clear statistic registers (STA_CSR0 to STA_CSR5) */
+ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof(sc->sta));
+
+ ok[0] = (le32toh(sc->sta[4]) & 0xffff); /* TX ok w/o retry */
+ ok[1] = (le32toh(sc->sta[4]) >> 16); /* TX ok w/ one retry */
+ ok[2] = (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ multiple retries */
+ fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */
+
+ txs->flags = IEEE80211_RATECTL_TX_STATS_RETRIES;
+ txs->nframes = ok[0] + ok[1] + ok[2] + fail;
+ txs->nsuccess = txs->nframes - fail;
+ /* XXX at least */
+ txs->nretries = ok[1] + ok[2] * 2 + fail * (rvp->maxretry + 1);
+
+ if (txs->nframes != 0)
+ ieee80211_ratectl_tx_update(vap, txs);
+
+ /* count TX retry-fail as Tx errors */
+ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, fail);
+
+ usb_callout_reset(&rvp->ratectl_ch, hz, rum_ratectl_timeout, rvp);
+ RUM_UNLOCK(sc);
+}
+
+static void
+rum_scan_start(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_softc;
+
+ RUM_LOCK(sc);
+ rum_abort_tsf_sync(sc);
+ rum_set_bssid(sc, ieee80211broadcastaddr);
+ RUM_UNLOCK(sc);
+
+}
+
+static void
+rum_scan_end(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_softc;
+
+ if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) {
+ RUM_LOCK(sc);
+ if (ic->ic_opmode != IEEE80211_M_AHDEMO)
+ rum_enable_tsf_sync(sc);
+ else
+ rum_enable_tsf(sc);
+ rum_set_bssid(sc, sc->sc_bssid);
+ RUM_UNLOCK(sc);
+ }
+}
+
+static void
+rum_set_channel(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_softc;
+
+ RUM_LOCK(sc);
+ rum_set_chan(sc, ic->ic_curchan);
+ RUM_UNLOCK(sc);
+}
+
+static void
+rum_getradiocaps(struct ieee80211com *ic,
+ int maxchans, int *nchans, struct ieee80211_channel chans[])
+{
+ struct rum_softc *sc = ic->ic_softc;
+ uint8_t bands[IEEE80211_MODE_BYTES];
+
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ ieee80211_add_channel_list_2ghz(chans, maxchans, nchans,
+ rum_chan_2ghz, nitems(rum_chan_2ghz), bands, 0);
+
+ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) {
+ setbit(bands, IEEE80211_MODE_11A);
+ ieee80211_add_channel_list_5ghz(chans, maxchans, nchans,
+ rum_chan_5ghz, nitems(rum_chan_5ghz), bands, 0);
+ }
+}
+
+static int
+rum_get_rssi(struct rum_softc *sc, uint8_t raw)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ int lna, agc, rssi;
+
+ lna = (raw >> 5) & 0x3;
+ agc = raw & 0x1f;
+
+ if (lna == 0) {
+ /*
+ * No RSSI mapping
+ *
+ * NB: Since RSSI is relative to noise floor, -1 is
+ * adequate for caller to know error happened.
+ */
+ return -1;
+ }
+
+ rssi = (2 * agc) - RT2573_NOISE_FLOOR;
+
+ if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
+ rssi += sc->rssi_2ghz_corr;
+
+ if (lna == 1)
+ rssi -= 64;
+ else if (lna == 2)
+ rssi -= 74;
+ else if (lna == 3)
+ rssi -= 90;
+ } else {
+ rssi += sc->rssi_5ghz_corr;
+
+ if (!sc->ext_5ghz_lna && lna != 1)
+ rssi += 4;
+
+ if (lna == 1)
+ rssi -= 64;
+ else if (lna == 2)
+ rssi -= 86;
+ else if (lna == 3)
+ rssi -= 100;
+ }
+ return rssi;
+}
+
+static int
+rum_pause(struct rum_softc *sc, int timeout)
+{
+
+ usb_pause_mtx(&sc->sc_mtx, timeout);
+ return (0);
+}
+
+static device_method_t rum_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rum_match),
+ DEVMETHOD(device_attach, rum_attach),
+ DEVMETHOD(device_detach, rum_detach),
+ DEVMETHOD_END
+};
+
+static driver_t rum_driver = {
+ .name = "rum",
+ .methods = rum_methods,
+ .size = sizeof(struct rum_softc),
+};
+
+static devclass_t rum_devclass;
+
+DRIVER_MODULE(rum, uhub, rum_driver, rum_devclass, NULL, 0);
+MODULE_DEPEND(rum, wlan, 1, 1, 1);
+MODULE_DEPEND(rum, usb, 1, 1, 1);
+MODULE_VERSION(rum, 1);
+USB_PNP_HOST_INFO(rum_devs);
diff --git a/freebsd/sys/dev/usb/wlan/if_rumfw.h b/freebsd/sys/dev/usb/wlan/if_rumfw.h
new file mode 100644
index 00000000..0f086744
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_rumfw.h
@@ -0,0 +1,213 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005-2006, Ralink Technology, Corp.
+ * Paul Lin <paul_lin@ralinktech.com.tw>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This file contains the loadable 8051 microcode for the Ralink RT2573
+ * chipset.
+ */
+
+static const uint8_t rt2573_ucode[] = {
+ 0x02, 0x13, 0x25, 0x12, 0x10, 0xd9, 0x02, 0x12, 0x58, 0x02, 0x13,
+ 0x58, 0x02, 0x13, 0x5a, 0xc0, 0xd0, 0x75, 0xd0, 0x18, 0x12, 0x13,
+ 0x5c, 0xd0, 0xd0, 0x22, 0x02, 0x14, 0x5c, 0x02, 0x14, 0xe7, 0xed,
+ 0x4c, 0x70, 0x44, 0x90, 0x01, 0xa8, 0x74, 0x80, 0xf0, 0xef, 0x30,
+ 0xe5, 0x07, 0xe4, 0x90, 0x00, 0x0f, 0xf0, 0x80, 0x2c, 0xe5, 0x40,
+ 0x24, 0xc0, 0x60, 0x13, 0x24, 0xc0, 0x60, 0x16, 0x24, 0xc0, 0x60,
+ 0x19, 0x24, 0xc0, 0x70, 0x1a, 0xe4, 0x90, 0x00, 0x0b, 0xf0, 0x80,
+ 0x13, 0xe4, 0x90, 0x00, 0x13, 0xf0, 0x80, 0x0c, 0xe4, 0x90, 0x00,
+ 0x1b, 0xf0, 0x80, 0x05, 0xe4, 0x90, 0x00, 0x23, 0xf0, 0xe4, 0x90,
+ 0x01, 0xa8, 0xf0, 0xd3, 0x22, 0x90, 0x02, 0x02, 0xed, 0xf0, 0x90,
+ 0x02, 0x01, 0xef, 0xf0, 0xd3, 0x22, 0xef, 0x24, 0xc0, 0x60, 0x1f,
+ 0x24, 0xc0, 0x60, 0x2e, 0x24, 0xc0, 0x60, 0x3d, 0x24, 0xc0, 0x70,
+ 0x53, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90,
+ 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00, 0x08, 0x80, 0x37, 0x90, 0x00,
+ 0x13, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x11, 0xe0,
+ 0xfe, 0x90, 0x00, 0x10, 0x80, 0x24, 0x90, 0x00, 0x1b, 0xe0, 0x30,
+ 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00,
+ 0x18, 0x80, 0x11, 0x90, 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x02, 0xc3,
+ 0x22, 0x90, 0x00, 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd,
+ 0xee, 0xf5, 0x37, 0xed, 0xf5, 0x38, 0xd3, 0x22, 0x30, 0x09, 0x20,
+ 0x20, 0x04, 0x0b, 0x90, 0x02, 0x08, 0xe0, 0x54, 0x0f, 0x70, 0x03,
+ 0x02, 0x12, 0x57, 0xc2, 0x09, 0x90, 0x02, 0x00, 0xe0, 0x44, 0x04,
+ 0xf0, 0x74, 0x04, 0x12, 0x0c, 0x3a, 0xc2, 0x04, 0xc2, 0x07, 0x90,
+ 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03,
+ 0x26, 0xe0, 0x20, 0xe2, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x08,
+ 0xe0, 0x70, 0x1b, 0x20, 0x07, 0x03, 0x02, 0x12, 0x57, 0x90, 0x03,
+ 0x12, 0xe0, 0x64, 0x22, 0x60, 0x03, 0x02, 0x12, 0x57, 0xd2, 0x09,
+ 0xc2, 0x07, 0x74, 0x02, 0x12, 0x0c, 0x3a, 0x22, 0x90, 0x02, 0x03,
+ 0xe0, 0x30, 0xe4, 0x47, 0x20, 0x06, 0x44, 0xe5, 0x3c, 0x60, 0x34,
+ 0xe5, 0x40, 0x24, 0xc0, 0x60, 0x14, 0x24, 0xc0, 0x60, 0x18, 0x24,
+ 0xc0, 0x60, 0x1c, 0x24, 0xc0, 0x70, 0x22, 0x90, 0x00, 0x0b, 0xe0,
+ 0x30, 0xe1, 0x1b, 0x22, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x13,
+ 0x22, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x0b, 0x22, 0x90, 0x00,
+ 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x03,
+ 0x74, 0x01, 0xf0, 0x00, 0xe0, 0x54, 0xc0, 0xf5, 0x40, 0xe5, 0x40,
+ 0x24, 0xc0, 0x60, 0x20, 0x24, 0xc0, 0x60, 0x30, 0x24, 0xc0, 0x60,
+ 0x40, 0x24, 0xc0, 0x70, 0x56, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1,
+ 0x03, 0x02, 0x12, 0x57, 0x90, 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00,
+ 0x08, 0x80, 0x3a, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x03, 0x02,
+ 0x12, 0x57, 0x90, 0x00, 0x11, 0xe0, 0xfe, 0x90, 0x00, 0x10, 0x80,
+ 0x26, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57,
+ 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00, 0x18, 0x80, 0x12, 0x90,
+ 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x00,
+ 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd, 0xee, 0xf5, 0x37,
+ 0xed, 0xf5, 0x38, 0x90, 0x03, 0x27, 0x74, 0x82, 0xf0, 0x90, 0x02,
+ 0x01, 0xe5, 0x40, 0xf0, 0x90, 0x02, 0x06, 0xe0, 0xf5, 0x3c, 0xc3,
+ 0xe5, 0x38, 0x95, 0x3a, 0xe5, 0x37, 0x95, 0x39, 0x50, 0x21, 0xe5,
+ 0x40, 0x44, 0x05, 0xff, 0xe5, 0x37, 0xa2, 0xe7, 0x13, 0xfc, 0xe5,
+ 0x38, 0x13, 0xfd, 0x12, 0x10, 0x20, 0xe5, 0x3c, 0x30, 0xe2, 0x04,
+ 0xd2, 0x06, 0x80, 0x02, 0xc2, 0x06, 0x53, 0x3c, 0x01, 0x22, 0x30,
+ 0x0b, 0x07, 0xe4, 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02,
+ 0x02, 0x74, 0x20, 0xf0, 0xe5, 0x40, 0x44, 0x01, 0x90, 0x02, 0x01,
+ 0xf0, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6,
+ 0x90, 0x03, 0x27, 0x74, 0x02, 0xf0, 0xaf, 0x40, 0x12, 0x10, 0x74,
+ 0x40, 0xa5, 0x00, 0x80, 0xf6, 0x22, 0x90, 0x7f, 0xf8, 0xe0, 0xb4,
+ 0x02, 0x03, 0x12, 0x16, 0x38, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0,
+ 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03, 0x26, 0xe0, 0x20, 0xe1, 0x07,
+ 0xe5, 0x3b, 0x70, 0x03, 0x02, 0x13, 0x24, 0xe5, 0x3b, 0x70, 0x15,
+ 0x90, 0x03, 0x24, 0xe0, 0x75, 0xf0, 0x40, 0xa4, 0xf5, 0x36, 0x85,
+ 0xf0, 0x35, 0x75, 0x24, 0x83, 0x75, 0x3b, 0x01, 0x80, 0x03, 0x75,
+ 0x24, 0x03, 0xd3, 0xe5, 0x36, 0x95, 0x3a, 0xe5, 0x35, 0x95, 0x39,
+ 0x40, 0x36, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80,
+ 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0,
+ 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x30, 0x0b, 0x07, 0xe4,
+ 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02, 0x02, 0x74, 0x20,
+ 0xf0, 0x90, 0x02, 0x01, 0x74, 0x21, 0xf0, 0x75, 0x24, 0x03, 0x80,
+ 0x3d, 0xe5, 0x35, 0xa2, 0xe7, 0x13, 0xfe, 0xe5, 0x36, 0x13, 0xfd,
+ 0xac, 0x06, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80,
+ 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0,
+ 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x7f, 0x25, 0x12, 0x10,
+ 0x20, 0xe5, 0x36, 0xb5, 0x3a, 0x08, 0xe5, 0x35, 0xb5, 0x39, 0x03,
+ 0x00, 0x80, 0x04, 0xe4, 0xf5, 0x3b, 0x22, 0xc3, 0xe5, 0x36, 0x95,
+ 0x3a, 0xf5, 0x36, 0xe5, 0x35, 0x95, 0x39, 0xf5, 0x35, 0x02, 0x12,
+ 0x96, 0x22, 0x75, 0xa8, 0x0f, 0x90, 0x03, 0x06, 0x74, 0x01, 0xf0,
+ 0x90, 0x03, 0x07, 0xf0, 0x90, 0x03, 0x08, 0x04, 0xf0, 0x90, 0x03,
+ 0x09, 0x74, 0x6c, 0xf0, 0x90, 0x03, 0x0a, 0x74, 0xff, 0xf0, 0x90,
+ 0x03, 0x02, 0x74, 0x1f, 0xf0, 0x90, 0x03, 0x00, 0x74, 0x04, 0xf0,
+ 0x90, 0x03, 0x25, 0x74, 0x31, 0xf0, 0xd2, 0xaf, 0x22, 0x00, 0x22,
+ 0x00, 0x22, 0x90, 0x03, 0x05, 0xe0, 0x30, 0xe0, 0x0b, 0xe0, 0x44,
+ 0x01, 0xf0, 0x30, 0x09, 0x02, 0xd2, 0x04, 0xc2, 0x07, 0x22, 0x8d,
+ 0x24, 0xa9, 0x07, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5,
+ 0x26, 0xa3, 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff,
+ 0xa3, 0xe0, 0xfd, 0xe9, 0x30, 0xe5, 0x14, 0x54, 0xc0, 0x60, 0x05,
+ 0x43, 0x05, 0x03, 0x80, 0x03, 0x53, 0x05, 0xfc, 0xef, 0x54, 0x3f,
+ 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x07, 0x3f, 0x53, 0x05, 0xf0,
+ 0xe5, 0x24, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53,
+ 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28,
+ 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x8f, 0x24, 0xa9,
+ 0x05, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5, 0x26, 0xa3,
+ 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff, 0xa3, 0xe0,
+ 0xfd, 0xe5, 0x24, 0x30, 0xe5, 0x0b, 0x43, 0x05, 0x0f, 0xef, 0x54,
+ 0x3f, 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x05, 0xf0, 0x53, 0x07,
+ 0x3f, 0xe9, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53,
+ 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28,
+ 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x90, 0x7f, 0xfc,
+ 0xe0, 0xf9, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfb,
+ 0xef, 0x30, 0xe5, 0x0b, 0x43, 0x03, 0x0f, 0xec, 0x54, 0x3f, 0x44,
+ 0x40, 0xfc, 0x80, 0x06, 0x53, 0x03, 0xf0, 0x53, 0x04, 0x3f, 0xed,
+ 0x30, 0xe0, 0x07, 0xef, 0x54, 0xc0, 0x60, 0x07, 0x80, 0x0a, 0xef,
+ 0x54, 0xc0, 0x60, 0x05, 0x43, 0x03, 0x10, 0x80, 0x03, 0x53, 0x03,
+ 0xef, 0x90, 0x7f, 0xfc, 0xe9, 0xf0, 0xa3, 0xee, 0xf0, 0xa3, 0xec,
+ 0xf0, 0xa3, 0xeb, 0xf0, 0x22, 0xe5, 0x4b, 0xfd, 0x54, 0x1f, 0x90,
+ 0x7f, 0xf8, 0xf0, 0xe5, 0x4a, 0xf5, 0x09, 0x90, 0x30, 0x38, 0xe0,
+ 0x90, 0x7f, 0xfc, 0xf0, 0x90, 0x30, 0x39, 0xe0, 0x90, 0x7f, 0xfd,
+ 0xf0, 0x90, 0x30, 0x3a, 0xe0, 0x90, 0x7f, 0xfe, 0xf0, 0x90, 0x30,
+ 0x3b, 0xe0, 0x90, 0x7f, 0xff, 0xf0, 0xed, 0x30, 0xe5, 0x0c, 0x54,
+ 0xc0, 0x60, 0x0d, 0x90, 0x7f, 0xf0, 0xe5, 0x47, 0xf0, 0x80, 0x05,
+ 0xe4, 0x90, 0x7f, 0xf0, 0xf0, 0x90, 0x7f, 0xf8, 0xe0, 0x14, 0x60,
+ 0x08, 0x24, 0xfe, 0x60, 0x0d, 0x24, 0x03, 0x80, 0x12, 0xaf, 0x05,
+ 0xad, 0x09, 0x12, 0x13, 0xc5, 0x80, 0x10, 0xaf, 0x05, 0xad, 0x09,
+ 0x12, 0x14, 0x12, 0x80, 0x07, 0xaf, 0x05, 0xad, 0x09, 0x12, 0x13,
+ 0x6f, 0x90, 0x7f, 0xfc, 0xe0, 0x90, 0x30, 0x38, 0xf0, 0x90, 0x7f,
+ 0xfd, 0xe0, 0x90, 0x30, 0x39, 0xf0, 0x90, 0x7f, 0xfe, 0xe0, 0x90,
+ 0x30, 0x3a, 0xf0, 0x90, 0x7f, 0xff, 0xe0, 0x90, 0x30, 0x3b, 0xf0,
+ 0x22, 0xe5, 0x4b, 0x64, 0x01, 0x60, 0x03, 0x02, 0x15, 0x71, 0xf5,
+ 0x4b, 0xe5, 0x44, 0x45, 0x43, 0x70, 0x03, 0x02, 0x15, 0xa0, 0x12,
+ 0x0c, 0x14, 0x12, 0x0b, 0x86, 0x50, 0xfb, 0x90, 0x00, 0x00, 0xe0,
+ 0xf5, 0x25, 0x12, 0x15, 0xb4, 0xc2, 0x92, 0xe4, 0xf5, 0x24, 0xe5,
+ 0x24, 0xc3, 0x95, 0x25, 0x50, 0x49, 0x7e, 0x00, 0x7f, 0x4c, 0x74,
+ 0x40, 0x25, 0x24, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xad, 0x82, 0xfc,
+ 0x75, 0x2b, 0x02, 0x7b, 0x10, 0x12, 0x07, 0x1e, 0xc2, 0x93, 0x12,
+ 0x15, 0xa1, 0x7d, 0xa0, 0x12, 0x15, 0xd0, 0xe5, 0x24, 0x54, 0x0f,
+ 0x24, 0x4c, 0xf8, 0xe6, 0xfd, 0xaf, 0x4b, 0xae, 0x4a, 0x12, 0x15,
+ 0xd8, 0x05, 0x4b, 0xe5, 0x4b, 0x70, 0x02, 0x05, 0x4a, 0x12, 0x0a,
+ 0x5f, 0x05, 0x24, 0xe5, 0x24, 0x54, 0x0f, 0x70, 0xd5, 0xd2, 0x93,
+ 0x80, 0xb0, 0xc3, 0xe5, 0x44, 0x95, 0x25, 0xf5, 0x44, 0xe5, 0x43,
+ 0x94, 0x00, 0xf5, 0x43, 0x02, 0x14, 0xf2, 0x12, 0x15, 0xb4, 0xc2,
+ 0x93, 0xc2, 0x92, 0x12, 0x15, 0xa1, 0x7d, 0x80, 0x12, 0x15, 0xd0,
+ 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55,
+ 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x7d, 0x30, 0xaf, 0x4b,
+ 0xae, 0x4a, 0x12, 0x15, 0xd8, 0x12, 0x0a, 0x5f, 0xd2, 0x93, 0x22,
+ 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55,
+ 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x22, 0xad, 0x47, 0x7f,
+ 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0x7d, 0xff, 0x7f, 0x35, 0x7e,
+ 0x30, 0x12, 0x15, 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x7e, 0x30, 0x12,
+ 0x15, 0xd8, 0x22, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x22,
+ 0x8f, 0x82, 0x8e, 0x83, 0xed, 0xf0, 0x22, 0xe4, 0xfc, 0x90, 0x7f,
+ 0xf0, 0xe0, 0xaf, 0x09, 0x14, 0x60, 0x14, 0x14, 0x60, 0x15, 0x14,
+ 0x60, 0x16, 0x14, 0x60, 0x17, 0x14, 0x60, 0x18, 0x24, 0x05, 0x70,
+ 0x16, 0xe4, 0xfc, 0x80, 0x12, 0x7c, 0x01, 0x80, 0x0e, 0x7c, 0x03,
+ 0x80, 0x0a, 0x7c, 0x07, 0x80, 0x06, 0x7c, 0x0f, 0x80, 0x02, 0x7c,
+ 0x1f, 0xec, 0x6f, 0xf4, 0x54, 0x1f, 0xfc, 0x90, 0x30, 0x34, 0xe0,
+ 0x54, 0xe0, 0x4c, 0xfd, 0xa3, 0xe0, 0xfc, 0x43, 0x04, 0x1f, 0x7f,
+ 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0xad, 0x04, 0x0f, 0x12, 0x15,
+ 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x02, 0x15, 0xd8, 0x02, 0x15, 0xdf,
+ 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, 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, 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, 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, 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, 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, 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, 0x01, 0x07,
+ 0x29, 0xe9
+};
diff --git a/freebsd/sys/dev/usb/wlan/if_rumreg.h b/freebsd/sys/dev/usb/wlan/if_rumreg.h
new file mode 100644
index 00000000..96726b14
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_rumreg.h
@@ -0,0 +1,307 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RT2573_NOISE_FLOOR -95
+
+#define RT2573_TX_DESC_SIZE (sizeof (struct rum_tx_desc))
+#define RT2573_RX_DESC_SIZE (sizeof (struct rum_rx_desc))
+
+#define RT2573_CONFIG_NO 1
+#define RT2573_IFACE_INDEX 0
+
+#define RT2573_MCU_CNTL 0x01
+#define RT2573_WRITE_MAC 0x02
+#define RT2573_READ_MAC 0x03
+#define RT2573_WRITE_MULTI_MAC 0x06
+#define RT2573_READ_MULTI_MAC 0x07
+#define RT2573_READ_EEPROM 0x09
+#define RT2573_WRITE_LED 0x0a
+
+/*
+ * WME registers.
+ */
+#define RT2573_AIFSN_CSR 0x0400
+#define RT2573_CWMIN_CSR 0x0404
+#define RT2573_CWMAX_CSR 0x0408
+#define RT2573_TXOP01_CSR 0x040C
+#define RT2573_TXOP23_CSR 0x0410
+#define RT2573_MCU_CODE_BASE 0x0800
+
+/*
+ * H/w encryption/decryption support
+ */
+#define KEY_SIZE (IEEE80211_KEYBUF_SIZE + IEEE80211_MICBUF_SIZE)
+#define RT2573_ADDR_MAX 64
+#define RT2573_SKEY_MAX 4
+
+#define RT2573_SKEY(vap, kidx) (0x1000 + ((vap) * RT2573_SKEY_MAX + \
+ (kidx)) * KEY_SIZE)
+#define RT2573_PKEY(id) (0x1200 + (id) * KEY_SIZE)
+
+#define RT2573_ADDR_ENTRY(id) (0x1a00 + (id) * 8)
+
+/*
+ * Shared memory area
+ */
+#define RT2573_HW_BCN_BASE(id) (0x2400 + (id) * 0x100)
+
+/*
+ * Control and status registers.
+ */
+#define RT2573_MAC_CSR0 0x3000
+#define RT2573_MAC_CSR1 0x3004
+#define RT2573_MAC_CSR2 0x3008
+#define RT2573_MAC_CSR3 0x300c
+#define RT2573_MAC_CSR4 0x3010
+#define RT2573_MAC_CSR5 0x3014
+#define RT2573_MAC_CSR6 0x3018
+#define RT2573_MAC_CSR7 0x301c
+#define RT2573_MAC_CSR8 0x3020
+#define RT2573_MAC_CSR9 0x3024
+#define RT2573_MAC_CSR10 0x3028
+#define RT2573_MAC_CSR11 0x302c
+#define RT2573_MAC_CSR12 0x3030
+#define RT2573_MAC_CSR13 0x3034
+#define RT2573_MAC_CSR14 0x3038
+#define RT2573_MAC_CSR15 0x303c
+#define RT2573_TXRX_CSR0 0x3040
+#define RT2573_TXRX_CSR1 0x3044
+#define RT2573_TXRX_CSR2 0x3048
+#define RT2573_TXRX_CSR3 0x304c
+#define RT2573_TXRX_CSR4 0x3050
+#define RT2573_TXRX_CSR5 0x3054
+#define RT2573_TXRX_CSR6 0x3058
+#define RT2573_TXRX_CSR7 0x305c
+#define RT2573_TXRX_CSR8 0x3060
+#define RT2573_TXRX_CSR9 0x3064
+#define RT2573_TXRX_CSR10 0x3068
+#define RT2573_TXRX_CSR11 0x306c
+#define RT2573_TXRX_CSR12 0x3070
+#define RT2573_TXRX_CSR13 0x3074
+#define RT2573_TXRX_CSR14 0x3078
+#define RT2573_TXRX_CSR15 0x307c
+#define RT2573_PHY_CSR0 0x3080
+#define RT2573_PHY_CSR1 0x3084
+#define RT2573_PHY_CSR2 0x3088
+#define RT2573_PHY_CSR3 0x308c
+#define RT2573_PHY_CSR4 0x3090
+#define RT2573_PHY_CSR5 0x3094
+#define RT2573_PHY_CSR6 0x3098
+#define RT2573_PHY_CSR7 0x309c
+#define RT2573_SEC_CSR0 0x30a0
+#define RT2573_SEC_CSR1 0x30a4
+#define RT2573_SEC_CSR2 0x30a8
+#define RT2573_SEC_CSR3 0x30ac
+#define RT2573_SEC_CSR4 0x30b0
+#define RT2573_SEC_CSR5 0x30b4
+#define RT2573_STA_CSR0 0x30c0
+#define RT2573_STA_CSR1 0x30c4
+#define RT2573_STA_CSR2 0x30c8
+#define RT2573_STA_CSR3 0x30cc
+#define RT2573_STA_CSR4 0x30d0
+#define RT2573_STA_CSR5 0x30d4
+
+
+/* possible values for register RT2573_ADDR_MODE */
+#define RT2573_MODE_MASK 0x7
+#define RT2573_MODE_NOSEC 0
+#define RT2573_MODE_WEP40 1
+#define RT2573_MODE_WEP104 2
+#define RT2573_MODE_TKIP 3
+#define RT2573_MODE_AES_CCMP 4
+#define RT2573_MODE_CKIP40 5
+#define RT2573_MODE_CKIP104 6
+
+/* possible flags for register RT2573_MAC_CSR1 */
+#define RT2573_RESET_ASIC (1 << 0)
+#define RT2573_RESET_BBP (1 << 1)
+#define RT2573_HOST_READY (1 << 2)
+
+/* possible flags for register MAC_CSR5 */
+#define RT2573_NUM_BSSID_MSK(n) (((n * 3) & 3) << 16)
+
+/* possible flags for register MAC_CSR11 */
+#define RT2573_AUTO_WAKEUP (1 << 15)
+#define RT2573_TBCN_EXP(n) ((n) << 8)
+#define RT2573_TBCN_EXP_MAX 0x7f
+#define RT2573_TBCN_DELAY(t) (t)
+#define RT2573_TBCN_DELAY_MAX 0xff
+
+/* possible flags for register TXRX_CSR0 */
+/* Tx filter flags are in the low 16 bits */
+#define RT2573_AUTO_TX_SEQ (1 << 15)
+/* Rx filter flags are in the high 16 bits */
+#define RT2573_DISABLE_RX (1 << 16)
+#define RT2573_DROP_CRC_ERROR (1 << 17)
+#define RT2573_DROP_PHY_ERROR (1 << 18)
+#define RT2573_DROP_CTL (1 << 19)
+#define RT2573_DROP_NOT_TO_ME (1 << 20)
+#define RT2573_DROP_TODS (1 << 21)
+#define RT2573_DROP_VER_ERROR (1 << 22)
+#define RT2573_DROP_MULTICAST (1 << 23)
+#define RT2573_DROP_BROADCAST (1 << 24)
+#define RT2573_DROP_ACKCTS (1 << 25)
+
+/* possible flags for register TXRX_CSR4 */
+#define RT2573_ACKCTS_PWRMGT (1 << 16)
+#define RT2573_SHORT_PREAMBLE (1 << 18)
+#define RT2573_MRR_ENABLED (1 << 19)
+#define RT2573_MRR_CCK_FALLBACK (1 << 22)
+#define RT2573_LONG_RETRY(max) ((max) << 24)
+#define RT2573_LONG_RETRY_MASK (0xf << 24)
+#define RT2573_SHORT_RETRY(max) ((max) << 28)
+#define RT2573_SHORT_RETRY_MASK (0xf << 28)
+
+/* possible flags for register TXRX_CSR9 */
+#define RT2573_TSF_TIMER_EN (1 << 16)
+#define RT2573_TSF_SYNC_MODE(x) (((x) & 0x3) << 17)
+#define RT2573_TSF_SYNC_MODE_DIS 0
+#define RT2573_TSF_SYNC_MODE_STA 1
+#define RT2573_TSF_SYNC_MODE_IBSS 2
+#define RT2573_TSF_SYNC_MODE_HOSTAP 3
+#define RT2573_TBTT_TIMER_EN (1 << 19)
+#define RT2573_BCN_TX_EN (1 << 20)
+
+/* possible flags for register PHY_CSR0 */
+#define RT2573_PA_PE_2GHZ (1 << 16)
+#define RT2573_PA_PE_5GHZ (1 << 17)
+
+/* possible flags for register PHY_CSR3 */
+#define RT2573_BBP_READ (1 << 15)
+#define RT2573_BBP_BUSY (1 << 16)
+/* possible flags for register PHY_CSR4 */
+#define RT2573_RF_20BIT (20 << 24)
+#define RT2573_RF_BUSY (1U << 31)
+
+/* LED values */
+#define RT2573_LED_RADIO (1 << 8)
+#define RT2573_LED_G (1 << 9)
+#define RT2573_LED_A (1 << 10)
+#define RT2573_LED_ON 0x1e1e
+#define RT2573_LED_OFF 0x0
+
+/* USB vendor requests */
+#define RT2573_MCU_SLEEP 7
+#define RT2573_MCU_RUN 8
+#define RT2573_MCU_WAKEUP 9
+
+#define RT2573_SMART_MODE (1 << 0)
+
+#define RT2573_BBPR94_DEFAULT 6
+
+#define RT2573_BBP_WRITE (1 << 15)
+
+/* dual-band RF */
+#define RT2573_RF_5226 1
+#define RT2573_RF_5225 3
+/* single-band RF */
+#define RT2573_RF_2528 2
+#define RT2573_RF_2527 4
+
+#define RT2573_BBP_VERSION 0
+
+struct rum_tx_desc {
+ uint32_t flags;
+#define RT2573_TX_BURST (1 << 0)
+#define RT2573_TX_VALID (1 << 1)
+#define RT2573_TX_MORE_FRAG (1 << 2)
+#define RT2573_TX_NEED_ACK (1 << 3)
+#define RT2573_TX_TIMESTAMP (1 << 4)
+#define RT2573_TX_OFDM (1 << 5)
+#define RT2573_TX_IFS_SIFS (1 << 6)
+#define RT2573_TX_LONG_RETRY (1 << 7)
+#define RT2573_TX_TKIPMIC (1 << 8)
+#define RT2573_TX_KEY_PAIR (1 << 9)
+#define RT2573_TX_KEY_ID(id) (((id) & 0x3f) << 10)
+#define RT2573_TX_CIP_MODE(m) ((m) << 29)
+
+ uint16_t wme;
+#define RT2573_QID(v) (v)
+#define RT2573_AIFSN(v) ((v) << 4)
+#define RT2573_LOGCWMIN(v) ((v) << 8)
+#define RT2573_LOGCWMAX(v) ((v) << 12)
+
+ uint8_t hdrlen;
+ uint8_t xflags;
+#define RT2573_TX_HWSEQ (1 << 4)
+
+ uint8_t plcp_signal;
+ uint8_t plcp_service;
+#define RT2573_PLCP_LENGEXT 0x80
+
+ uint8_t plcp_length_lo;
+ uint8_t plcp_length_hi;
+
+ uint32_t iv;
+ uint32_t eiv;
+
+ uint8_t offset;
+ uint8_t qid;
+ uint8_t txpower;
+#define RT2573_DEFAULT_TXPOWER 0
+
+ uint8_t reserved;
+} __packed;
+
+struct rum_rx_desc {
+ uint32_t flags;
+#define RT2573_RX_BUSY (1 << 0)
+#define RT2573_RX_DROP (1 << 1)
+#define RT2573_RX_UC2ME (1 << 2)
+#define RT2573_RX_MC (1 << 3)
+#define RT2573_RX_BC (1 << 4)
+#define RT2573_RX_MYBSS (1 << 5)
+#define RT2573_RX_CRC_ERROR (1 << 6)
+#define RT2573_RX_OFDM (1 << 7)
+
+#define RT2573_RX_DEC_MASK (3 << 8)
+#define RT2573_RX_DEC_OK (0 << 8)
+
+#define RT2573_RX_IV_ERROR (1 << 8)
+#define RT2573_RX_MIC_ERROR (2 << 8)
+#define RT2573_RX_KEY_ERROR (3 << 8)
+
+#define RT2573_RX_KEY_PAIR (1 << 28)
+
+#define RT2573_RX_CIP_MASK (7 << 29)
+#define RT2573_RX_CIP_MODE(m) ((m) << 29)
+
+ uint8_t rate;
+ uint8_t rssi;
+ uint8_t reserved1;
+ uint8_t offset;
+ uint32_t iv;
+ uint32_t eiv;
+ uint32_t reserved2[2];
+} __packed;
+
+#define RT2573_RF1 0
+#define RT2573_RF2 2
+#define RT2573_RF3 1
+#define RT2573_RF4 3
+
+#define RT2573_EEPROM_MACBBP 0x0000
+#define RT2573_EEPROM_ADDRESS 0x0004
+#define RT2573_EEPROM_ANTENNA 0x0020
+#define RT2573_EEPROM_CONFIG2 0x0022
+#define RT2573_EEPROM_BBP_BASE 0x0026
+#define RT2573_EEPROM_TXPOWER 0x0046
+#define RT2573_EEPROM_FREQ_OFFSET 0x005e
+#define RT2573_EEPROM_RSSI_2GHZ_OFFSET 0x009a
+#define RT2573_EEPROM_RSSI_5GHZ_OFFSET 0x009c
diff --git a/freebsd/sys/dev/usb/wlan/if_rumvar.h b/freebsd/sys/dev/usb/wlan/if_rumvar.h
new file mode 100644
index 00000000..4ff831f4
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_rumvar.h
@@ -0,0 +1,186 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RUM_TX_LIST_COUNT 8
+#define RUM_TX_MINFREE 2
+
+struct rum_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint64_t wr_tsf;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_antsignal;
+ int8_t wr_antnoise;
+ uint8_t wr_antenna;
+} __packed __aligned(8);
+
+#define RT2573_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_TSFT) | \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ 0)
+
+struct rum_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+ uint8_t wt_antenna;
+} __packed __aligned(8);
+
+#define RT2573_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+struct rum_softc;
+
+struct rum_tx_data {
+ STAILQ_ENTRY(rum_tx_data) next;
+ struct rum_softc *sc;
+ struct rum_tx_desc desc;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ int rate;
+};
+typedef STAILQ_HEAD(, rum_tx_data) rum_txdhead;
+
+union sec_param {
+ struct ieee80211_key key;
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ struct ieee80211vap *vap;
+};
+#define CMD_FUNC_PROTO void (*func)(struct rum_softc *, \
+ union sec_param *, uint8_t)
+
+struct rum_cmdq {
+ union sec_param data;
+ uint8_t rvp_id;
+
+ CMD_FUNC_PROTO;
+};
+#define RUM_CMDQ_SIZE 16
+
+struct rum_vap {
+ struct ieee80211vap vap;
+ struct mbuf *bcn_mbuf;
+ struct usb_callout ratectl_ch;
+ struct task ratectl_task;
+ uint8_t maxretry;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ void (*bmiss)(struct ieee80211vap *);
+ void (*recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int,
+ const struct ieee80211_rx_stats *,
+ int, int);
+};
+#define RUM_VAP(vap) ((struct rum_vap *)(vap))
+
+enum {
+ RUM_BULK_WR,
+ RUM_BULK_RD,
+ RUM_N_TRANSFER = 2,
+};
+
+struct rum_softc {
+ struct ieee80211com sc_ic;
+ struct ieee80211_ratectl_tx_stats sc_txs;
+ struct mbufq sc_snd;
+ device_t sc_dev;
+ struct usb_device *sc_udev;
+
+ struct usb_xfer *sc_xfer[RUM_N_TRANSFER];
+
+ uint8_t rf_rev;
+ uint8_t rffreq;
+
+ struct rum_tx_data tx_data[RUM_TX_LIST_COUNT];
+ rum_txdhead tx_q;
+ rum_txdhead tx_free;
+ int tx_nfree;
+ struct rum_rx_desc sc_rx_desc;
+
+ struct mtx sc_mtx;
+
+ int sc_sleep_end;
+ int sc_sleep_time;
+ uint8_t last_rx_flags;
+
+ struct rum_cmdq cmdq[RUM_CMDQ_SIZE];
+ struct mtx cmdq_mtx;
+ struct task cmdq_task;
+ uint8_t cmdq_first;
+ uint8_t cmdq_last;
+
+ uint32_t sta[6];
+ uint32_t rf_regs[4];
+ uint8_t txpow[44];
+ u_int sc_detached:1,
+ sc_running:1,
+ sc_sleeping:1,
+ sc_clr_shkeys:1;
+
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
+ struct wmeParams wme_params[WME_NUM_AC];
+
+ uint8_t vap_key_count[1];
+ uint64_t keys_bmap;
+
+ struct {
+ uint8_t val;
+ uint8_t reg;
+ } __packed bbp_prom[16];
+
+ int hw_radio;
+ int rx_ant;
+ int tx_ant;
+ int nb_ant;
+ int ext_2ghz_lna;
+ int ext_5ghz_lna;
+ int rssi_2ghz_corr;
+ int rssi_5ghz_corr;
+ uint8_t bbp17;
+
+ struct rum_rx_radiotap_header sc_rxtap;
+ struct rum_tx_radiotap_header sc_txtap;
+};
+
+#define RUM_LOCK_INIT(sc) \
+ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->sc_dev), \
+ MTX_NETWORK_LOCK, MTX_DEF);
+#define RUM_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RUM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RUM_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+#define RUM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx)
+
+#define RUM_CMDQ_LOCK_INIT(sc) \
+ mtx_init(&(sc)->cmdq_mtx, "cmdq lock", NULL, MTX_DEF)
+#define RUM_CMDQ_LOCK(sc) mtx_lock(&(sc)->cmdq_mtx)
+#define RUM_CMDQ_UNLOCK(sc) mtx_unlock(&(sc)->cmdq_mtx)
+#define RUM_CMDQ_LOCK_DESTROY(sc) mtx_destroy(&(sc)->cmdq_mtx)
diff --git a/freebsd/sys/dev/usb/wlan/if_run.c b/freebsd/sys/dev/usb/wlan/if_run.c
new file mode 100644
index 00000000..9983fce2
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_run.c
@@ -0,0 +1,6339 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2008,2010 Damien Bergamini <damien.bergamini@free.fr>
+ * ported to FreeBSD by Akinori Furukoshi <moonlightakkiy@yahoo.ca>
+ * USB Consulting, Hans Petter Selasky <hselasky@freebsd.org>
+ * Copyright (c) 2013-2014 Kevin Lo
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Ralink Technology RT2700U/RT2800U/RT3000U/RT3900E chipset driver.
+ * http://www.ralinktech.com/
+ */
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/linker.h>
+#include <sys/firmware.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_ratectl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR run_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_msctest.h>
+
+#include <dev/usb/wlan/if_runreg.h>
+#include <dev/usb/wlan/if_runvar.h>
+
+#ifdef USB_DEBUG
+#define RUN_DEBUG
+#endif
+
+#ifdef RUN_DEBUG
+int run_debug = 0;
+static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW, 0, "USB run");
+SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RWTUN, &run_debug, 0,
+ "run debug level");
+
+enum {
+ RUN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ RUN_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */
+ RUN_DEBUG_RECV = 0x00000004, /* basic recv operation */
+ RUN_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */
+ RUN_DEBUG_STATE = 0x00000010, /* 802.11 state transitions */
+ RUN_DEBUG_RATE = 0x00000020, /* rate adaptation */
+ RUN_DEBUG_USB = 0x00000040, /* usb requests */
+ RUN_DEBUG_FIRMWARE = 0x00000080, /* firmware(9) loading debug */
+ RUN_DEBUG_BEACON = 0x00000100, /* beacon handling */
+ RUN_DEBUG_INTR = 0x00000200, /* ISR */
+ RUN_DEBUG_TEMP = 0x00000400, /* temperature calibration */
+ RUN_DEBUG_ROM = 0x00000800, /* various ROM info */
+ RUN_DEBUG_KEY = 0x00001000, /* crypto keys management */
+ RUN_DEBUG_TXPWR = 0x00002000, /* dump Tx power values */
+ RUN_DEBUG_RSSI = 0x00004000, /* dump RSSI lookups */
+ RUN_DEBUG_RESET = 0x00008000, /* initialization progress */
+ RUN_DEBUG_CALIB = 0x00010000, /* calibration progress */
+ RUN_DEBUG_CMD = 0x00020000, /* command queue */
+ RUN_DEBUG_ANY = 0xffffffff
+};
+
+#define RUN_DPRINTF(_sc, _m, ...) do { \
+ if (run_debug & (_m)) \
+ device_printf((_sc)->sc_dev, __VA_ARGS__); \
+} while(0)
+#else
+#define RUN_DPRINTF(_sc, _m, ...) do { (void) _sc; } while (0)
+#endif
+
+#define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh)
+
+/*
+ * Because of LOR in run_key_delete(), use atomic instead.
+ * '& RUN_CMDQ_MASQ' is to loop cmdq[].
+ */
+#define RUN_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & RUN_CMDQ_MASQ)
+
+static const STRUCT_USB_HOST_ID run_devs[] = {
+#define RUN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+#define RUN_DEV_EJECT(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RUN_EJECT) }
+#define RUN_EJECT 1
+ RUN_DEV(ABOCOM, RT2770),
+ RUN_DEV(ABOCOM, RT2870),
+ RUN_DEV(ABOCOM, RT3070),
+ RUN_DEV(ABOCOM, RT3071),
+ RUN_DEV(ABOCOM, RT3072),
+ RUN_DEV(ABOCOM2, RT2870_1),
+ RUN_DEV(ACCTON, RT2770),
+ RUN_DEV(ACCTON, RT2870_1),
+ RUN_DEV(ACCTON, RT2870_2),
+ RUN_DEV(ACCTON, RT2870_3),
+ RUN_DEV(ACCTON, RT2870_4),
+ RUN_DEV(ACCTON, RT2870_5),
+ RUN_DEV(ACCTON, RT3070),
+ RUN_DEV(ACCTON, RT3070_1),
+ RUN_DEV(ACCTON, RT3070_2),
+ RUN_DEV(ACCTON, RT3070_3),
+ RUN_DEV(ACCTON, RT3070_4),
+ RUN_DEV(ACCTON, RT3070_5),
+ RUN_DEV(AIRTIES, RT3070),
+ RUN_DEV(ALLWIN, RT2070),
+ RUN_DEV(ALLWIN, RT2770),
+ RUN_DEV(ALLWIN, RT2870),
+ RUN_DEV(ALLWIN, RT3070),
+ RUN_DEV(ALLWIN, RT3071),
+ RUN_DEV(ALLWIN, RT3072),
+ RUN_DEV(ALLWIN, RT3572),
+ RUN_DEV(AMIGO, RT2870_1),
+ RUN_DEV(AMIGO, RT2870_2),
+ RUN_DEV(AMIT, CGWLUSB2GNR),
+ RUN_DEV(AMIT, RT2870_1),
+ RUN_DEV(AMIT2, RT2870),
+ RUN_DEV(ASUS, RT2870_1),
+ RUN_DEV(ASUS, RT2870_2),
+ RUN_DEV(ASUS, RT2870_3),
+ RUN_DEV(ASUS, RT2870_4),
+ RUN_DEV(ASUS, RT2870_5),
+ RUN_DEV(ASUS, USBN13),
+ RUN_DEV(ASUS, RT3070_1),
+ RUN_DEV(ASUS, USBN66),
+ RUN_DEV(ASUS, USB_N53),
+ RUN_DEV(ASUS2, USBN11),
+ RUN_DEV(AZUREWAVE, RT2870_1),
+ RUN_DEV(AZUREWAVE, RT2870_2),
+ RUN_DEV(AZUREWAVE, RT3070_1),
+ RUN_DEV(AZUREWAVE, RT3070_2),
+ RUN_DEV(AZUREWAVE, RT3070_3),
+ RUN_DEV(BELKIN, F9L1103),
+ RUN_DEV(BELKIN, F5D8053V3),
+ RUN_DEV(BELKIN, F5D8055),
+ RUN_DEV(BELKIN, F5D8055V2),
+ RUN_DEV(BELKIN, F6D4050V1),
+ RUN_DEV(BELKIN, F6D4050V2),
+ RUN_DEV(BELKIN, RT2870_1),
+ RUN_DEV(BELKIN, RT2870_2),
+ RUN_DEV(CISCOLINKSYS, AE1000),
+ RUN_DEV(CISCOLINKSYS2, RT3070),
+ RUN_DEV(CISCOLINKSYS3, RT3070),
+ RUN_DEV(CONCEPTRONIC2, RT2870_1),
+ RUN_DEV(CONCEPTRONIC2, RT2870_2),
+ RUN_DEV(CONCEPTRONIC2, RT2870_3),
+ RUN_DEV(CONCEPTRONIC2, RT2870_4),
+ RUN_DEV(CONCEPTRONIC2, RT2870_5),
+ RUN_DEV(CONCEPTRONIC2, RT2870_6),
+ RUN_DEV(CONCEPTRONIC2, RT2870_7),
+ RUN_DEV(CONCEPTRONIC2, RT2870_8),
+ RUN_DEV(CONCEPTRONIC2, RT3070_1),
+ RUN_DEV(CONCEPTRONIC2, RT3070_2),
+ RUN_DEV(CONCEPTRONIC2, VIGORN61),
+ RUN_DEV(COREGA, CGWLUSB300GNM),
+ RUN_DEV(COREGA, RT2870_1),
+ RUN_DEV(COREGA, RT2870_2),
+ RUN_DEV(COREGA, RT2870_3),
+ RUN_DEV(COREGA, RT3070),
+ RUN_DEV(CYBERTAN, RT2870),
+ RUN_DEV(DLINK, RT2870),
+ RUN_DEV(DLINK, RT3072),
+ RUN_DEV(DLINK, DWA127),
+ RUN_DEV(DLINK, DWA140B3),
+ RUN_DEV(DLINK, DWA160B2),
+ RUN_DEV(DLINK, DWA140D1),
+ RUN_DEV(DLINK, DWA162),
+ RUN_DEV(DLINK2, DWA130),
+ RUN_DEV(DLINK2, RT2870_1),
+ RUN_DEV(DLINK2, RT2870_2),
+ RUN_DEV(DLINK2, RT3070_1),
+ RUN_DEV(DLINK2, RT3070_2),
+ RUN_DEV(DLINK2, RT3070_3),
+ RUN_DEV(DLINK2, RT3070_4),
+ RUN_DEV(DLINK2, RT3070_5),
+ RUN_DEV(DLINK2, RT3072),
+ RUN_DEV(DLINK2, RT3072_1),
+ RUN_DEV(EDIMAX, EW7717),
+ RUN_DEV(EDIMAX, EW7718),
+ RUN_DEV(EDIMAX, EW7733UND),
+ RUN_DEV(EDIMAX, RT2870_1),
+ RUN_DEV(ENCORE, RT3070_1),
+ RUN_DEV(ENCORE, RT3070_2),
+ RUN_DEV(ENCORE, RT3070_3),
+ RUN_DEV(GIGABYTE, GNWB31N),
+ RUN_DEV(GIGABYTE, GNWB32L),
+ RUN_DEV(GIGABYTE, RT2870_1),
+ RUN_DEV(GIGASET, RT3070_1),
+ RUN_DEV(GIGASET, RT3070_2),
+ RUN_DEV(GUILLEMOT, HWNU300),
+ RUN_DEV(HAWKING, HWUN2),
+ RUN_DEV(HAWKING, RT2870_1),
+ RUN_DEV(HAWKING, RT2870_2),
+ RUN_DEV(HAWKING, RT3070),
+ RUN_DEV(IODATA, RT3072_1),
+ RUN_DEV(IODATA, RT3072_2),
+ RUN_DEV(IODATA, RT3072_3),
+ RUN_DEV(IODATA, RT3072_4),
+ RUN_DEV(LINKSYS4, RT3070),
+ RUN_DEV(LINKSYS4, WUSB100),
+ RUN_DEV(LINKSYS4, WUSB54GCV3),
+ RUN_DEV(LINKSYS4, WUSB600N),
+ RUN_DEV(LINKSYS4, WUSB600NV2),
+ RUN_DEV(LOGITEC, RT2870_1),
+ RUN_DEV(LOGITEC, RT2870_2),
+ RUN_DEV(LOGITEC, RT2870_3),
+ RUN_DEV(LOGITEC, LANW300NU2),
+ RUN_DEV(LOGITEC, LANW150NU2),
+ RUN_DEV(LOGITEC, LANW300NU2S),
+ RUN_DEV(MELCO, WLIUCG300HP),
+ RUN_DEV(MELCO, RT2870_2),
+ RUN_DEV(MELCO, WLIUCAG300N),
+ RUN_DEV(MELCO, WLIUCG300N),
+ RUN_DEV(MELCO, WLIUCG301N),
+ RUN_DEV(MELCO, WLIUCGN),
+ RUN_DEV(MELCO, WLIUCGNM),
+ RUN_DEV(MELCO, WLIUCG300HPV1),
+ RUN_DEV(MELCO, WLIUCGNM2),
+ RUN_DEV(MOTOROLA4, RT2770),
+ RUN_DEV(MOTOROLA4, RT3070),
+ RUN_DEV(MSI, RT3070_1),
+ RUN_DEV(MSI, RT3070_2),
+ RUN_DEV(MSI, RT3070_3),
+ RUN_DEV(MSI, RT3070_4),
+ RUN_DEV(MSI, RT3070_5),
+ RUN_DEV(MSI, RT3070_6),
+ RUN_DEV(MSI, RT3070_7),
+ RUN_DEV(MSI, RT3070_8),
+ RUN_DEV(MSI, RT3070_9),
+ RUN_DEV(MSI, RT3070_10),
+ RUN_DEV(MSI, RT3070_11),
+ RUN_DEV(NETGEAR, WNDA4100),
+ RUN_DEV(OVISLINK, RT3072),
+ RUN_DEV(PARA, RT3070),
+ RUN_DEV(PEGATRON, RT2870),
+ RUN_DEV(PEGATRON, RT3070),
+ RUN_DEV(PEGATRON, RT3070_2),
+ RUN_DEV(PEGATRON, RT3070_3),
+ RUN_DEV(PHILIPS, RT2870),
+ RUN_DEV(PLANEX2, GWUS300MINIS),
+ RUN_DEV(PLANEX2, GWUSMICRON),
+ RUN_DEV(PLANEX2, RT2870),
+ RUN_DEV(PLANEX2, RT3070),
+ RUN_DEV(QCOM, RT2870),
+ RUN_DEV(QUANTA, RT3070),
+ RUN_DEV(RALINK, RT2070),
+ RUN_DEV(RALINK, RT2770),
+ RUN_DEV(RALINK, RT2870),
+ RUN_DEV(RALINK, RT3070),
+ RUN_DEV(RALINK, RT3071),
+ RUN_DEV(RALINK, RT3072),
+ RUN_DEV(RALINK, RT3370),
+ RUN_DEV(RALINK, RT3572),
+ RUN_DEV(RALINK, RT3573),
+ RUN_DEV(RALINK, RT5370),
+ RUN_DEV(RALINK, RT5572),
+ RUN_DEV(RALINK, RT8070),
+ RUN_DEV(SAMSUNG, WIS09ABGN),
+ RUN_DEV(SAMSUNG2, RT2870_1),
+ RUN_DEV(SENAO, RT2870_1),
+ RUN_DEV(SENAO, RT2870_2),
+ RUN_DEV(SENAO, RT2870_3),
+ RUN_DEV(SENAO, RT2870_4),
+ RUN_DEV(SENAO, RT3070),
+ RUN_DEV(SENAO, RT3071),
+ RUN_DEV(SENAO, RT3072_1),
+ RUN_DEV(SENAO, RT3072_2),
+ RUN_DEV(SENAO, RT3072_3),
+ RUN_DEV(SENAO, RT3072_4),
+ RUN_DEV(SENAO, RT3072_5),
+ RUN_DEV(SITECOMEU, RT2770),
+ RUN_DEV(SITECOMEU, RT2870_1),
+ RUN_DEV(SITECOMEU, RT2870_2),
+ RUN_DEV(SITECOMEU, RT2870_3),
+ RUN_DEV(SITECOMEU, RT2870_4),
+ RUN_DEV(SITECOMEU, RT3070),
+ RUN_DEV(SITECOMEU, RT3070_2),
+ RUN_DEV(SITECOMEU, RT3070_3),
+ RUN_DEV(SITECOMEU, RT3070_4),
+ RUN_DEV(SITECOMEU, RT3071),
+ RUN_DEV(SITECOMEU, RT3072_1),
+ RUN_DEV(SITECOMEU, RT3072_2),
+ RUN_DEV(SITECOMEU, RT3072_3),
+ RUN_DEV(SITECOMEU, RT3072_4),
+ RUN_DEV(SITECOMEU, RT3072_5),
+ RUN_DEV(SITECOMEU, RT3072_6),
+ RUN_DEV(SITECOMEU, WL608),
+ RUN_DEV(SPARKLAN, RT2870_1),
+ RUN_DEV(SPARKLAN, RT3070),
+ RUN_DEV(SWEEX2, LW153),
+ RUN_DEV(SWEEX2, LW303),
+ RUN_DEV(SWEEX2, LW313),
+ RUN_DEV(TOSHIBA, RT3070),
+ RUN_DEV(UMEDIA, RT2870_1),
+ RUN_DEV(ZCOM, RT2870_1),
+ RUN_DEV(ZCOM, RT2870_2),
+ RUN_DEV(ZINWELL, RT2870_1),
+ RUN_DEV(ZINWELL, RT2870_2),
+ RUN_DEV(ZINWELL, RT3070),
+ RUN_DEV(ZINWELL, RT3072_1),
+ RUN_DEV(ZINWELL, RT3072_2),
+ RUN_DEV(ZYXEL, RT2870_1),
+ RUN_DEV(ZYXEL, RT2870_2),
+ RUN_DEV(ZYXEL, RT3070),
+ RUN_DEV_EJECT(ZYXEL, NWD2705),
+ RUN_DEV_EJECT(RALINK, RT_STOR),
+#undef RUN_DEV_EJECT
+#undef RUN_DEV
+};
+
+static device_probe_t run_match;
+static device_attach_t run_attach;
+static device_detach_t run_detach;
+
+static usb_callback_t run_bulk_rx_callback;
+static usb_callback_t run_bulk_tx_callback0;
+static usb_callback_t run_bulk_tx_callback1;
+static usb_callback_t run_bulk_tx_callback2;
+static usb_callback_t run_bulk_tx_callback3;
+static usb_callback_t run_bulk_tx_callback4;
+static usb_callback_t run_bulk_tx_callback5;
+
+static void run_autoinst(void *, struct usb_device *,
+ struct usb_attach_arg *);
+static int run_driver_loaded(struct module *, int, void *);
+static void run_bulk_tx_callbackN(struct usb_xfer *xfer,
+ usb_error_t error, u_int index);
+static struct ieee80211vap *run_vap_create(struct ieee80211com *,
+ const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void run_vap_delete(struct ieee80211vap *);
+static void run_cmdq_cb(void *, int);
+static void run_setup_tx_list(struct run_softc *,
+ struct run_endpoint_queue *);
+static void run_unsetup_tx_list(struct run_softc *,
+ struct run_endpoint_queue *);
+static int run_load_microcode(struct run_softc *);
+static int run_reset(struct run_softc *);
+static usb_error_t run_do_request(struct run_softc *,
+ struct usb_device_request *, void *);
+static int run_read(struct run_softc *, uint16_t, uint32_t *);
+static int run_read_region_1(struct run_softc *, uint16_t, uint8_t *, int);
+static int run_write_2(struct run_softc *, uint16_t, uint16_t);
+static int run_write(struct run_softc *, uint16_t, uint32_t);
+static int run_write_region_1(struct run_softc *, uint16_t,
+ const uint8_t *, int);
+static int run_set_region_4(struct run_softc *, uint16_t, uint32_t, int);
+static int run_efuse_read(struct run_softc *, uint16_t, uint16_t *, int);
+static int run_efuse_read_2(struct run_softc *, uint16_t, uint16_t *);
+static int run_eeprom_read_2(struct run_softc *, uint16_t, uint16_t *);
+static int run_rt2870_rf_write(struct run_softc *, uint32_t);
+static int run_rt3070_rf_read(struct run_softc *, uint8_t, uint8_t *);
+static int run_rt3070_rf_write(struct run_softc *, uint8_t, uint8_t);
+static int run_bbp_read(struct run_softc *, uint8_t, uint8_t *);
+static int run_bbp_write(struct run_softc *, uint8_t, uint8_t);
+static int run_mcu_cmd(struct run_softc *, uint8_t, uint16_t);
+static const char *run_get_rf(uint16_t);
+static void run_rt3593_get_txpower(struct run_softc *);
+static void run_get_txpower(struct run_softc *);
+static int run_read_eeprom(struct run_softc *);
+static struct ieee80211_node *run_node_alloc(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static int run_media_change(struct ifnet *);
+static int run_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int run_wme_update(struct ieee80211com *);
+static void run_key_set_cb(void *);
+static int run_key_set(struct ieee80211vap *, struct ieee80211_key *);
+static void run_key_delete_cb(void *);
+static int run_key_delete(struct ieee80211vap *, struct ieee80211_key *);
+static void run_ratectl_to(void *);
+static void run_ratectl_cb(void *, int);
+static void run_drain_fifo(void *);
+static void run_iter_func(void *, struct ieee80211_node *);
+static void run_newassoc_cb(void *);
+static void run_newassoc(struct ieee80211_node *, int);
+static void run_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
+ const struct ieee80211_rx_stats *, int, int);
+static void run_rx_frame(struct run_softc *, struct mbuf *, uint32_t);
+static void run_tx_free(struct run_endpoint_queue *pq,
+ struct run_tx_data *, int);
+static void run_set_tx_desc(struct run_softc *, struct run_tx_data *);
+static int run_tx(struct run_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int run_tx_mgt(struct run_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int run_sendprot(struct run_softc *, const struct mbuf *,
+ struct ieee80211_node *, int, int);
+static int run_tx_param(struct run_softc *, struct mbuf *,
+ struct ieee80211_node *,
+ const struct ieee80211_bpf_params *);
+static int run_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static int run_transmit(struct ieee80211com *, struct mbuf *);
+static void run_start(struct run_softc *);
+static void run_parent(struct ieee80211com *);
+static void run_iq_calib(struct run_softc *, u_int);
+static void run_set_agc(struct run_softc *, uint8_t);
+static void run_select_chan_group(struct run_softc *, int);
+static void run_set_rx_antenna(struct run_softc *, int);
+static void run_rt2870_set_chan(struct run_softc *, u_int);
+static void run_rt3070_set_chan(struct run_softc *, u_int);
+static void run_rt3572_set_chan(struct run_softc *, u_int);
+static void run_rt3593_set_chan(struct run_softc *, u_int);
+static void run_rt5390_set_chan(struct run_softc *, u_int);
+static void run_rt5592_set_chan(struct run_softc *, u_int);
+static int run_set_chan(struct run_softc *, struct ieee80211_channel *);
+static void run_set_channel(struct ieee80211com *);
+static void run_getradiocaps(struct ieee80211com *, int, int *,
+ struct ieee80211_channel[]);
+static void run_scan_start(struct ieee80211com *);
+static void run_scan_end(struct ieee80211com *);
+static void run_update_beacon(struct ieee80211vap *, int);
+static void run_update_beacon_cb(void *);
+static void run_updateprot(struct ieee80211com *);
+static void run_updateprot_cb(void *);
+static void run_usb_timeout_cb(void *);
+static void run_reset_livelock(struct run_softc *);
+static void run_enable_tsf_sync(struct run_softc *);
+static void run_enable_tsf(struct run_softc *);
+static void run_get_tsf(struct run_softc *, uint64_t *);
+static void run_enable_mrr(struct run_softc *);
+static void run_set_txpreamble(struct run_softc *);
+static void run_set_basicrates(struct run_softc *);
+static void run_set_leds(struct run_softc *, uint16_t);
+static void run_set_bssid(struct run_softc *, const uint8_t *);
+static void run_set_macaddr(struct run_softc *, const uint8_t *);
+static void run_updateslot(struct ieee80211com *);
+static void run_updateslot_cb(void *);
+static void run_update_mcast(struct ieee80211com *);
+static int8_t run_rssi2dbm(struct run_softc *, uint8_t, uint8_t);
+static void run_update_promisc_locked(struct run_softc *);
+static void run_update_promisc(struct ieee80211com *);
+static void run_rt5390_bbp_init(struct run_softc *);
+static int run_bbp_init(struct run_softc *);
+static int run_rt3070_rf_init(struct run_softc *);
+static void run_rt3593_rf_init(struct run_softc *);
+static void run_rt5390_rf_init(struct run_softc *);
+static int run_rt3070_filter_calib(struct run_softc *, uint8_t, uint8_t,
+ uint8_t *);
+static void run_rt3070_rf_setup(struct run_softc *);
+static void run_rt3593_rf_setup(struct run_softc *);
+static void run_rt5390_rf_setup(struct run_softc *);
+static int run_txrx_enable(struct run_softc *);
+static void run_adjust_freq_offset(struct run_softc *);
+static void run_init_locked(struct run_softc *);
+static void run_stop(void *);
+static void run_delay(struct run_softc *, u_int);
+
+static eventhandler_tag run_etag;
+
+static const struct rt2860_rate {
+ uint8_t rate;
+ uint8_t mcs;
+ enum ieee80211_phytype phy;
+ uint8_t ctl_ridx;
+ uint16_t sp_ack_dur;
+ uint16_t lp_ack_dur;
+} rt2860_rates[] = {
+ { 2, 0, IEEE80211_T_DS, 0, 314, 314 },
+ { 4, 1, IEEE80211_T_DS, 1, 258, 162 },
+ { 11, 2, IEEE80211_T_DS, 2, 223, 127 },
+ { 22, 3, IEEE80211_T_DS, 3, 213, 117 },
+ { 12, 0, IEEE80211_T_OFDM, 4, 60, 60 },
+ { 18, 1, IEEE80211_T_OFDM, 4, 52, 52 },
+ { 24, 2, IEEE80211_T_OFDM, 6, 48, 48 },
+ { 36, 3, IEEE80211_T_OFDM, 6, 44, 44 },
+ { 48, 4, IEEE80211_T_OFDM, 8, 44, 44 },
+ { 72, 5, IEEE80211_T_OFDM, 8, 40, 40 },
+ { 96, 6, IEEE80211_T_OFDM, 8, 40, 40 },
+ { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 }
+};
+
+static const struct {
+ uint16_t reg;
+ uint32_t val;
+} rt2870_def_mac[] = {
+ RT2870_DEF_MAC
+};
+
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} rt2860_def_bbp[] = {
+ RT2860_DEF_BBP
+},rt5390_def_bbp[] = {
+ RT5390_DEF_BBP
+},rt5592_def_bbp[] = {
+ RT5592_DEF_BBP
+};
+
+/*
+ * Default values for BBP register R196 for RT5592.
+ */
+static const uint8_t rt5592_bbp_r196[] = {
+ 0xe0, 0x1f, 0x38, 0x32, 0x08, 0x28, 0x19, 0x0a, 0xff, 0x00,
+ 0x16, 0x10, 0x10, 0x0b, 0x36, 0x2c, 0x26, 0x24, 0x42, 0x36,
+ 0x30, 0x2d, 0x4c, 0x46, 0x3d, 0x40, 0x3e, 0x42, 0x3d, 0x40,
+ 0x3c, 0x34, 0x2c, 0x2f, 0x3c, 0x35, 0x2e, 0x2a, 0x49, 0x41,
+ 0x36, 0x31, 0x30, 0x30, 0x0e, 0x0d, 0x28, 0x21, 0x1c, 0x16,
+ 0x50, 0x4a, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7d, 0x14, 0x32, 0x2c, 0x36, 0x4c, 0x43, 0x2c,
+ 0x2e, 0x36, 0x30, 0x6e
+};
+
+static const struct rfprog {
+ uint8_t chan;
+ uint32_t r1, r2, r3, r4;
+} rt2860_rf2850[] = {
+ RT2860_RF2850
+};
+
+struct {
+ uint8_t n, r, k;
+} rt3070_freqs[] = {
+ RT3070_RF3052
+};
+
+static const struct rt5592_freqs {
+ uint16_t n;
+ uint8_t k, m, r;
+} rt5592_freqs_20mhz[] = {
+ RT5592_RF5592_20MHZ
+},rt5592_freqs_40mhz[] = {
+ RT5592_RF5592_40MHZ
+};
+
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} rt3070_def_rf[] = {
+ RT3070_DEF_RF
+},rt3572_def_rf[] = {
+ RT3572_DEF_RF
+},rt3593_def_rf[] = {
+ RT3593_DEF_RF
+},rt5390_def_rf[] = {
+ RT5390_DEF_RF
+},rt5392_def_rf[] = {
+ RT5392_DEF_RF
+},rt5592_def_rf[] = {
+ RT5592_DEF_RF
+},rt5592_2ghz_def_rf[] = {
+ RT5592_2GHZ_DEF_RF
+},rt5592_5ghz_def_rf[] = {
+ RT5592_5GHZ_DEF_RF
+};
+
+static const struct {
+ u_int firstchan;
+ u_int lastchan;
+ uint8_t reg;
+ uint8_t val;
+} rt5592_chan_5ghz[] = {
+ RT5592_CHAN_5GHZ
+};
+
+static const struct usb_config run_config[RUN_N_XFER] = {
+ [RUN_BULK_TX_BE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .ep_index = 0,
+ .direction = UE_DIR_OUT,
+ .bufsize = RUN_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = run_bulk_tx_callback0,
+ .timeout = 5000, /* ms */
+ },
+ [RUN_BULK_TX_BK] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .ep_index = 1,
+ .bufsize = RUN_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = run_bulk_tx_callback1,
+ .timeout = 5000, /* ms */
+ },
+ [RUN_BULK_TX_VI] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .ep_index = 2,
+ .bufsize = RUN_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = run_bulk_tx_callback2,
+ .timeout = 5000, /* ms */
+ },
+ [RUN_BULK_TX_VO] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .ep_index = 3,
+ .bufsize = RUN_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = run_bulk_tx_callback3,
+ .timeout = 5000, /* ms */
+ },
+ [RUN_BULK_TX_HCCA] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .ep_index = 4,
+ .bufsize = RUN_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
+ .callback = run_bulk_tx_callback4,
+ .timeout = 5000, /* ms */
+ },
+ [RUN_BULK_TX_PRIO] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .ep_index = 5,
+ .bufsize = RUN_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
+ .callback = run_bulk_tx_callback5,
+ .timeout = 5000, /* ms */
+ },
+ [RUN_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = RUN_MAX_RXSZ,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = run_bulk_rx_callback,
+ }
+};
+
+static void
+run_autoinst(void *arg, struct usb_device *udev,
+ struct usb_attach_arg *uaa)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *id;
+
+ if (uaa->dev_state != UAA_DEV_READY)
+ return;
+
+ iface = usbd_get_iface(udev, 0);
+ if (iface == NULL)
+ return;
+ id = iface->idesc;
+ if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
+ return;
+ if (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa))
+ return;
+
+ if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0)
+ uaa->dev_state = UAA_DEV_EJECTING;
+}
+
+static int
+run_driver_loaded(struct module *mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ run_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
+ run_autoinst, NULL, EVENTHANDLER_PRI_ANY);
+ break;
+ case MOD_UNLOAD:
+ EVENTHANDLER_DEREGISTER(usb_dev_configured, run_etag);
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+
+static int
+run_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != 0)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != RT2860_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa));
+}
+
+static int
+run_attach(device_t self)
+{
+ struct run_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t ver;
+ uint8_t iface_index;
+ int ntries, error;
+
+ device_set_usb_desc(self);
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+ if (USB_GET_DRIVER_INFO(uaa) != RUN_EJECT)
+ sc->sc_flags |= RUN_FLAG_FWLOAD_NEEDED;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ iface_index = RT2860_IFACE_INDEX;
+
+ error = usbd_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, run_config, RUN_N_XFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(self, "could not allocate USB transfers, "
+ "err=%s\n", usbd_errstr(error));
+ goto detach;
+ }
+
+ RUN_LOCK(sc);
+
+ /* wait for the chip to settle */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (run_read(sc, RT2860_ASIC_VER_ID, &ver) != 0) {
+ RUN_UNLOCK(sc);
+ goto detach;
+ }
+ if (ver != 0 && ver != 0xffffffff)
+ break;
+ run_delay(sc, 10);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for NIC to initialize\n");
+ RUN_UNLOCK(sc);
+ goto detach;
+ }
+ sc->mac_ver = ver >> 16;
+ sc->mac_rev = ver & 0xffff;
+
+ /* retrieve RF rev. no and various other things from EEPROM */
+ run_read_eeprom(sc);
+
+ device_printf(sc->sc_dev,
+ "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n",
+ sc->mac_ver, sc->mac_rev, run_get_rf(sc->rf_rev),
+ sc->ntxchains, sc->nrxchains, ether_sprintf(ic->ic_macaddr));
+
+ RUN_UNLOCK(sc);
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(self);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA | /* station mode supported */
+ IEEE80211_C_MONITOR | /* monitor mode supported */
+ IEEE80211_C_IBSS |
+ IEEE80211_C_HOSTAP |
+ IEEE80211_C_WDS | /* 4-address traffic works */
+ IEEE80211_C_MBSS |
+ IEEE80211_C_SHPREAMBLE | /* short preamble supported */
+ IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_WME | /* WME */
+ IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */
+
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
+ ic->ic_flags |= IEEE80211_F_DATAPAD;
+ ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+
+ run_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
+ ic->ic_channels);
+
+ ieee80211_ifattach(ic);
+
+ ic->ic_scan_start = run_scan_start;
+ ic->ic_scan_end = run_scan_end;
+ ic->ic_set_channel = run_set_channel;
+ ic->ic_getradiocaps = run_getradiocaps;
+ ic->ic_node_alloc = run_node_alloc;
+ ic->ic_newassoc = run_newassoc;
+ ic->ic_updateslot = run_updateslot;
+ ic->ic_update_mcast = run_update_mcast;
+ ic->ic_wme.wme_update = run_wme_update;
+ ic->ic_raw_xmit = run_raw_xmit;
+ ic->ic_update_promisc = run_update_promisc;
+ ic->ic_vap_create = run_vap_create;
+ ic->ic_vap_delete = run_vap_delete;
+ ic->ic_transmit = run_transmit;
+ ic->ic_parent = run_parent;
+
+ ieee80211_radiotap_attach(ic,
+ &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
+ RUN_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ RUN_RX_RADIOTAP_PRESENT);
+
+ TASK_INIT(&sc->cmdq_task, 0, run_cmdq_cb, sc);
+ TASK_INIT(&sc->ratectl_task, 0, run_ratectl_cb, sc);
+ usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return (0);
+
+detach:
+ run_detach(self);
+ return (ENXIO);
+}
+
+static void
+run_drain_mbufq(struct run_softc *sc)
+{
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ }
+}
+
+static int
+run_detach(device_t self)
+{
+ struct run_softc *sc = device_get_softc(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+ int i;
+
+ RUN_LOCK(sc);
+ sc->sc_detached = 1;
+ RUN_UNLOCK(sc);
+
+ /* stop all USB transfers */
+ usbd_transfer_unsetup(sc->sc_xfer, RUN_N_XFER);
+
+ RUN_LOCK(sc);
+ sc->ratectl_run = RUN_RATECTL_OFF;
+ sc->cmdq_run = sc->cmdq_key_set = RUN_CMDQ_ABORT;
+
+ /* free TX list, if any */
+ for (i = 0; i != RUN_EP_QUEUES; i++)
+ run_unsetup_tx_list(sc, &sc->sc_epq[i]);
+
+ /* Free TX queue */
+ run_drain_mbufq(sc);
+ RUN_UNLOCK(sc);
+
+ if (sc->sc_ic.ic_softc == sc) {
+ /* drain tasks */
+ usb_callout_drain(&sc->ratectl_ch);
+ ieee80211_draintask(ic, &sc->cmdq_task);
+ ieee80211_draintask(ic, &sc->ratectl_task);
+ ieee80211_ifdetach(ic);
+ }
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static struct ieee80211vap *
+run_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct run_softc *sc = ic->ic_softc;
+ struct run_vap *rvp;
+ struct ieee80211vap *vap;
+ int i;
+
+ if (sc->rvp_cnt >= RUN_VAP_MAX) {
+ device_printf(sc->sc_dev, "number of VAPs maxed out\n");
+ return (NULL);
+ }
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ /* enable s/w bmiss handling for sta mode */
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ /* fall though */
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_MONITOR:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
+ /* other than WDS vaps, only one at a time */
+ if (!TAILQ_EMPTY(&ic->ic_vaps))
+ return (NULL);
+ break;
+ case IEEE80211_M_WDS:
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next){
+ if(vap->iv_opmode != IEEE80211_M_HOSTAP)
+ continue;
+ /* WDS vap's always share the local mac address. */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ break;
+ }
+ if (vap == NULL) {
+ device_printf(sc->sc_dev,
+ "wds only supported in ap mode\n");
+ return (NULL);
+ }
+ break;
+ default:
+ device_printf(sc->sc_dev, "unknown opmode %d\n", opmode);
+ return (NULL);
+ }
+
+ rvp = malloc(sizeof(struct run_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &rvp->vap;
+
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags,
+ bssid) != 0) {
+ /* out of memory */
+ free(rvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ vap->iv_update_beacon = run_update_beacon;
+ vap->iv_max_aid = RT2870_WCID_MAX;
+ /*
+ * To delete the right key from h/w, we need wcid.
+ * Luckily, there is unused space in ieee80211_key{}, wk_pad,
+ * and matching wcid will be written into there. So, cast
+ * some spells to remove 'const' from ieee80211_key{}
+ */
+ vap->iv_key_delete = (void *)run_key_delete;
+ vap->iv_key_set = (void *)run_key_set;
+
+ /* override state transition machine */
+ rvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = run_newstate;
+ if (opmode == IEEE80211_M_IBSS) {
+ rvp->recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = run_recv_mgmt;
+ }
+
+ ieee80211_ratectl_init(vap);
+ ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, run_media_change, ieee80211_media_status,
+ mac);
+
+ /* make sure id is always unique */
+ for (i = 0; i < RUN_VAP_MAX; i++) {
+ if((sc->rvp_bmap & 1 << i) == 0){
+ sc->rvp_bmap |= 1 << i;
+ rvp->rvp_id = i;
+ break;
+ }
+ }
+ if (sc->rvp_cnt++ == 0)
+ ic->ic_opmode = opmode;
+
+ if (opmode == IEEE80211_M_HOSTAP)
+ sc->cmdq_run = RUN_CMDQ_GO;
+
+ RUN_DPRINTF(sc, RUN_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n",
+ rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt);
+
+ return (vap);
+}
+
+static void
+run_vap_delete(struct ieee80211vap *vap)
+{
+ struct run_vap *rvp = RUN_VAP(vap);
+ struct ieee80211com *ic;
+ struct run_softc *sc;
+ uint8_t rvp_id;
+
+ if (vap == NULL)
+ return;
+
+ ic = vap->iv_ic;
+ sc = ic->ic_softc;
+
+ RUN_LOCK(sc);
+
+ m_freem(rvp->beacon_mbuf);
+ rvp->beacon_mbuf = NULL;
+
+ rvp_id = rvp->rvp_id;
+ sc->ratectl_run &= ~(1 << rvp_id);
+ sc->rvp_bmap &= ~(1 << rvp_id);
+ run_set_region_4(sc, RT2860_SKEY(rvp_id, 0), 0, 128);
+ run_set_region_4(sc, RT2860_BCN_BASE(rvp_id), 0, 512);
+ --sc->rvp_cnt;
+
+ RUN_DPRINTF(sc, RUN_DEBUG_STATE,
+ "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n",
+ vap, rvp_id, sc->rvp_bmap, sc->rvp_cnt);
+
+ RUN_UNLOCK(sc);
+
+ ieee80211_ratectl_deinit(vap);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
+/*
+ * There are numbers of functions need to be called in context thread.
+ * Rather than creating taskqueue event for each of those functions,
+ * here is all-for-one taskqueue callback function. This function
+ * guarantees deferred functions are executed in the same order they
+ * were enqueued.
+ * '& RUN_CMDQ_MASQ' is to loop cmdq[].
+ */
+static void
+run_cmdq_cb(void *arg, int pending)
+{
+ struct run_softc *sc = arg;
+ uint8_t i;
+
+ /* call cmdq[].func locked */
+ RUN_LOCK(sc);
+ for (i = sc->cmdq_exec; sc->cmdq[i].func && pending;
+ i = sc->cmdq_exec, pending--) {
+ RUN_DPRINTF(sc, RUN_DEBUG_CMD, "cmdq_exec=%d pending=%d\n",
+ i, pending);
+ if (sc->cmdq_run == RUN_CMDQ_GO) {
+ /*
+ * If arg0 is NULL, callback func needs more
+ * than one arg. So, pass ptr to cmdq struct.
+ */
+ if (sc->cmdq[i].arg0)
+ sc->cmdq[i].func(sc->cmdq[i].arg0);
+ else
+ sc->cmdq[i].func(&sc->cmdq[i]);
+ }
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].func = NULL;
+ sc->cmdq_exec++;
+ sc->cmdq_exec &= RUN_CMDQ_MASQ;
+ }
+ RUN_UNLOCK(sc);
+}
+
+static void
+run_setup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq)
+{
+ struct run_tx_data *data;
+
+ memset(pq, 0, sizeof(*pq));
+
+ STAILQ_INIT(&pq->tx_qh);
+ STAILQ_INIT(&pq->tx_fh);
+
+ for (data = &pq->tx_data[0];
+ data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) {
+ data->sc = sc;
+ STAILQ_INSERT_TAIL(&pq->tx_fh, data, next);
+ }
+ pq->tx_nfree = RUN_TX_RING_COUNT;
+}
+
+static void
+run_unsetup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq)
+{
+ struct run_tx_data *data;
+
+ /* make sure any subsequent use of the queues will fail */
+ pq->tx_nfree = 0;
+ STAILQ_INIT(&pq->tx_fh);
+ STAILQ_INIT(&pq->tx_qh);
+
+ /* free up all node references and mbufs */
+ for (data = &pq->tx_data[0];
+ data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) {
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static int
+run_load_microcode(struct run_softc *sc)
+{
+ usb_device_request_t req;
+ const struct firmware *fw;
+ const u_char *base;
+ uint32_t tmp;
+ int ntries, error;
+ const uint64_t *temp;
+ uint64_t bytes;
+
+ RUN_UNLOCK(sc);
+ fw = firmware_get("runfw");
+ RUN_LOCK(sc);
+ if (fw == NULL) {
+ device_printf(sc->sc_dev,
+ "failed loadfirmware of file %s\n", "runfw");
+ return ENOENT;
+ }
+
+ if (fw->datasize != 8192) {
+ device_printf(sc->sc_dev,
+ "invalid firmware size (should be 8KB)\n");
+ error = EINVAL;
+ goto fail;
+ }
+
+ /*
+ * RT3071/RT3072 use a different firmware
+ * run-rt2870 (8KB) contains both,
+ * first half (4KB) is for rt2870,
+ * last half is for rt3071.
+ */
+ base = fw->data;
+ if ((sc->mac_ver) != 0x2860 &&
+ (sc->mac_ver) != 0x2872 &&
+ (sc->mac_ver) != 0x3070) {
+ base += 4096;
+ }
+
+ /* cheap sanity check */
+ temp = fw->data;
+ bytes = *temp;
+ if (bytes != be64toh(0xffffff0210280210ULL)) {
+ device_printf(sc->sc_dev, "firmware checksum failed\n");
+ error = EINVAL;
+ goto fail;
+ }
+
+ /* write microcode image */
+ if (sc->sc_flags & RUN_FLAG_FWLOAD_NEEDED) {
+ run_write_region_1(sc, RT2870_FW_BASE, base, 4096);
+ run_write(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff);
+ run_write(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff);
+ }
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2870_RESET;
+ USETW(req.wValue, 8);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ if ((error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL))
+ != 0) {
+ device_printf(sc->sc_dev, "firmware reset failed\n");
+ goto fail;
+ }
+
+ run_delay(sc, 10);
+
+ run_write(sc, RT2860_H2M_BBPAGENT, 0);
+ run_write(sc, RT2860_H2M_MAILBOX, 0);
+ run_write(sc, RT2860_H2M_INTSRC, 0);
+ if ((error = run_mcu_cmd(sc, RT2860_MCU_CMD_RFRESET, 0)) != 0)
+ goto fail;
+
+ /* wait until microcontroller is ready */
+ for (ntries = 0; ntries < 1000; ntries++) {
+ if ((error = run_read(sc, RT2860_SYS_CTRL, &tmp)) != 0)
+ goto fail;
+ if (tmp & RT2860_MCU_READY)
+ break;
+ run_delay(sc, 10);
+ }
+ if (ntries == 1000) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for MCU to initialize\n");
+ error = ETIMEDOUT;
+ goto fail;
+ }
+ device_printf(sc->sc_dev, "firmware %s ver. %u.%u loaded\n",
+ (base == fw->data) ? "RT2870" : "RT3071",
+ *(base + 4092), *(base + 4093));
+
+fail:
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ return (error);
+}
+
+static int
+run_reset(struct run_softc *sc)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2870_RESET;
+ USETW(req.wValue, 1);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static usb_error_t
+run_do_request(struct run_softc *sc,
+ struct usb_device_request *req, void *data)
+{
+ usb_error_t err;
+ int ntries = 10;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ while (ntries--) {
+ err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+ req, data, 0, NULL, 250 /* ms */);
+ if (err == 0)
+ break;
+ RUN_DPRINTF(sc, RUN_DEBUG_USB,
+ "Control request failed, %s (retrying)\n",
+ usbd_errstr(err));
+ run_delay(sc, 10);
+ }
+ return (err);
+}
+
+static int
+run_read(struct run_softc *sc, uint16_t reg, uint32_t *val)
+{
+ uint32_t tmp;
+ int error;
+
+ error = run_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp);
+ if (error == 0)
+ *val = le32toh(tmp);
+ else
+ *val = 0xffffffff;
+ return (error);
+}
+
+static int
+run_read_region_1(struct run_softc *sc, uint16_t reg, uint8_t *buf, int len)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2870_READ_REGION_1;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ return (run_do_request(sc, &req, buf));
+}
+
+static int
+run_write_2(struct run_softc *sc, uint16_t reg, uint16_t val)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2870_WRITE_2;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ return (run_do_request(sc, &req, NULL));
+}
+
+static int
+run_write(struct run_softc *sc, uint16_t reg, uint32_t val)
+{
+ int error;
+
+ if ((error = run_write_2(sc, reg, val & 0xffff)) == 0)
+ error = run_write_2(sc, reg + 2, val >> 16);
+ return (error);
+}
+
+static int
+run_write_region_1(struct run_softc *sc, uint16_t reg, const uint8_t *buf,
+ int len)
+{
+#if 1
+ int i, error = 0;
+ /*
+ * NB: the WRITE_REGION_1 command is not stable on RT2860.
+ * We thus issue multiple WRITE_2 commands instead.
+ */
+ KASSERT((len & 1) == 0, ("run_write_region_1: Data too long.\n"));
+ for (i = 0; i < len && error == 0; i += 2)
+ error = run_write_2(sc, reg + i, buf[i] | buf[i + 1] << 8);
+ return (error);
+#else
+ usb_device_request_t req;
+ int error = 0;
+
+ /*
+ * NOTE: It appears the WRITE_REGION_1 command cannot be
+ * passed a huge amount of data, which will crash the
+ * firmware. Limit amount of data passed to 64-bytes at a
+ * time.
+ */
+ while (len > 0) {
+ int delta = 64;
+ if (delta > len)
+ delta = len;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2870_WRITE_REGION_1;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, delta);
+ error = run_do_request(sc, &req, __DECONST(uint8_t *, buf));
+ if (error != 0)
+ break;
+ reg += delta;
+ buf += delta;
+ len -= delta;
+ }
+ return (error);
+#endif
+}
+
+static int
+run_set_region_4(struct run_softc *sc, uint16_t reg, uint32_t val, int len)
+{
+ int i, error = 0;
+
+ KASSERT((len & 3) == 0, ("run_set_region_4: Invalid data length.\n"));
+ for (i = 0; i < len && error == 0; i += 4)
+ error = run_write(sc, reg + i, val);
+ return (error);
+}
+
+static int
+run_efuse_read(struct run_softc *sc, uint16_t addr, uint16_t *val, int count)
+{
+ uint32_t tmp;
+ uint16_t reg;
+ int error, ntries;
+
+ if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0)
+ return (error);
+
+ if (count == 2)
+ addr *= 2;
+ /*-
+ * Read one 16-byte block into registers EFUSE_DATA[0-3]:
+ * DATA0: F E D C
+ * DATA1: B A 9 8
+ * DATA2: 7 6 5 4
+ * DATA3: 3 2 1 0
+ */
+ tmp &= ~(RT3070_EFSROM_MODE_MASK | RT3070_EFSROM_AIN_MASK);
+ tmp |= (addr & ~0xf) << RT3070_EFSROM_AIN_SHIFT | RT3070_EFSROM_KICK;
+ run_write(sc, RT3070_EFUSE_CTRL, tmp);
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0)
+ return (error);
+ if (!(tmp & RT3070_EFSROM_KICK))
+ break;
+ run_delay(sc, 2);
+ }
+ if (ntries == 100)
+ return (ETIMEDOUT);
+
+ if ((tmp & RT3070_EFUSE_AOUT_MASK) == RT3070_EFUSE_AOUT_MASK) {
+ *val = 0xffff; /* address not found */
+ return (0);
+ }
+ /* determine to which 32-bit register our 16-bit word belongs */
+ reg = RT3070_EFUSE_DATA3 - (addr & 0xc);
+ if ((error = run_read(sc, reg, &tmp)) != 0)
+ return (error);
+
+ tmp >>= (8 * (addr & 0x3));
+ *val = (addr & 1) ? tmp >> 16 : tmp & 0xffff;
+
+ return (0);
+}
+
+/* Read 16-bit from eFUSE ROM for RT3xxx. */
+static int
+run_efuse_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val)
+{
+ return (run_efuse_read(sc, addr, val, 2));
+}
+
+static int
+run_eeprom_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val)
+{
+ usb_device_request_t req;
+ uint16_t tmp;
+ int error;
+
+ addr *= 2;
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2870_EEPROM_READ;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, sizeof(tmp));
+
+ error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &tmp);
+ if (error == 0)
+ *val = le16toh(tmp);
+ else
+ *val = 0xffff;
+ return (error);
+}
+
+static __inline int
+run_srom_read(struct run_softc *sc, uint16_t addr, uint16_t *val)
+{
+ /* either eFUSE ROM or EEPROM */
+ return sc->sc_srom_read(sc, addr, val);
+}
+
+static int
+run_rt2870_rf_write(struct run_softc *sc, uint32_t val)
+{
+ uint32_t tmp;
+ int error, ntries;
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = run_read(sc, RT2860_RF_CSR_CFG0, &tmp)) != 0)
+ return (error);
+ if (!(tmp & RT2860_RF_REG_CTRL))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ return (run_write(sc, RT2860_RF_CSR_CFG0, val));
+}
+
+static int
+run_rt3070_rf_read(struct run_softc *sc, uint8_t reg, uint8_t *val)
+{
+ uint32_t tmp;
+ int error, ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0)
+ return (error);
+ if (!(tmp & RT3070_RF_KICK))
+ break;
+ }
+ if (ntries == 100)
+ return (ETIMEDOUT);
+
+ tmp = RT3070_RF_KICK | reg << 8;
+ if ((error = run_write(sc, RT3070_RF_CSR_CFG, tmp)) != 0)
+ return (error);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0)
+ return (error);
+ if (!(tmp & RT3070_RF_KICK))
+ break;
+ }
+ if (ntries == 100)
+ return (ETIMEDOUT);
+
+ *val = tmp & 0xff;
+ return (0);
+}
+
+static int
+run_rt3070_rf_write(struct run_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint32_t tmp;
+ int error, ntries;
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0)
+ return (error);
+ if (!(tmp & RT3070_RF_KICK))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ tmp = RT3070_RF_WRITE | RT3070_RF_KICK | reg << 8 | val;
+ return (run_write(sc, RT3070_RF_CSR_CFG, tmp));
+}
+
+static int
+run_bbp_read(struct run_softc *sc, uint8_t reg, uint8_t *val)
+{
+ uint32_t tmp;
+ int ntries, error;
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0)
+ return (error);
+ if (!(tmp & RT2860_BBP_CSR_KICK))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ tmp = RT2860_BBP_CSR_READ | RT2860_BBP_CSR_KICK | reg << 8;
+ if ((error = run_write(sc, RT2860_BBP_CSR_CFG, tmp)) != 0)
+ return (error);
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0)
+ return (error);
+ if (!(tmp & RT2860_BBP_CSR_KICK))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ *val = tmp & 0xff;
+ return (0);
+}
+
+static int
+run_bbp_write(struct run_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint32_t tmp;
+ int ntries, error;
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0)
+ return (error);
+ if (!(tmp & RT2860_BBP_CSR_KICK))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ tmp = RT2860_BBP_CSR_KICK | reg << 8 | val;
+ return (run_write(sc, RT2860_BBP_CSR_CFG, tmp));
+}
+
+/*
+ * Send a command to the 8051 microcontroller unit.
+ */
+static int
+run_mcu_cmd(struct run_softc *sc, uint8_t cmd, uint16_t arg)
+{
+ uint32_t tmp;
+ int error, ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = run_read(sc, RT2860_H2M_MAILBOX, &tmp)) != 0)
+ return error;
+ if (!(tmp & RT2860_H2M_BUSY))
+ break;
+ }
+ if (ntries == 100)
+ return ETIMEDOUT;
+
+ tmp = RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg;
+ if ((error = run_write(sc, RT2860_H2M_MAILBOX, tmp)) == 0)
+ error = run_write(sc, RT2860_HOST_CMD, cmd);
+ return (error);
+}
+
+/*
+ * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word.
+ * Used to adjust per-rate Tx power registers.
+ */
+static __inline uint32_t
+b4inc(uint32_t b32, int8_t delta)
+{
+ int8_t i, b4;
+
+ for (i = 0; i < 8; i++) {
+ b4 = b32 & 0xf;
+ b4 += delta;
+ if (b4 < 0)
+ b4 = 0;
+ else if (b4 > 0xf)
+ b4 = 0xf;
+ b32 = b32 >> 4 | b4 << 28;
+ }
+ return (b32);
+}
+
+static const char *
+run_get_rf(uint16_t rev)
+{
+ switch (rev) {
+ case RT2860_RF_2820: return "RT2820";
+ case RT2860_RF_2850: return "RT2850";
+ case RT2860_RF_2720: return "RT2720";
+ case RT2860_RF_2750: return "RT2750";
+ case RT3070_RF_3020: return "RT3020";
+ case RT3070_RF_2020: return "RT2020";
+ case RT3070_RF_3021: return "RT3021";
+ case RT3070_RF_3022: return "RT3022";
+ case RT3070_RF_3052: return "RT3052";
+ case RT3593_RF_3053: return "RT3053";
+ case RT5592_RF_5592: return "RT5592";
+ case RT5390_RF_5370: return "RT5370";
+ case RT5390_RF_5372: return "RT5372";
+ }
+ return ("unknown");
+}
+
+static void
+run_rt3593_get_txpower(struct run_softc *sc)
+{
+ uint16_t addr, val;
+ int i;
+
+ /* Read power settings for 2GHz channels. */
+ for (i = 0; i < 14; i += 2) {
+ addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE1 :
+ RT2860_EEPROM_PWR2GHZ_BASE1;
+ run_srom_read(sc, addr + i / 2, &val);
+ sc->txpow1[i + 0] = (int8_t)(val & 0xff);
+ sc->txpow1[i + 1] = (int8_t)(val >> 8);
+
+ addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE2 :
+ RT2860_EEPROM_PWR2GHZ_BASE2;
+ run_srom_read(sc, addr + i / 2, &val);
+ sc->txpow2[i + 0] = (int8_t)(val & 0xff);
+ sc->txpow2[i + 1] = (int8_t)(val >> 8);
+
+ if (sc->ntxchains == 3) {
+ run_srom_read(sc, RT3593_EEPROM_PWR2GHZ_BASE3 + i / 2,
+ &val);
+ sc->txpow3[i + 0] = (int8_t)(val & 0xff);
+ sc->txpow3[i + 1] = (int8_t)(val >> 8);
+ }
+ }
+ /* Fix broken Tx power entries. */
+ for (i = 0; i < 14; i++) {
+ if (sc->txpow1[i] > 31)
+ sc->txpow1[i] = 5;
+ if (sc->txpow2[i] > 31)
+ sc->txpow2[i] = 5;
+ if (sc->ntxchains == 3) {
+ if (sc->txpow3[i] > 31)
+ sc->txpow3[i] = 5;
+ }
+ }
+ /* Read power settings for 5GHz channels. */
+ for (i = 0; i < 40; i += 2) {
+ run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE1 + i / 2, &val);
+ sc->txpow1[i + 14] = (int8_t)(val & 0xff);
+ sc->txpow1[i + 15] = (int8_t)(val >> 8);
+
+ run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE2 + i / 2, &val);
+ sc->txpow2[i + 14] = (int8_t)(val & 0xff);
+ sc->txpow2[i + 15] = (int8_t)(val >> 8);
+
+ if (sc->ntxchains == 3) {
+ run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE3 + i / 2,
+ &val);
+ sc->txpow3[i + 14] = (int8_t)(val & 0xff);
+ sc->txpow3[i + 15] = (int8_t)(val >> 8);
+ }
+ }
+}
+
+static void
+run_get_txpower(struct run_softc *sc)
+{
+ uint16_t val;
+ int i;
+
+ /* Read power settings for 2GHz channels. */
+ for (i = 0; i < 14; i += 2) {
+ run_srom_read(sc, RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2, &val);
+ sc->txpow1[i + 0] = (int8_t)(val & 0xff);
+ sc->txpow1[i + 1] = (int8_t)(val >> 8);
+
+ if (sc->mac_ver != 0x5390) {
+ run_srom_read(sc,
+ RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2, &val);
+ sc->txpow2[i + 0] = (int8_t)(val & 0xff);
+ sc->txpow2[i + 1] = (int8_t)(val >> 8);
+ }
+ }
+ /* Fix broken Tx power entries. */
+ for (i = 0; i < 14; i++) {
+ if (sc->mac_ver >= 0x5390) {
+ if (sc->txpow1[i] < 0 || sc->txpow1[i] > 39)
+ sc->txpow1[i] = 5;
+ } else {
+ if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31)
+ sc->txpow1[i] = 5;
+ }
+ if (sc->mac_ver > 0x5390) {
+ if (sc->txpow2[i] < 0 || sc->txpow2[i] > 39)
+ sc->txpow2[i] = 5;
+ } else if (sc->mac_ver < 0x5390) {
+ if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31)
+ sc->txpow2[i] = 5;
+ }
+ RUN_DPRINTF(sc, RUN_DEBUG_TXPWR,
+ "chan %d: power1=%d, power2=%d\n",
+ rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]);
+ }
+ /* Read power settings for 5GHz channels. */
+ for (i = 0; i < 40; i += 2) {
+ run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2, &val);
+ sc->txpow1[i + 14] = (int8_t)(val & 0xff);
+ sc->txpow1[i + 15] = (int8_t)(val >> 8);
+
+ run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2, &val);
+ sc->txpow2[i + 14] = (int8_t)(val & 0xff);
+ sc->txpow2[i + 15] = (int8_t)(val >> 8);
+ }
+ /* Fix broken Tx power entries. */
+ for (i = 0; i < 40; i++ ) {
+ if (sc->mac_ver != 0x5592) {
+ if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15)
+ sc->txpow1[14 + i] = 5;
+ if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15)
+ sc->txpow2[14 + i] = 5;
+ }
+ RUN_DPRINTF(sc, RUN_DEBUG_TXPWR,
+ "chan %d: power1=%d, power2=%d\n",
+ rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i],
+ sc->txpow2[14 + i]);
+ }
+}
+
+static int
+run_read_eeprom(struct run_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ int8_t delta_2ghz, delta_5ghz;
+ uint32_t tmp;
+ uint16_t val;
+ int ridx, ant, i;
+
+ /* check whether the ROM is eFUSE ROM or EEPROM */
+ sc->sc_srom_read = run_eeprom_read_2;
+ if (sc->mac_ver >= 0x3070) {
+ run_read(sc, RT3070_EFUSE_CTRL, &tmp);
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EFUSE_CTRL=0x%08x\n", tmp);
+ if ((tmp & RT3070_SEL_EFUSE) || sc->mac_ver == 0x3593)
+ sc->sc_srom_read = run_efuse_read_2;
+ }
+
+ /* read ROM version */
+ run_srom_read(sc, RT2860_EEPROM_VERSION, &val);
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM,
+ "EEPROM rev=%d, FAE=%d\n", val >> 8, val & 0xff);
+
+ /* read MAC address */
+ run_srom_read(sc, RT2860_EEPROM_MAC01, &val);
+ ic->ic_macaddr[0] = val & 0xff;
+ ic->ic_macaddr[1] = val >> 8;
+ run_srom_read(sc, RT2860_EEPROM_MAC23, &val);
+ ic->ic_macaddr[2] = val & 0xff;
+ ic->ic_macaddr[3] = val >> 8;
+ run_srom_read(sc, RT2860_EEPROM_MAC45, &val);
+ ic->ic_macaddr[4] = val & 0xff;
+ ic->ic_macaddr[5] = val >> 8;
+
+ if (sc->mac_ver < 0x3593) {
+ /* read vender BBP settings */
+ for (i = 0; i < 10; i++) {
+ run_srom_read(sc, RT2860_EEPROM_BBP_BASE + i, &val);
+ sc->bbp[i].val = val & 0xff;
+ sc->bbp[i].reg = val >> 8;
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM,
+ "BBP%d=0x%02x\n", sc->bbp[i].reg, sc->bbp[i].val);
+ }
+ if (sc->mac_ver >= 0x3071) {
+ /* read vendor RF settings */
+ for (i = 0; i < 10; i++) {
+ run_srom_read(sc, RT3071_EEPROM_RF_BASE + i,
+ &val);
+ sc->rf[i].val = val & 0xff;
+ sc->rf[i].reg = val >> 8;
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM, "RF%d=0x%02x\n",
+ sc->rf[i].reg, sc->rf[i].val);
+ }
+ }
+ }
+
+ /* read RF frequency offset from EEPROM */
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS :
+ RT3593_EEPROM_FREQ, &val);
+ sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0;
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM freq offset %d\n",
+ sc->freq & 0xff);
+
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS :
+ RT3593_EEPROM_FREQ_LEDS, &val);
+ if (val >> 8 != 0xff) {
+ /* read LEDs operating mode */
+ sc->leds = val >> 8;
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED1 :
+ RT3593_EEPROM_LED1, &sc->led[0]);
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED2 :
+ RT3593_EEPROM_LED2, &sc->led[1]);
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED3 :
+ RT3593_EEPROM_LED3, &sc->led[2]);
+ } else {
+ /* broken EEPROM, use default settings */
+ sc->leds = 0x01;
+ sc->led[0] = 0x5555;
+ sc->led[1] = 0x2221;
+ sc->led[2] = 0x5627; /* differs from RT2860 */
+ }
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM,
+ "EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n",
+ sc->leds, sc->led[0], sc->led[1], sc->led[2]);
+
+ /* read RF information */
+ if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392)
+ run_srom_read(sc, 0x00, &val);
+ else
+ run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val);
+
+ if (val == 0xffff) {
+ device_printf(sc->sc_dev,
+ "invalid EEPROM antenna info, using default\n");
+ if (sc->mac_ver == 0x3572) {
+ /* default to RF3052 2T2R */
+ sc->rf_rev = RT3070_RF_3052;
+ sc->ntxchains = 2;
+ sc->nrxchains = 2;
+ } else if (sc->mac_ver >= 0x3070) {
+ /* default to RF3020 1T1R */
+ sc->rf_rev = RT3070_RF_3020;
+ sc->ntxchains = 1;
+ sc->nrxchains = 1;
+ } else {
+ /* default to RF2820 1T2R */
+ sc->rf_rev = RT2860_RF_2820;
+ sc->ntxchains = 1;
+ sc->nrxchains = 2;
+ }
+ } else {
+ if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) {
+ sc->rf_rev = val;
+ run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val);
+ } else
+ sc->rf_rev = (val >> 8) & 0xf;
+ sc->ntxchains = (val >> 4) & 0xf;
+ sc->nrxchains = val & 0xf;
+ }
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM RF rev=0x%04x chains=%dT%dR\n",
+ sc->rf_rev, sc->ntxchains, sc->nrxchains);
+
+ /* check if RF supports automatic Tx access gain control */
+ run_srom_read(sc, RT2860_EEPROM_CONFIG, &val);
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM CFG 0x%04x\n", val);
+ /* check if driver should patch the DAC issue */
+ if ((val >> 8) != 0xff)
+ sc->patch_dac = (val >> 15) & 1;
+ if ((val & 0xff) != 0xff) {
+ sc->ext_5ghz_lna = (val >> 3) & 1;
+ sc->ext_2ghz_lna = (val >> 2) & 1;
+ /* check if RF supports automatic Tx access gain control */
+ sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1;
+ /* check if we have a hardware radio switch */
+ sc->rfswitch = val & 1;
+ }
+
+ /* Read Tx power settings. */
+ if (sc->mac_ver == 0x3593)
+ run_rt3593_get_txpower(sc);
+ else
+ run_get_txpower(sc);
+
+ /* read Tx power compensation for each Tx rate */
+ run_srom_read(sc, RT2860_EEPROM_DELTAPWR, &val);
+ delta_2ghz = delta_5ghz = 0;
+ if ((val & 0xff) != 0xff && (val & 0x80)) {
+ delta_2ghz = val & 0xf;
+ if (!(val & 0x40)) /* negative number */
+ delta_2ghz = -delta_2ghz;
+ }
+ val >>= 8;
+ if ((val & 0xff) != 0xff && (val & 0x80)) {
+ delta_5ghz = val & 0xf;
+ if (!(val & 0x40)) /* negative number */
+ delta_5ghz = -delta_5ghz;
+ }
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR,
+ "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz, delta_5ghz);
+
+ for (ridx = 0; ridx < 5; ridx++) {
+ uint32_t reg;
+
+ run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2, &val);
+ reg = val;
+ run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2 + 1, &val);
+ reg |= (uint32_t)val << 16;
+
+ sc->txpow20mhz[ridx] = reg;
+ sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz);
+ sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz);
+
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR,
+ "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, "
+ "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx],
+ sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]);
+ }
+
+ /* Read RSSI offsets and LNA gains from EEPROM. */
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_2GHZ :
+ RT3593_EEPROM_RSSI1_2GHZ, &val);
+ sc->rssi_2ghz[0] = val & 0xff; /* Ant A */
+ sc->rssi_2ghz[1] = val >> 8; /* Ant B */
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_2GHZ :
+ RT3593_EEPROM_RSSI2_2GHZ, &val);
+ if (sc->mac_ver >= 0x3070) {
+ if (sc->mac_ver == 0x3593) {
+ sc->txmixgain_2ghz = 0;
+ sc->rssi_2ghz[2] = val & 0xff; /* Ant C */
+ } else {
+ /*
+ * On RT3070 chips (limited to 2 Rx chains), this ROM
+ * field contains the Tx mixer gain for the 2GHz band.
+ */
+ if ((val & 0xff) != 0xff)
+ sc->txmixgain_2ghz = val & 0x7;
+ }
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n",
+ sc->txmixgain_2ghz);
+ } else
+ sc->rssi_2ghz[2] = val & 0xff; /* Ant C */
+ if (sc->mac_ver == 0x3593)
+ run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val);
+ sc->lna[2] = val >> 8; /* channel group 2 */
+
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_5GHZ :
+ RT3593_EEPROM_RSSI1_5GHZ, &val);
+ sc->rssi_5ghz[0] = val & 0xff; /* Ant A */
+ sc->rssi_5ghz[1] = val >> 8; /* Ant B */
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_5GHZ :
+ RT3593_EEPROM_RSSI2_5GHZ, &val);
+ if (sc->mac_ver == 0x3572) {
+ /*
+ * On RT3572 chips (limited to 2 Rx chains), this ROM
+ * field contains the Tx mixer gain for the 5GHz band.
+ */
+ if ((val & 0xff) != 0xff)
+ sc->txmixgain_5ghz = val & 0x7;
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (5GHz)\n",
+ sc->txmixgain_5ghz);
+ } else
+ sc->rssi_5ghz[2] = val & 0xff; /* Ant C */
+ if (sc->mac_ver == 0x3593) {
+ sc->txmixgain_5ghz = 0;
+ run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val);
+ }
+ sc->lna[3] = val >> 8; /* channel group 3 */
+
+ run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LNA :
+ RT3593_EEPROM_LNA, &val);
+ sc->lna[0] = val & 0xff; /* channel group 0 */
+ sc->lna[1] = val >> 8; /* channel group 1 */
+
+ /* fix broken 5GHz LNA entries */
+ if (sc->lna[2] == 0 || sc->lna[2] == 0xff) {
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM,
+ "invalid LNA for channel group %d\n", 2);
+ sc->lna[2] = sc->lna[1];
+ }
+ if (sc->lna[3] == 0 || sc->lna[3] == 0xff) {
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM,
+ "invalid LNA for channel group %d\n", 3);
+ sc->lna[3] = sc->lna[1];
+ }
+
+ /* fix broken RSSI offset entries */
+ for (ant = 0; ant < 3; ant++) {
+ if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) {
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI,
+ "invalid RSSI%d offset: %d (2GHz)\n",
+ ant + 1, sc->rssi_2ghz[ant]);
+ sc->rssi_2ghz[ant] = 0;
+ }
+ if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) {
+ RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI,
+ "invalid RSSI%d offset: %d (5GHz)\n",
+ ant + 1, sc->rssi_5ghz[ant]);
+ sc->rssi_5ghz[ant] = 0;
+ }
+ }
+ return (0);
+}
+
+static struct ieee80211_node *
+run_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ return malloc(sizeof (struct run_node), M_DEVBUF, M_NOWAIT | M_ZERO);
+}
+
+static int
+run_media_change(struct ifnet *ifp)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_txparam *tp;
+ struct run_softc *sc = ic->ic_softc;
+ uint8_t rate, ridx;
+ int error;
+
+ RUN_LOCK(sc);
+
+ error = ieee80211_media_change(ifp);
+ if (error != ENETRESET) {
+ RUN_UNLOCK(sc);
+ return (error);
+ }
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+ if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ struct ieee80211_node *ni;
+ struct run_node *rn;
+
+ rate = ic->ic_sup_rates[ic->ic_curmode].
+ rs_rates[tp->ucastrate] & IEEE80211_RATE_VAL;
+ for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == rate)
+ break;
+ ni = ieee80211_ref_node(vap->iv_bss);
+ rn = RUN_NODE(ni);
+ rn->fix_ridx = ridx;
+ RUN_DPRINTF(sc, RUN_DEBUG_RATE, "rate=%d, fix_ridx=%d\n",
+ rate, rn->fix_ridx);
+ ieee80211_free_node(ni);
+ }
+
+#if 0
+ if ((ifp->if_flags & IFF_UP) &&
+ (ifp->if_drv_flags & RUN_RUNNING)){
+ run_init_locked(sc);
+ }
+#endif
+
+ RUN_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ const struct ieee80211_txparam *tp;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_softc;
+ struct run_vap *rvp = RUN_VAP(vap);
+ enum ieee80211_state ostate;
+ uint32_t sta[3];
+ uint32_t tmp;
+ uint8_t ratectl;
+ uint8_t restart_ratectl = 0;
+ uint8_t bid = 1 << rvp->rvp_id;
+
+ ostate = vap->iv_state;
+ RUN_DPRINTF(sc, RUN_DEBUG_STATE, "%s -> %s\n",
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+
+ IEEE80211_UNLOCK(ic);
+ RUN_LOCK(sc);
+
+ ratectl = sc->ratectl_run; /* remember current state */
+ sc->ratectl_run = RUN_RATECTL_OFF;
+ usb_callout_stop(&sc->ratectl_ch);
+
+ if (ostate == IEEE80211_S_RUN) {
+ /* turn link LED off */
+ run_set_leds(sc, RT2860_LED_RADIO);
+ }
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ restart_ratectl = 1;
+
+ if (ostate != IEEE80211_S_RUN)
+ break;
+
+ ratectl &= ~bid;
+ sc->runbmap &= ~bid;
+
+ /* abort TSF synchronization if there is no vap running */
+ if (--sc->running == 0) {
+ run_read(sc, RT2860_BCN_TIME_CFG, &tmp);
+ run_write(sc, RT2860_BCN_TIME_CFG,
+ tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN |
+ RT2860_TBTT_TIMER_EN));
+ }
+ break;
+
+ case IEEE80211_S_RUN:
+ if (!(sc->runbmap & bid)) {
+ if(sc->running++)
+ restart_ratectl = 1;
+ sc->runbmap |= bid;
+ }
+
+ m_freem(rvp->beacon_mbuf);
+ rvp->beacon_mbuf = NULL;
+
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
+ sc->ap_running |= bid;
+ ic->ic_opmode = vap->iv_opmode;
+ run_update_beacon_cb(vap);
+ break;
+ case IEEE80211_M_IBSS:
+ sc->adhoc_running |= bid;
+ if (!sc->ap_running)
+ ic->ic_opmode = vap->iv_opmode;
+ run_update_beacon_cb(vap);
+ break;
+ case IEEE80211_M_STA:
+ sc->sta_running |= bid;
+ if (!sc->ap_running && !sc->adhoc_running)
+ ic->ic_opmode = vap->iv_opmode;
+
+ /* read statistic counters (clear on read) */
+ run_read_region_1(sc, RT2860_TX_STA_CNT0,
+ (uint8_t *)sta, sizeof sta);
+
+ break;
+ default:
+ ic->ic_opmode = vap->iv_opmode;
+ break;
+ }
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ struct ieee80211_node *ni;
+
+ if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) {
+ RUN_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (-1);
+ }
+ run_updateslot(ic);
+ run_enable_mrr(sc);
+ run_set_txpreamble(sc);
+ run_set_basicrates(sc);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ run_set_bssid(sc, sc->sc_bssid);
+ ieee80211_free_node(ni);
+ run_enable_tsf_sync(sc);
+
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ ratectl |= bid;
+ } else
+ run_enable_tsf(sc);
+
+ /* turn link LED on */
+ run_set_leds(sc, RT2860_LED_RADIO |
+ (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ?
+ RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ));
+
+ break;
+ default:
+ RUN_DPRINTF(sc, RUN_DEBUG_STATE, "undefined state\n");
+ break;
+ }
+
+ /* restart amrr for running VAPs */
+ if ((sc->ratectl_run = ratectl) && restart_ratectl)
+ usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
+
+ RUN_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+
+ return(rvp->newstate(vap, nstate, arg));
+}
+
+static int
+run_wme_update(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_softc;
+ const struct wmeParams *ac =
+ ic->ic_wme.wme_chanParams.cap_wmeParams;
+ int aci, error = 0;
+
+ /* update MAC TX configuration registers */
+ RUN_LOCK(sc);
+ for (aci = 0; aci < WME_NUM_AC; aci++) {
+ error = run_write(sc, RT2860_EDCA_AC_CFG(aci),
+ ac[aci].wmep_logcwmax << 16 |
+ ac[aci].wmep_logcwmin << 12 |
+ ac[aci].wmep_aifsn << 8 |
+ ac[aci].wmep_txopLimit);
+ if (error) goto err;
+ }
+
+ /* update SCH/DMA registers too */
+ error = run_write(sc, RT2860_WMM_AIFSN_CFG,
+ ac[WME_AC_VO].wmep_aifsn << 12 |
+ ac[WME_AC_VI].wmep_aifsn << 8 |
+ ac[WME_AC_BK].wmep_aifsn << 4 |
+ ac[WME_AC_BE].wmep_aifsn);
+ if (error) goto err;
+ error = run_write(sc, RT2860_WMM_CWMIN_CFG,
+ ac[WME_AC_VO].wmep_logcwmin << 12 |
+ ac[WME_AC_VI].wmep_logcwmin << 8 |
+ ac[WME_AC_BK].wmep_logcwmin << 4 |
+ ac[WME_AC_BE].wmep_logcwmin);
+ if (error) goto err;
+ error = run_write(sc, RT2860_WMM_CWMAX_CFG,
+ ac[WME_AC_VO].wmep_logcwmax << 12 |
+ ac[WME_AC_VI].wmep_logcwmax << 8 |
+ ac[WME_AC_BK].wmep_logcwmax << 4 |
+ ac[WME_AC_BE].wmep_logcwmax);
+ if (error) goto err;
+ error = run_write(sc, RT2860_WMM_TXOP0_CFG,
+ ac[WME_AC_BK].wmep_txopLimit << 16 |
+ ac[WME_AC_BE].wmep_txopLimit);
+ if (error) goto err;
+ error = run_write(sc, RT2860_WMM_TXOP1_CFG,
+ ac[WME_AC_VO].wmep_txopLimit << 16 |
+ ac[WME_AC_VI].wmep_txopLimit);
+
+err:
+ RUN_UNLOCK(sc);
+ if (error)
+ RUN_DPRINTF(sc, RUN_DEBUG_USB, "WME update failed\n");
+
+ return (error);
+}
+
+static void
+run_key_set_cb(void *arg)
+{
+ struct run_cmdq *cmdq = arg;
+ struct ieee80211vap *vap = cmdq->arg1;
+ struct ieee80211_key *k = cmdq->k;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_softc;
+ struct ieee80211_node *ni;
+ u_int cipher = k->wk_cipher->ic_cipher;
+ uint32_t attr;
+ uint16_t base, associd;
+ uint8_t mode, wcid, iv[8];
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac);
+ else
+ ni = vap->iv_bss;
+ associd = (ni != NULL) ? ni->ni_associd : 0;
+
+ /* map net80211 cipher to RT2860 security mode */
+ switch (cipher) {
+ case IEEE80211_CIPHER_WEP:
+ if(k->wk_keylen < 8)
+ mode = RT2860_MODE_WEP40;
+ else
+ mode = RT2860_MODE_WEP104;
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ mode = RT2860_MODE_TKIP;
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ mode = RT2860_MODE_AES_CCMP;
+ break;
+ default:
+ RUN_DPRINTF(sc, RUN_DEBUG_KEY, "undefined case\n");
+ return;
+ }
+
+ RUN_DPRINTF(sc, RUN_DEBUG_KEY,
+ "associd=%x, keyix=%d, mode=%x, type=%s, tx=%s, rx=%s\n",
+ associd, k->wk_keyix, mode,
+ (k->wk_flags & IEEE80211_KEY_GROUP) ? "group" : "pairwise",
+ (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off",
+ (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off");
+
+ if (k->wk_flags & IEEE80211_KEY_GROUP) {
+ wcid = 0; /* NB: update WCID0 for group keys */
+ base = RT2860_SKEY(RUN_VAP(vap)->rvp_id, k->wk_keyix);
+ } else {
+ wcid = (vap->iv_opmode == IEEE80211_M_STA) ?
+ 1 : RUN_AID2WCID(associd);
+ base = RT2860_PKEY(wcid);
+ }
+
+ if (cipher == IEEE80211_CIPHER_TKIP) {
+ if(run_write_region_1(sc, base, k->wk_key, 16))
+ return;
+ if(run_write_region_1(sc, base + 16, &k->wk_key[16], 8)) /* wk_txmic */
+ return;
+ if(run_write_region_1(sc, base + 24, &k->wk_key[24], 8)) /* wk_rxmic */
+ return;
+ } else {
+ /* roundup len to 16-bit: XXX fix write_region_1() instead */
+ if(run_write_region_1(sc, base, k->wk_key, (k->wk_keylen + 1) & ~1))
+ return;
+ }
+
+ if (!(k->wk_flags & IEEE80211_KEY_GROUP) ||
+ (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) {
+ /* set initial packet number in IV+EIV */
+ if (cipher == IEEE80211_CIPHER_WEP) {
+ memset(iv, 0, sizeof iv);
+ iv[3] = vap->iv_def_txkey << 6;
+ } else {
+ if (cipher == IEEE80211_CIPHER_TKIP) {
+ iv[0] = k->wk_keytsc >> 8;
+ iv[1] = (iv[0] | 0x20) & 0x7f;
+ iv[2] = k->wk_keytsc;
+ } else /* CCMP */ {
+ iv[0] = k->wk_keytsc;
+ iv[1] = k->wk_keytsc >> 8;
+ iv[2] = 0;
+ }
+ iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV;
+ iv[4] = k->wk_keytsc >> 16;
+ iv[5] = k->wk_keytsc >> 24;
+ iv[6] = k->wk_keytsc >> 32;
+ iv[7] = k->wk_keytsc >> 40;
+ }
+ if (run_write_region_1(sc, RT2860_IVEIV(wcid), iv, 8))
+ return;
+ }
+
+ if (k->wk_flags & IEEE80211_KEY_GROUP) {
+ /* install group key */
+ if (run_read(sc, RT2860_SKEY_MODE_0_7, &attr))
+ return;
+ attr &= ~(0xf << (k->wk_keyix * 4));
+ attr |= mode << (k->wk_keyix * 4);
+ if (run_write(sc, RT2860_SKEY_MODE_0_7, attr))
+ return;
+ } else {
+ /* install pairwise key */
+ if (run_read(sc, RT2860_WCID_ATTR(wcid), &attr))
+ return;
+ attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN;
+ if (run_write(sc, RT2860_WCID_ATTR(wcid), attr))
+ return;
+ }
+
+ /* TODO create a pass-thru key entry? */
+
+ /* need wcid to delete the right key later */
+ k->wk_pad = wcid;
+}
+
+/*
+ * Don't have to be deferred, but in order to keep order of
+ * execution, i.e. with run_key_delete(), defer this and let
+ * run_cmdq_cb() maintain the order.
+ *
+ * return 0 on error
+ */
+static int
+run_key_set(struct ieee80211vap *vap, struct ieee80211_key *k)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_softc;
+ uint32_t i;
+
+ i = RUN_CMDQ_GET(&sc->cmdq_store);
+ RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_key_set_cb;
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].arg1 = vap;
+ sc->cmdq[i].k = k;
+ IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr);
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ /*
+ * To make sure key will be set when hostapd
+ * calls iv_key_set() before if_init().
+ */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ RUN_LOCK(sc);
+ sc->cmdq_key_set = RUN_CMDQ_GO;
+ RUN_UNLOCK(sc);
+ }
+
+ return (1);
+}
+
+/*
+ * If wlan is destroyed without being brought down i.e. without
+ * wlan down or wpa_cli terminate, this function is called after
+ * vap is gone. Don't refer it.
+ */
+static void
+run_key_delete_cb(void *arg)
+{
+ struct run_cmdq *cmdq = arg;
+ struct run_softc *sc = cmdq->arg1;
+ struct ieee80211_key *k = &cmdq->key;
+ uint32_t attr;
+ uint8_t wcid;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (k->wk_flags & IEEE80211_KEY_GROUP) {
+ /* remove group key */
+ RUN_DPRINTF(sc, RUN_DEBUG_KEY, "removing group key\n");
+ run_read(sc, RT2860_SKEY_MODE_0_7, &attr);
+ attr &= ~(0xf << (k->wk_keyix * 4));
+ run_write(sc, RT2860_SKEY_MODE_0_7, attr);
+ } else {
+ /* remove pairwise key */
+ RUN_DPRINTF(sc, RUN_DEBUG_KEY,
+ "removing key for wcid %x\n", k->wk_pad);
+ /* matching wcid was written to wk_pad in run_key_set() */
+ wcid = k->wk_pad;
+ run_read(sc, RT2860_WCID_ATTR(wcid), &attr);
+ attr &= ~0xf;
+ run_write(sc, RT2860_WCID_ATTR(wcid), attr);
+ run_set_region_4(sc, RT2860_WCID_ENTRY(wcid), 0, 8);
+ }
+
+ k->wk_pad = 0;
+}
+
+/*
+ * return 0 on error
+ */
+static int
+run_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_softc;
+ struct ieee80211_key *k0;
+ uint32_t i;
+
+ /*
+ * When called back, key might be gone. So, make a copy
+ * of some values need to delete keys before deferring.
+ * But, because of LOR with node lock, cannot use lock here.
+ * So, use atomic instead.
+ */
+ i = RUN_CMDQ_GET(&sc->cmdq_store);
+ RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_key_delete_cb;
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].arg1 = sc;
+ k0 = &sc->cmdq[i].key;
+ k0->wk_flags = k->wk_flags;
+ k0->wk_keyix = k->wk_keyix;
+ /* matching wcid was written to wk_pad in run_key_set() */
+ k0->wk_pad = k->wk_pad;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ return (1); /* return fake success */
+
+}
+
+static void
+run_ratectl_to(void *arg)
+{
+ struct run_softc *sc = arg;
+
+ /* do it in a process context, so it can go sleep */
+ ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task);
+ /* next timeout will be rescheduled in the callback task */
+}
+
+/* ARGSUSED */
+static void
+run_ratectl_cb(void *arg, int pending)
+{
+ struct run_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ if (vap == NULL)
+ return;
+
+ if (sc->rvp_cnt > 1 || vap->iv_opmode != IEEE80211_M_STA) {
+ /*
+ * run_reset_livelock() doesn't do anything with AMRR,
+ * but Ralink wants us to call it every 1 sec. So, we
+ * piggyback here rather than creating another callout.
+ * Livelock may occur only in HOSTAP or IBSS mode
+ * (when h/w is sending beacons).
+ */
+ RUN_LOCK(sc);
+ run_reset_livelock(sc);
+ /* just in case, there are some stats to drain */
+ run_drain_fifo(sc);
+ RUN_UNLOCK(sc);
+ }
+
+ ieee80211_iterate_nodes(&ic->ic_sta, run_iter_func, sc);
+
+ RUN_LOCK(sc);
+ if(sc->ratectl_run != RUN_RATECTL_OFF)
+ usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
+ RUN_UNLOCK(sc);
+}
+
+static void
+run_drain_fifo(void *arg)
+{
+ struct run_softc *sc = arg;
+ uint32_t stat;
+ uint16_t (*wstat)[3];
+ uint8_t wcid, mcs, pid;
+ int8_t retry;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ for (;;) {
+ /* drain Tx status FIFO (maxsize = 16) */
+ run_read(sc, RT2860_TX_STAT_FIFO, &stat);
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx stat 0x%08x\n", stat);
+ if (!(stat & RT2860_TXQ_VLD))
+ break;
+
+ wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff;
+
+ /* if no ACK was requested, no feedback is available */
+ if (!(stat & RT2860_TXQ_ACKREQ) || wcid > RT2870_WCID_MAX ||
+ wcid == 0)
+ continue;
+
+ /*
+ * Even though each stat is Tx-complete-status like format,
+ * the device can poll stats. Because there is no guarantee
+ * that the referring node is still around when read the stats.
+ * So that, if we use ieee80211_ratectl_tx_update(), we will
+ * have hard time not to refer already freed node.
+ *
+ * To eliminate such page faults, we poll stats in softc.
+ * Then, update the rates later with ieee80211_ratectl_tx_update().
+ */
+ wstat = &(sc->wcid_stats[wcid]);
+ (*wstat)[RUN_TXCNT]++;
+ if (stat & RT2860_TXQ_OK)
+ (*wstat)[RUN_SUCCESS]++;
+ else
+ counter_u64_add(sc->sc_ic.ic_oerrors, 1);
+ /*
+ * Check if there were retries, ie if the Tx success rate is
+ * different from the requested rate. Note that it works only
+ * because we do not allow rate fallback from OFDM to CCK.
+ */
+ mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f;
+ pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf;
+ if ((retry = pid -1 - mcs) > 0) {
+ (*wstat)[RUN_TXCNT] += retry;
+ (*wstat)[RUN_RETRY] += retry;
+ }
+ }
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt);
+
+ sc->fifo_cnt = 0;
+}
+
+static void
+run_iter_func(void *arg, struct ieee80211_node *ni)
+{
+ struct run_softc *sc = arg;
+ struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct run_node *rn = RUN_NODE(ni);
+ union run_stats sta[2];
+ uint16_t (*wstat)[3];
+ int error;
+
+ RUN_LOCK(sc);
+
+ /* Check for special case */
+ if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA &&
+ ni != vap->iv_bss)
+ goto fail;
+
+ txs->flags = IEEE80211_RATECTL_TX_STATS_NODE |
+ IEEE80211_RATECTL_TX_STATS_RETRIES;
+ txs->ni = ni;
+ if (sc->rvp_cnt <= 1 && (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_STA)) {
+ /* read statistic counters (clear on read) and update AMRR state */
+ error = run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta,
+ sizeof sta);
+ if (error != 0)
+ goto fail;
+
+ /* count failed TX as errors */
+ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS,
+ le16toh(sta[0].error.fail));
+
+ txs->nretries = le16toh(sta[1].tx.retry);
+ txs->nsuccess = le16toh(sta[1].tx.success);
+ /* nretries??? */
+ txs->nframes = txs->nretries + txs->nsuccess +
+ le16toh(sta[0].error.fail);
+
+ RUN_DPRINTF(sc, RUN_DEBUG_RATE,
+ "retrycnt=%d success=%d failcnt=%d\n",
+ txs->nretries, txs->nsuccess, le16toh(sta[0].error.fail));
+ } else {
+ wstat = &(sc->wcid_stats[RUN_AID2WCID(ni->ni_associd)]);
+
+ if (wstat == &(sc->wcid_stats[0]) ||
+ wstat > &(sc->wcid_stats[RT2870_WCID_MAX]))
+ goto fail;
+
+ txs->nretries = (*wstat)[RUN_RETRY];
+ txs->nsuccess = (*wstat)[RUN_SUCCESS];
+ txs->nframes = (*wstat)[RUN_TXCNT];
+ RUN_DPRINTF(sc, RUN_DEBUG_RATE,
+ "retrycnt=%d txcnt=%d success=%d\n",
+ txs->nretries, txs->nframes, txs->nsuccess);
+
+ memset(wstat, 0, sizeof(*wstat));
+ }
+
+ ieee80211_ratectl_tx_update(vap, txs);
+ rn->amrr_ridx = ieee80211_ratectl_rate(ni, NULL, 0);
+
+fail:
+ RUN_UNLOCK(sc);
+
+ RUN_DPRINTF(sc, RUN_DEBUG_RATE, "ridx=%d\n", rn->amrr_ridx);
+}
+
+static void
+run_newassoc_cb(void *arg)
+{
+ struct run_cmdq *cmdq = arg;
+ struct ieee80211_node *ni = cmdq->arg1;
+ struct run_softc *sc = ni->ni_vap->iv_ic->ic_softc;
+ uint8_t wcid = cmdq->wcid;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ run_write_region_1(sc, RT2860_WCID_ENTRY(wcid),
+ ni->ni_macaddr, IEEE80211_ADDR_LEN);
+
+ memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid]));
+}
+
+static void
+run_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct run_node *rn = RUN_NODE(ni);
+ struct ieee80211_rateset *rs = &ni->ni_rates;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_softc;
+ uint8_t rate;
+ uint8_t ridx;
+ uint8_t wcid;
+ int i, j;
+
+ wcid = (vap->iv_opmode == IEEE80211_M_STA) ?
+ 1 : RUN_AID2WCID(ni->ni_associd);
+
+ if (wcid > RT2870_WCID_MAX) {
+ device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid);
+ return;
+ }
+
+ /* only interested in true associations */
+ if (isnew && ni->ni_associd != 0) {
+
+ /*
+ * This function could is called though timeout function.
+ * Need to defer.
+ */
+ uint32_t cnt = RUN_CMDQ_GET(&sc->cmdq_store);
+ RUN_DPRINTF(sc, RUN_DEBUG_STATE, "cmdq_store=%d\n", cnt);
+ sc->cmdq[cnt].func = run_newassoc_cb;
+ sc->cmdq[cnt].arg0 = NULL;
+ sc->cmdq[cnt].arg1 = ni;
+ sc->cmdq[cnt].wcid = wcid;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ }
+
+ RUN_DPRINTF(sc, RUN_DEBUG_STATE,
+ "new assoc isnew=%d associd=%x addr=%s\n",
+ isnew, ni->ni_associd, ether_sprintf(ni->ni_macaddr));
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ rate = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+ /* convert 802.11 rate to hardware rate index */
+ for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == rate)
+ break;
+ rn->ridx[i] = ridx;
+ /* determine rate of control response frames */
+ for (j = i; j >= 0; j--) {
+ if ((rs->rs_rates[j] & IEEE80211_RATE_BASIC) &&
+ rt2860_rates[rn->ridx[i]].phy ==
+ rt2860_rates[rn->ridx[j]].phy)
+ break;
+ }
+ if (j >= 0) {
+ rn->ctl_ridx[i] = rn->ridx[j];
+ } else {
+ /* no basic rate found, use mandatory one */
+ rn->ctl_ridx[i] = rt2860_rates[ridx].ctl_ridx;
+ }
+ RUN_DPRINTF(sc, RUN_DEBUG_STATE | RUN_DEBUG_RATE,
+ "rate=0x%02x ridx=%d ctl_ridx=%d\n",
+ rs->rs_rates[i], rn->ridx[i], rn->ctl_ridx[i]);
+ }
+ rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
+ for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == rate)
+ break;
+ rn->mgt_ridx = ridx;
+ RUN_DPRINTF(sc, RUN_DEBUG_STATE | RUN_DEBUG_RATE,
+ "rate=%d, mgmt_ridx=%d\n", rate, rn->mgt_ridx);
+
+ RUN_LOCK(sc);
+ if(sc->ratectl_run != RUN_RATECTL_OFF)
+ usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
+ RUN_UNLOCK(sc);
+}
+
+/*
+ * Return the Rx chain with the highest RSSI for a given frame.
+ */
+static __inline uint8_t
+run_maxrssi_chain(struct run_softc *sc, const struct rt2860_rxwi *rxwi)
+{
+ uint8_t rxchain = 0;
+
+ if (sc->nrxchains > 1) {
+ if (rxwi->rssi[1] > rxwi->rssi[rxchain])
+ rxchain = 1;
+ if (sc->nrxchains > 2)
+ if (rxwi->rssi[2] > rxwi->rssi[rxchain])
+ rxchain = 2;
+ }
+ return (rxchain);
+}
+
+static void
+run_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct run_softc *sc = vap->iv_ic->ic_softc;
+ struct run_vap *rvp = RUN_VAP(vap);
+ uint64_t ni_tstamp, rx_tstamp;
+
+ rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);
+
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
+ subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
+ ni_tstamp = le64toh(ni->ni_tstamp.tsf);
+ RUN_LOCK(sc);
+ run_get_tsf(sc, &rx_tstamp);
+ RUN_UNLOCK(sc);
+ rx_tstamp = le64toh(rx_tstamp);
+
+ if (ni_tstamp >= rx_tstamp) {
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV | RUN_DEBUG_BEACON,
+ "ibss merge, tsf %ju tstamp %ju\n",
+ (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp);
+ (void) ieee80211_ibss_merge(ni);
+ }
+ }
+}
+
+static void
+run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct rt2870_rxd *rxd;
+ struct rt2860_rxwi *rxwi;
+ uint32_t flags;
+ uint16_t len, rxwisize;
+ uint8_t ant, rssi;
+ int8_t nf;
+
+ rxwi = mtod(m, struct rt2860_rxwi *);
+ len = le16toh(rxwi->len) & 0xfff;
+ rxwisize = sizeof(struct rt2860_rxwi);
+ if (sc->mac_ver == 0x5592)
+ rxwisize += sizeof(uint64_t);
+ else if (sc->mac_ver == 0x3593)
+ rxwisize += sizeof(uint32_t);
+ if (__predict_false(len > dmalen)) {
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV,
+ "bad RXWI length %u > %u\n", len, dmalen);
+ return;
+ }
+ /* Rx descriptor is located at the end */
+ rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen);
+ flags = le32toh(rxd->flags);
+
+ if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) {
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s error.\n",
+ (flags & RT2860_RX_CRCERR)?"CRC":"ICV");
+ return;
+ }
+
+ m->m_data += rxwisize;
+ m->m_pkthdr.len = m->m_len -= rxwisize;
+
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+ m->m_flags |= M_WEP;
+ }
+
+ if (flags & RT2860_RX_L2PAD) {
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV,
+ "received RT2860_RX_L2PAD frame\n");
+ len += 2;
+ }
+
+ ni = ieee80211_find_rxnode(ic,
+ mtod(m, struct ieee80211_frame_min *));
+
+ if (__predict_false(flags & RT2860_RX_MICERR)) {
+ /* report MIC failures to net80211 for TKIP */
+ if (ni != NULL)
+ ieee80211_notify_michael_failure(ni->ni_vap, wh,
+ rxwi->keyidx);
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV,
+ "MIC error. Someone is lying.\n");
+ return;
+ }
+
+ ant = run_maxrssi_chain(sc, rxwi);
+ rssi = rxwi->rssi[ant];
+ nf = run_rssi2dbm(sc, rssi, ant);
+
+ m->m_pkthdr.len = m->m_len = len;
+
+ if (__predict_false(ieee80211_radiotap_active(ic))) {
+ struct run_rx_radiotap_header *tap = &sc->sc_rxtap;
+ uint16_t phy;
+
+ tap->wr_flags = 0;
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_antsignal = rssi;
+ tap->wr_antenna = ant;
+ tap->wr_dbm_antsignal = run_rssi2dbm(sc, rssi, ant);
+ tap->wr_rate = 2; /* in case it can't be found below */
+ RUN_LOCK(sc);
+ run_get_tsf(sc, &tap->wr_tsf);
+ RUN_UNLOCK(sc);
+ phy = le16toh(rxwi->phy);
+ switch (phy & RT2860_PHY_MODE) {
+ case RT2860_PHY_CCK:
+ switch ((phy & RT2860_PHY_MCS) & ~RT2860_PHY_SHPRE) {
+ case 0: tap->wr_rate = 2; break;
+ case 1: tap->wr_rate = 4; break;
+ case 2: tap->wr_rate = 11; break;
+ case 3: tap->wr_rate = 22; break;
+ }
+ if (phy & RT2860_PHY_SHPRE)
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+ break;
+ case RT2860_PHY_OFDM:
+ switch (phy & RT2860_PHY_MCS) {
+ case 0: tap->wr_rate = 12; break;
+ case 1: tap->wr_rate = 18; break;
+ case 2: tap->wr_rate = 24; break;
+ case 3: tap->wr_rate = 36; break;
+ case 4: tap->wr_rate = 48; break;
+ case 5: tap->wr_rate = 72; break;
+ case 6: tap->wr_rate = 96; break;
+ case 7: tap->wr_rate = 108; break;
+ }
+ break;
+ }
+ }
+
+ if (ni != NULL) {
+ (void)ieee80211_input(ni, m, rssi, nf);
+ ieee80211_free_node(ni);
+ } else {
+ (void)ieee80211_input_all(ic, m, rssi, nf);
+ }
+}
+
+static void
+run_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct run_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct mbuf *m = NULL;
+ struct mbuf *m0;
+ uint32_t dmalen;
+ uint16_t rxwisize;
+ int xferlen;
+
+ rxwisize = sizeof(struct rt2860_rxwi);
+ if (sc->mac_ver == 0x5592)
+ rxwisize += sizeof(uint64_t);
+ else if (sc->mac_ver == 0x3593)
+ rxwisize += sizeof(uint32_t);
+
+ usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV,
+ "rx done, actlen=%d\n", xferlen);
+
+ if (xferlen < (int)(sizeof(uint32_t) + rxwisize +
+ sizeof(struct rt2870_rxd))) {
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
+ "xfer too short %d\n", xferlen);
+ goto tr_setup;
+ }
+
+ m = sc->rx_m;
+ sc->rx_m = NULL;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if (sc->rx_m == NULL) {
+ sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
+ MJUMPAGESIZE /* xfer can be bigger than MCLBYTES */);
+ }
+ if (sc->rx_m == NULL) {
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV | RUN_DEBUG_RECV_DESC,
+ "could not allocate mbuf - idle with stall\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ usbd_xfer_set_stall(xfer);
+ usbd_xfer_set_frames(xfer, 0);
+ } else {
+ /*
+ * Directly loading a mbuf cluster into DMA to
+ * save some data copying. This works because
+ * there is only one cluster.
+ */
+ usbd_xfer_set_frame_data(xfer, 0,
+ mtod(sc->rx_m, caddr_t), RUN_MAX_RXSZ);
+ usbd_xfer_set_frames(xfer, 1);
+ }
+ usbd_transfer_submit(xfer);
+ break;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ if (error == USB_ERR_TIMEOUT)
+ device_printf(sc->sc_dev, "device timeout\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ if (sc->rx_m != NULL) {
+ m_freem(sc->rx_m);
+ sc->rx_m = NULL;
+ }
+ break;
+ }
+
+ if (m == NULL)
+ return;
+
+ /* inputting all the frames must be last */
+
+ RUN_UNLOCK(sc);
+
+ m->m_pkthdr.len = m->m_len = xferlen;
+
+ /* HW can aggregate multiple 802.11 frames in a single USB xfer */
+ for(;;) {
+ dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff;
+
+ if ((dmalen >= (uint32_t)-8) || (dmalen == 0) ||
+ ((dmalen & 3) != 0)) {
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
+ "bad DMA length %u\n", dmalen);
+ break;
+ }
+ if ((dmalen + 8) > (uint32_t)xferlen) {
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
+ "bad DMA length %u > %d\n",
+ dmalen + 8, xferlen);
+ break;
+ }
+
+ /* If it is the last one or a single frame, we won't copy. */
+ if ((xferlen -= dmalen + 8) <= 8) {
+ /* trim 32-bit DMA-len header */
+ m->m_data += 4;
+ m->m_pkthdr.len = m->m_len -= 4;
+ run_rx_frame(sc, m, dmalen);
+ m = NULL; /* don't free source buffer */
+ break;
+ }
+
+ /* copy aggregated frames to another mbuf */
+ m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (__predict_false(m0 == NULL)) {
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC,
+ "could not allocate mbuf\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ break;
+ }
+ m_copydata(m, 4 /* skip 32-bit DMA-len header */,
+ dmalen + sizeof(struct rt2870_rxd), mtod(m0, caddr_t));
+ m0->m_pkthdr.len = m0->m_len =
+ dmalen + sizeof(struct rt2870_rxd);
+ run_rx_frame(sc, m0, dmalen);
+
+ /* update data ptr */
+ m->m_data += dmalen + 8;
+ m->m_pkthdr.len = m->m_len -= dmalen + 8;
+ }
+
+ /* make sure we free the source buffer, if any */
+ m_freem(m);
+
+ RUN_LOCK(sc);
+}
+
+static void
+run_tx_free(struct run_endpoint_queue *pq,
+ struct run_tx_data *data, int txerr)
+{
+
+ ieee80211_tx_complete(data->ni, data->m, txerr);
+
+ data->m = NULL;
+ data->ni = NULL;
+
+ STAILQ_INSERT_TAIL(&pq->tx_fh, data, next);
+ pq->tx_nfree++;
+}
+
+static void
+run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index)
+{
+ struct run_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct run_tx_data *data;
+ struct ieee80211vap *vap = NULL;
+ struct usb_page_cache *pc;
+ struct run_endpoint_queue *pq = &sc->sc_epq[index];
+ struct mbuf *m;
+ usb_frlength_t size;
+ int actlen;
+ int sumlen;
+
+ usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB,
+ "transfer complete: %d bytes @ index %d\n", actlen, index);
+
+ data = usbd_xfer_get_priv(xfer);
+ run_tx_free(pq, data, 0);
+ usbd_xfer_set_priv(xfer, NULL);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&pq->tx_qh);
+ if (data == NULL)
+ break;
+
+ STAILQ_REMOVE_HEAD(&pq->tx_qh, next);
+
+ m = data->m;
+ size = (sc->mac_ver == 0x5592) ?
+ sizeof(data->desc) + sizeof(uint32_t) : sizeof(data->desc);
+ if ((m->m_pkthdr.len +
+ size + 3 + 8) > RUN_MAX_TXSZ) {
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT_DESC | RUN_DEBUG_USB,
+ "data overflow, %u bytes\n", m->m_pkthdr.len);
+ run_tx_free(pq, data, 1);
+ goto tr_setup;
+ }
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &data->desc, size);
+ usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len);
+ size += m->m_pkthdr.len;
+ /*
+ * Align end on a 4-byte boundary, pad 8 bytes (CRC +
+ * 4-byte padding), and be sure to zero those trailing
+ * bytes:
+ */
+ usbd_frame_zero(pc, size, ((-size) & 3) + 8);
+ size += ((-size) & 3) + 8;
+
+ vap = data->ni->ni_vap;
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct run_tx_radiotap_header *tap = &sc->sc_txtap;
+ struct rt2860_txwi *txwi =
+ (struct rt2860_txwi *)(&data->desc + sizeof(struct rt2870_txd));
+ tap->wt_flags = 0;
+ tap->wt_rate = rt2860_rates[data->ridx].rate;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wt_hwqueue = index;
+ if (le16toh(txwi->phy) & RT2860_PHY_SHPRE)
+ tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+ ieee80211_radiotap_tx(vap, m);
+ }
+
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB,
+ "sending frame len=%u/%u @ index %d\n",
+ m->m_pkthdr.len, size, index);
+
+ usbd_xfer_set_frame_len(xfer, 0, size);
+ usbd_xfer_set_priv(xfer, data);
+ usbd_transfer_submit(xfer);
+ run_start(sc);
+
+ break;
+
+ default:
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB,
+ "USB transfer error, %s\n", usbd_errstr(error));
+
+ data = usbd_xfer_get_priv(xfer);
+
+ if (data != NULL) {
+ if(data->ni != NULL)
+ vap = data->ni->ni_vap;
+ run_tx_free(pq, data, error);
+ usbd_xfer_set_priv(xfer, NULL);
+ }
+
+ if (vap == NULL)
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ if (error != USB_ERR_CANCELLED) {
+ if (error == USB_ERR_TIMEOUT) {
+ device_printf(sc->sc_dev, "device timeout\n");
+ uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store);
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB,
+ "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_usb_timeout_cb;
+ sc->cmdq[i].arg0 = vap;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ }
+
+ /*
+ * Try to clear stall first, also if other
+ * errors occur, hence clearing stall
+ * introduces a 50 ms delay:
+ */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+run_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error)
+{
+ run_bulk_tx_callbackN(xfer, error, 0);
+}
+
+static void
+run_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error)
+{
+ run_bulk_tx_callbackN(xfer, error, 1);
+}
+
+static void
+run_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error)
+{
+ run_bulk_tx_callbackN(xfer, error, 2);
+}
+
+static void
+run_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error)
+{
+ run_bulk_tx_callbackN(xfer, error, 3);
+}
+
+static void
+run_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error)
+{
+ run_bulk_tx_callbackN(xfer, error, 4);
+}
+
+static void
+run_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error)
+{
+ run_bulk_tx_callbackN(xfer, error, 5);
+}
+
+static void
+run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data)
+{
+ struct mbuf *m = data->m;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = data->ni->ni_vap;
+ struct ieee80211_frame *wh;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
+ uint16_t xferlen, txwisize;
+ uint16_t mcs;
+ uint8_t ridx = data->ridx;
+ uint8_t pad;
+
+ /* get MCS code from rate index */
+ mcs = rt2860_rates[ridx].mcs;
+
+ txwisize = (sc->mac_ver == 0x5592) ?
+ sizeof(*txwi) + sizeof(uint32_t) : sizeof(*txwi);
+ xferlen = txwisize + m->m_pkthdr.len;
+
+ /* roundup to 32-bit alignment */
+ xferlen = (xferlen + 3) & ~3;
+
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->len = htole16(xferlen);
+
+ wh = mtod(m, struct ieee80211_frame *);
+
+ /*
+ * Ether both are true or both are false, the header
+ * are nicely aligned to 32-bit. So, no L2 padding.
+ */
+ if(IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh))
+ pad = 0;
+ else
+ pad = 2;
+
+ /* setup TX Wireless Information */
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->len = htole16(m->m_pkthdr.len - pad);
+ if (rt2860_rates[ridx].phy == IEEE80211_T_DS) {
+ mcs |= RT2860_PHY_CCK;
+ if (ridx != RT2860_RIDX_CCK1 &&
+ (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ mcs |= RT2860_PHY_SHPRE;
+ } else
+ mcs |= RT2860_PHY_OFDM;
+ txwi->phy = htole16(mcs);
+
+ /* check if RTS/CTS or CTS-to-self protection is required */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold ||
+ ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ rt2860_rates[ridx].phy == IEEE80211_T_OFDM)))
+ txwi->txop |= RT2860_TX_TXOP_HT;
+ else
+ txwi->txop |= RT2860_TX_TXOP_BACKOFF;
+
+ if (vap->iv_opmode != IEEE80211_M_STA && !IEEE80211_QOS_HAS_SEQ(wh))
+ txwi->xflags |= RT2860_TX_NSEQ;
+}
+
+/* This function must be called locked */
+static int
+run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_frame *wh;
+ struct ieee80211_channel *chan;
+ const struct ieee80211_txparam *tp;
+ struct run_node *rn = RUN_NODE(ni);
+ struct run_tx_data *data;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
+ uint16_t qos;
+ uint16_t dur;
+ uint16_t qid;
+ uint8_t type;
+ uint8_t tid;
+ uint8_t ridx;
+ uint8_t ctl_ridx;
+ uint8_t qflags;
+ uint8_t xflags = 0;
+ int hasqos;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ wh = mtod(m, struct ieee80211_frame *);
+
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+ /*
+ * There are 7 bulk endpoints: 1 for RX
+ * and 6 for TX (4 EDCAs + HCCA + Prio).
+ * Update 03-14-2009: some devices like the Planex GW-US300MiniS
+ * seem to have only 4 TX bulk endpoints (Fukaumi Naoki).
+ */
+ if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) {
+ uint8_t *frm;
+
+ if(IEEE80211_HAS_ADDR4(wh))
+ frm = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos;
+ else
+ frm =((struct ieee80211_qosframe *)wh)->i_qos;
+
+ qos = le16toh(*(const uint16_t *)frm);
+ tid = qos & IEEE80211_QOS_TID;
+ qid = TID_TO_WME_AC(tid);
+ } else {
+ qos = 0;
+ tid = 0;
+ qid = WME_AC_BE;
+ }
+ qflags = (qid < 4) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_HCCA;
+
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "qos %d\tqid %d\ttid %d\tqflags %x\n",
+ qos, qid, tid, qflags);
+
+ chan = (ni->ni_chan != IEEE80211_CHAN_ANYC)?ni->ni_chan:ic->ic_curchan;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(chan)];
+
+ /* pickup a rate index */
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+ type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) {
+ ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
+ RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1;
+ ctl_ridx = rt2860_rates[ridx].ctl_ridx;
+ } else {
+ if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ ridx = rn->fix_ridx;
+ else
+ ridx = rn->amrr_ridx;
+ ctl_ridx = rt2860_rates[ridx].ctl_ridx;
+ }
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) !=
+ IEEE80211_QOS_ACKPOLICY_NOACK)) {
+ xflags |= RT2860_TX_ACK;
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ dur = rt2860_rates[ctl_ridx].sp_ack_dur;
+ else
+ dur = rt2860_rates[ctl_ridx].lp_ack_dur;
+ USETW(wh->i_dur, dur);
+ }
+
+ /* reserve slots for mgmt packets, just in case */
+ if (sc->sc_epq[qid].tx_nfree < 3) {
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx ring %d is full\n", qid);
+ return (-1);
+ }
+
+ data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh);
+ STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next);
+ sc->sc_epq[qid].tx_nfree--;
+
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->flags = qflags;
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->xflags = xflags;
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ txwi->wcid = 0;
+ else
+ txwi->wcid = (vap->iv_opmode == IEEE80211_M_STA) ?
+ 1 : RUN_AID2WCID(ni->ni_associd);
+
+ /* clear leftover garbage bits */
+ txwi->flags = 0;
+ txwi->txop = 0;
+
+ data->m = m;
+ data->ni = ni;
+ data->ridx = ridx;
+
+ run_set_tx_desc(sc, data);
+
+ /*
+ * The chip keeps track of 2 kind of Tx stats,
+ * * TX_STAT_FIFO, for per WCID stats, and
+ * * TX_STA_CNT0 for all-TX-in-one stats.
+ *
+ * To use FIFO stats, we need to store MCS into the driver-private
+ * PacketID field. So that, we can tell whose stats when we read them.
+ * We add 1 to the MCS because setting the PacketID field to 0 means
+ * that we don't want feedback in TX_STAT_FIFO.
+ * And, that's what we want for STA mode, since TX_STA_CNT0 does the job.
+ *
+ * FIFO stats doesn't count Tx with WCID 0xff, so we do this in run_tx().
+ */
+ if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS) {
+ uint16_t pid = (rt2860_rates[ridx].mcs + 1) & 0xf;
+ txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT);
+
+ /*
+ * Unlike PCI based devices, we don't get any interrupt from
+ * USB devices, so we simulate FIFO-is-full interrupt here.
+ * Ralink recommends to drain FIFO stats every 100 ms, but 16 slots
+ * quickly get fulled. To prevent overflow, increment a counter on
+ * every FIFO stat request, so we know how many slots are left.
+ * We do this only in HOSTAP or multiple vap mode since FIFO stats
+ * are used only in those modes.
+ * We just drain stats. AMRR gets updated every 1 sec by
+ * run_ratectl_cb() via callout.
+ * Call it early. Otherwise overflow.
+ */
+ if (sc->fifo_cnt++ == 10) {
+ /*
+ * With multiple vaps or if_bridge, if_start() is called
+ * with a non-sleepable lock, tcpinp. So, need to defer.
+ */
+ uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store);
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_drain_fifo;
+ sc->cmdq[i].arg0 = sc;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ }
+ }
+
+ STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next);
+
+ usbd_transfer_start(sc->sc_xfer[qid]);
+
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT,
+ "sending data frame len=%d rate=%d qid=%d\n",
+ m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) +
+ sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate, qid);
+
+ return (0);
+}
+
+static int
+run_tx_mgt(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct run_node *rn = RUN_NODE(ni);
+ struct run_tx_data *data;
+ struct ieee80211_frame *wh;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
+ uint16_t dur;
+ uint8_t ridx = rn->mgt_ridx;
+ uint8_t type;
+ uint8_t xflags = 0;
+ uint8_t wflags = 0;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ wh = mtod(m, struct ieee80211_frame *);
+
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+ /* tell hardware to add timestamp for probe responses */
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
+ wflags |= RT2860_TX_TS;
+ else if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ xflags |= RT2860_TX_ACK;
+
+ dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ USETW(wh->i_dur, dur);
+ }
+
+ if (sc->sc_epq[0].tx_nfree == 0)
+ /* let caller free mbuf */
+ return (EIO);
+ data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
+ STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
+ sc->sc_epq[0].tx_nfree--;
+
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->flags = RT2860_TX_QSEL_EDCA;
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->flags = wflags;
+ txwi->xflags = xflags;
+ txwi->txop = 0; /* clear leftover garbage bits */
+
+ data->m = m;
+ data->ni = ni;
+ data->ridx = ridx;
+
+ run_set_tx_desc(sc, data);
+
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n",
+ m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) +
+ sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate);
+
+ STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
+
+ usbd_transfer_start(sc->sc_xfer[0]);
+
+ return (0);
+}
+
+static int
+run_sendprot(struct run_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ struct run_tx_data *data;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
+ struct mbuf *mprot;
+ int ridx;
+ int protrate;
+ int ackrate;
+ int pktlen;
+ int isshort;
+ uint16_t dur;
+ uint8_t type;
+ uint8_t wflags = 0;
+ uint8_t xflags = 0;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+ protrate = ieee80211_ctl_rate(ic->ic_rt, rate);
+ ackrate = ieee80211_ack_rate(ic->ic_rt, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort)
+ + ieee80211_ack_duration(ic->ic_rt, rate, isshort);
+ wflags = RT2860_TX_FRAG;
+
+ /* check that there are free slots before allocating the mbuf */
+ if (sc->sc_epq[0].tx_nfree == 0)
+ /* let caller free mbuf */
+ return (ENOBUFS);
+
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort);
+ xflags |= RT2860_TX_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1);
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "could not allocate mbuf\n");
+ return (ENOBUFS);
+ }
+
+ data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
+ STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
+ sc->sc_epq[0].tx_nfree--;
+
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->flags = RT2860_TX_QSEL_EDCA;
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->flags = wflags;
+ txwi->xflags = xflags;
+ txwi->txop = 0; /* clear leftover garbage bits */
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+
+ for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == protrate)
+ break;
+ data->ridx = ridx;
+
+ run_set_tx_desc(sc, data);
+
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending prot len=%u rate=%u\n",
+ m->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
+
+ usbd_transfer_start(sc->sc_xfer[0]);
+
+ return (0);
+}
+
+static int
+run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ struct run_tx_data *data;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
+ uint8_t type;
+ uint8_t ridx;
+ uint8_t rate;
+ uint8_t opflags = 0;
+ uint8_t xflags = 0;
+ int error;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ wh = mtod(m, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+ rate = params->ibp_rate0;
+ if (!ieee80211_isratevalid(ic->ic_rt, rate)) {
+ /* let caller free mbuf */
+ return (EINVAL);
+ }
+
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ xflags |= RT2860_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = run_sendprot(sc, m, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ /* let caller free mbuf */
+ return error;
+ }
+ opflags |= /*XXX RT2573_TX_LONG_RETRY |*/ RT2860_TX_TXOP_SIFS;
+ }
+
+ if (sc->sc_epq[0].tx_nfree == 0) {
+ /* let caller free mbuf */
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT,
+ "sending raw frame, but tx ring is full\n");
+ return (EIO);
+ }
+ data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
+ STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
+ sc->sc_epq[0].tx_nfree--;
+
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->flags = RT2860_TX_QSEL_EDCA;
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->xflags = xflags;
+ txwi->txop = opflags;
+ txwi->flags = 0; /* clear leftover garbage bits */
+
+ data->m = m;
+ data->ni = ni;
+ for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == rate)
+ break;
+ data->ridx = ridx;
+
+ run_set_tx_desc(sc, data);
+
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending raw frame len=%u rate=%u\n",
+ m->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
+
+ usbd_transfer_start(sc->sc_xfer[0]);
+
+ return (0);
+}
+
+static int
+run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct run_softc *sc = ni->ni_ic->ic_softc;
+ int error = 0;
+
+ RUN_LOCK(sc);
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(sc->sc_flags & RUN_RUNNING)) {
+ error = ENETDOWN;
+ goto done;
+ }
+
+ if (params == NULL) {
+ /* tx mgt packet */
+ if ((error = run_tx_mgt(sc, m, ni)) != 0) {
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "mgt tx failed\n");
+ goto done;
+ }
+ } else {
+ /* tx raw packet with param */
+ if ((error = run_tx_param(sc, m, ni, params)) != 0) {
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx with param failed\n");
+ goto done;
+ }
+ }
+
+done:
+ RUN_UNLOCK(sc);
+
+ if (error != 0) {
+ if(m != NULL)
+ m_freem(m);
+ }
+
+ return (error);
+}
+
+static int
+run_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct run_softc *sc = ic->ic_softc;
+ int error;
+
+ RUN_LOCK(sc);
+ if ((sc->sc_flags & RUN_RUNNING) == 0) {
+ RUN_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ RUN_UNLOCK(sc);
+ return (error);
+ }
+ run_start(sc);
+ RUN_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+run_start(struct run_softc *sc)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ if ((sc->sc_flags & RUN_RUNNING) == 0)
+ return;
+
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ if (run_tx(sc, m, ni) != 0) {
+ mbufq_prepend(&sc->sc_snd, m);
+ break;
+ }
+ }
+}
+
+static void
+run_parent(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_softc;
+ int startall = 0;
+
+ RUN_LOCK(sc);
+ if (sc->sc_detached) {
+ RUN_UNLOCK(sc);
+ return;
+ }
+
+ if (ic->ic_nrunning > 0) {
+ if (!(sc->sc_flags & RUN_RUNNING)) {
+ startall = 1;
+ run_init_locked(sc);
+ } else
+ run_update_promisc_locked(sc);
+ } else if ((sc->sc_flags & RUN_RUNNING) && sc->rvp_cnt <= 1)
+ run_stop(sc);
+ RUN_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+}
+
+static void
+run_iq_calib(struct run_softc *sc, u_int chan)
+{
+ uint16_t val;
+
+ /* Tx0 IQ gain. */
+ run_bbp_write(sc, 158, 0x2c);
+ if (chan <= 14)
+ run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ, &val, 1);
+ else if (chan <= 64) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ,
+ &val, 1);
+ } else if (chan <= 138) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ,
+ &val, 1);
+ } else if (chan <= 165) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ,
+ &val, 1);
+ } else
+ val = 0;
+ run_bbp_write(sc, 159, val);
+
+ /* Tx0 IQ phase. */
+ run_bbp_write(sc, 158, 0x2d);
+ if (chan <= 14) {
+ run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ,
+ &val, 1);
+ } else if (chan <= 64) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ,
+ &val, 1);
+ } else if (chan <= 138) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ,
+ &val, 1);
+ } else if (chan <= 165) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ,
+ &val, 1);
+ } else
+ val = 0;
+ run_bbp_write(sc, 159, val);
+
+ /* Tx1 IQ gain. */
+ run_bbp_write(sc, 158, 0x4a);
+ if (chan <= 14) {
+ run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ,
+ &val, 1);
+ } else if (chan <= 64) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ,
+ &val, 1);
+ } else if (chan <= 138) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ,
+ &val, 1);
+ } else if (chan <= 165) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ,
+ &val, 1);
+ } else
+ val = 0;
+ run_bbp_write(sc, 159, val);
+
+ /* Tx1 IQ phase. */
+ run_bbp_write(sc, 158, 0x4b);
+ if (chan <= 14) {
+ run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ,
+ &val, 1);
+ } else if (chan <= 64) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ,
+ &val, 1);
+ } else if (chan <= 138) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ,
+ &val, 1);
+ } else if (chan <= 165) {
+ run_efuse_read(sc,
+ RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ,
+ &val, 1);
+ } else
+ val = 0;
+ run_bbp_write(sc, 159, val);
+
+ /* RF IQ compensation control. */
+ run_bbp_write(sc, 158, 0x04);
+ run_efuse_read(sc, RT5390_EEPROM_RF_IQ_COMPENSATION_CTL,
+ &val, 1);
+ run_bbp_write(sc, 159, val);
+
+ /* RF IQ imbalance compensation control. */
+ run_bbp_write(sc, 158, 0x03);
+ run_efuse_read(sc,
+ RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL, &val, 1);
+ run_bbp_write(sc, 159, val);
+}
+
+static void
+run_set_agc(struct run_softc *sc, uint8_t agc)
+{
+ uint8_t bbp;
+
+ if (sc->mac_ver == 0x3572) {
+ run_bbp_read(sc, 27, &bbp);
+ bbp &= ~(0x3 << 5);
+ run_bbp_write(sc, 27, bbp | 0 << 5); /* select Rx0 */
+ run_bbp_write(sc, 66, agc);
+ run_bbp_write(sc, 27, bbp | 1 << 5); /* select Rx1 */
+ run_bbp_write(sc, 66, agc);
+ } else
+ run_bbp_write(sc, 66, agc);
+}
+
+static void
+run_select_chan_group(struct run_softc *sc, int group)
+{
+ uint32_t tmp;
+ uint8_t agc;
+
+ run_bbp_write(sc, 62, 0x37 - sc->lna[group]);
+ run_bbp_write(sc, 63, 0x37 - sc->lna[group]);
+ run_bbp_write(sc, 64, 0x37 - sc->lna[group]);
+ if (sc->mac_ver < 0x3572)
+ run_bbp_write(sc, 86, 0x00);
+
+ if (sc->mac_ver == 0x3593) {
+ run_bbp_write(sc, 77, 0x98);
+ run_bbp_write(sc, 83, (group == 0) ? 0x8a : 0x9a);
+ }
+
+ if (group == 0) {
+ if (sc->ext_2ghz_lna) {
+ if (sc->mac_ver >= 0x5390)
+ run_bbp_write(sc, 75, 0x52);
+ else {
+ run_bbp_write(sc, 82, 0x62);
+ run_bbp_write(sc, 75, 0x46);
+ }
+ } else {
+ if (sc->mac_ver == 0x5592) {
+ run_bbp_write(sc, 79, 0x1c);
+ run_bbp_write(sc, 80, 0x0e);
+ run_bbp_write(sc, 81, 0x3a);
+ run_bbp_write(sc, 82, 0x62);
+
+ run_bbp_write(sc, 195, 0x80);
+ run_bbp_write(sc, 196, 0xe0);
+ run_bbp_write(sc, 195, 0x81);
+ run_bbp_write(sc, 196, 0x1f);
+ run_bbp_write(sc, 195, 0x82);
+ run_bbp_write(sc, 196, 0x38);
+ run_bbp_write(sc, 195, 0x83);
+ run_bbp_write(sc, 196, 0x32);
+ run_bbp_write(sc, 195, 0x85);
+ run_bbp_write(sc, 196, 0x28);
+ run_bbp_write(sc, 195, 0x86);
+ run_bbp_write(sc, 196, 0x19);
+ } else if (sc->mac_ver >= 0x5390)
+ run_bbp_write(sc, 75, 0x50);
+ else {
+ run_bbp_write(sc, 82,
+ (sc->mac_ver == 0x3593) ? 0x62 : 0x84);
+ run_bbp_write(sc, 75, 0x50);
+ }
+ }
+ } else {
+ if (sc->mac_ver == 0x5592) {
+ run_bbp_write(sc, 79, 0x18);
+ run_bbp_write(sc, 80, 0x08);
+ run_bbp_write(sc, 81, 0x38);
+ run_bbp_write(sc, 82, 0x92);
+
+ run_bbp_write(sc, 195, 0x80);
+ run_bbp_write(sc, 196, 0xf0);
+ run_bbp_write(sc, 195, 0x81);
+ run_bbp_write(sc, 196, 0x1e);
+ run_bbp_write(sc, 195, 0x82);
+ run_bbp_write(sc, 196, 0x28);
+ run_bbp_write(sc, 195, 0x83);
+ run_bbp_write(sc, 196, 0x20);
+ run_bbp_write(sc, 195, 0x85);
+ run_bbp_write(sc, 196, 0x7f);
+ run_bbp_write(sc, 195, 0x86);
+ run_bbp_write(sc, 196, 0x7f);
+ } else if (sc->mac_ver == 0x3572)
+ run_bbp_write(sc, 82, 0x94);
+ else
+ run_bbp_write(sc, 82,
+ (sc->mac_ver == 0x3593) ? 0x82 : 0xf2);
+ if (sc->ext_5ghz_lna)
+ run_bbp_write(sc, 75, 0x46);
+ else
+ run_bbp_write(sc, 75, 0x50);
+ }
+
+ run_read(sc, RT2860_TX_BAND_CFG, &tmp);
+ tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P);
+ tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P;
+ run_write(sc, RT2860_TX_BAND_CFG, tmp);
+
+ /* enable appropriate Power Amplifiers and Low Noise Amplifiers */
+ tmp = RT2860_RFTR_EN | RT2860_TRSW_EN | RT2860_LNA_PE0_EN;
+ if (sc->mac_ver == 0x3593)
+ tmp |= 1 << 29 | 1 << 28;
+ if (sc->nrxchains > 1)
+ tmp |= RT2860_LNA_PE1_EN;
+ if (group == 0) { /* 2GHz */
+ tmp |= RT2860_PA_PE_G0_EN;
+ if (sc->ntxchains > 1)
+ tmp |= RT2860_PA_PE_G1_EN;
+ if (sc->mac_ver == 0x3593) {
+ if (sc->ntxchains > 2)
+ tmp |= 1 << 25;
+ }
+ } else { /* 5GHz */
+ tmp |= RT2860_PA_PE_A0_EN;
+ if (sc->ntxchains > 1)
+ tmp |= RT2860_PA_PE_A1_EN;
+ }
+ if (sc->mac_ver == 0x3572) {
+ run_rt3070_rf_write(sc, 8, 0x00);
+ run_write(sc, RT2860_TX_PIN_CFG, tmp);
+ run_rt3070_rf_write(sc, 8, 0x80);
+ } else
+ run_write(sc, RT2860_TX_PIN_CFG, tmp);
+
+ if (sc->mac_ver == 0x5592) {
+ run_bbp_write(sc, 195, 0x8d);
+ run_bbp_write(sc, 196, 0x1a);
+ }
+
+ if (sc->mac_ver == 0x3593) {
+ run_read(sc, RT2860_GPIO_CTRL, &tmp);
+ tmp &= ~0x01010000;
+ if (group == 0)
+ tmp |= 0x00010000;
+ tmp = (tmp & ~0x00009090) | 0x00000090;
+ run_write(sc, RT2860_GPIO_CTRL, tmp);
+ }
+
+ /* set initial AGC value */
+ if (group == 0) { /* 2GHz band */
+ if (sc->mac_ver >= 0x3070)
+ agc = 0x1c + sc->lna[0] * 2;
+ else
+ agc = 0x2e + sc->lna[0];
+ } else { /* 5GHz band */
+ if (sc->mac_ver == 0x5592)
+ agc = 0x24 + sc->lna[group] * 2;
+ else if (sc->mac_ver == 0x3572 || sc->mac_ver == 0x3593)
+ agc = 0x22 + (sc->lna[group] * 5) / 3;
+ else
+ agc = 0x32 + (sc->lna[group] * 5) / 3;
+ }
+ run_set_agc(sc, agc);
+}
+
+static void
+run_rt2870_set_chan(struct run_softc *sc, u_int chan)
+{
+ const struct rfprog *rfprog = rt2860_rf2850;
+ uint32_t r2, r3, r4;
+ int8_t txpow1, txpow2;
+ int i;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rfprog[i].chan != chan; i++);
+
+ r2 = rfprog[i].r2;
+ if (sc->ntxchains == 1)
+ r2 |= 1 << 14; /* 1T: disable Tx chain 2 */
+ if (sc->nrxchains == 1)
+ r2 |= 1 << 17 | 1 << 6; /* 1R: disable Rx chains 2 & 3 */
+ else if (sc->nrxchains == 2)
+ r2 |= 1 << 6; /* 2R: disable Rx chain 3 */
+
+ /* use Tx power values from EEPROM */
+ txpow1 = sc->txpow1[i];
+ txpow2 = sc->txpow2[i];
+
+ /* Initialize RF R3 and R4. */
+ r3 = rfprog[i].r3 & 0xffffc1ff;
+ r4 = (rfprog[i].r4 & ~(0x001f87c0)) | (sc->freq << 15);
+ if (chan > 14) {
+ if (txpow1 >= 0) {
+ txpow1 = (txpow1 > 0xf) ? (0xf) : (txpow1);
+ r3 |= (txpow1 << 10) | (1 << 9);
+ } else {
+ txpow1 += 7;
+
+ /* txpow1 is not possible larger than 15. */
+ r3 |= (txpow1 << 10);
+ }
+ if (txpow2 >= 0) {
+ txpow2 = (txpow2 > 0xf) ? (0xf) : (txpow2);
+ r4 |= (txpow2 << 7) | (1 << 6);
+ } else {
+ txpow2 += 7;
+ r4 |= (txpow2 << 7);
+ }
+ } else {
+ /* Set Tx0 power. */
+ r3 |= (txpow1 << 9);
+
+ /* Set frequency offset and Tx1 power. */
+ r4 |= (txpow2 << 6);
+ }
+
+ run_rt2870_rf_write(sc, rfprog[i].r1);
+ run_rt2870_rf_write(sc, r2);
+ run_rt2870_rf_write(sc, r3 & ~(1 << 2));
+ run_rt2870_rf_write(sc, r4);
+
+ run_delay(sc, 10);
+
+ run_rt2870_rf_write(sc, rfprog[i].r1);
+ run_rt2870_rf_write(sc, r2);
+ run_rt2870_rf_write(sc, r3 | (1 << 2));
+ run_rt2870_rf_write(sc, r4);
+
+ run_delay(sc, 10);
+
+ run_rt2870_rf_write(sc, rfprog[i].r1);
+ run_rt2870_rf_write(sc, r2);
+ run_rt2870_rf_write(sc, r3 & ~(1 << 2));
+ run_rt2870_rf_write(sc, r4);
+}
+
+static void
+run_rt3070_set_chan(struct run_softc *sc, u_int chan)
+{
+ int8_t txpow1, txpow2;
+ uint8_t rf;
+ int i;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rt2860_rf2850[i].chan != chan; i++);
+
+ /* use Tx power values from EEPROM */
+ txpow1 = sc->txpow1[i];
+ txpow2 = sc->txpow2[i];
+
+ run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n);
+
+ /* RT3370/RT3390: RF R3 [7:4] is not reserved bits. */
+ run_rt3070_rf_read(sc, 3, &rf);
+ rf = (rf & ~0x0f) | rt3070_freqs[i].k;
+ run_rt3070_rf_write(sc, 3, rf);
+
+ run_rt3070_rf_read(sc, 6, &rf);
+ rf = (rf & ~0x03) | rt3070_freqs[i].r;
+ run_rt3070_rf_write(sc, 6, rf);
+
+ /* set Tx0 power */
+ run_rt3070_rf_read(sc, 12, &rf);
+ rf = (rf & ~0x1f) | txpow1;
+ run_rt3070_rf_write(sc, 12, rf);
+
+ /* set Tx1 power */
+ run_rt3070_rf_read(sc, 13, &rf);
+ rf = (rf & ~0x1f) | txpow2;
+ run_rt3070_rf_write(sc, 13, rf);
+
+ run_rt3070_rf_read(sc, 1, &rf);
+ rf &= ~0xfc;
+ if (sc->ntxchains == 1)
+ rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */
+ else if (sc->ntxchains == 2)
+ rf |= 1 << 7; /* 2T: disable Tx chain 3 */
+ if (sc->nrxchains == 1)
+ rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */
+ else if (sc->nrxchains == 2)
+ rf |= 1 << 6; /* 2R: disable Rx chain 3 */
+ run_rt3070_rf_write(sc, 1, rf);
+
+ /* set RF offset */
+ run_rt3070_rf_read(sc, 23, &rf);
+ rf = (rf & ~0x7f) | sc->freq;
+ run_rt3070_rf_write(sc, 23, rf);
+
+ /* program RF filter */
+ run_rt3070_rf_read(sc, 24, &rf); /* Tx */
+ rf = (rf & ~0x3f) | sc->rf24_20mhz;
+ run_rt3070_rf_write(sc, 24, rf);
+ run_rt3070_rf_read(sc, 31, &rf); /* Rx */
+ rf = (rf & ~0x3f) | sc->rf24_20mhz;
+ run_rt3070_rf_write(sc, 31, rf);
+
+ /* enable RF tuning */
+ run_rt3070_rf_read(sc, 7, &rf);
+ run_rt3070_rf_write(sc, 7, rf | 0x01);
+}
+
+static void
+run_rt3572_set_chan(struct run_softc *sc, u_int chan)
+{
+ int8_t txpow1, txpow2;
+ uint32_t tmp;
+ uint8_t rf;
+ int i;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rt2860_rf2850[i].chan != chan; i++);
+
+ /* use Tx power values from EEPROM */
+ txpow1 = sc->txpow1[i];
+ txpow2 = sc->txpow2[i];
+
+ if (chan <= 14) {
+ run_bbp_write(sc, 25, sc->bbp25);
+ run_bbp_write(sc, 26, sc->bbp26);
+ } else {
+ /* enable IQ phase correction */
+ run_bbp_write(sc, 25, 0x09);
+ run_bbp_write(sc, 26, 0xff);
+ }
+
+ run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n);
+ run_rt3070_rf_write(sc, 3, rt3070_freqs[i].k);
+ run_rt3070_rf_read(sc, 6, &rf);
+ rf = (rf & ~0x0f) | rt3070_freqs[i].r;
+ rf |= (chan <= 14) ? 0x08 : 0x04;
+ run_rt3070_rf_write(sc, 6, rf);
+
+ /* set PLL mode */
+ run_rt3070_rf_read(sc, 5, &rf);
+ rf &= ~(0x08 | 0x04);
+ rf |= (chan <= 14) ? 0x04 : 0x08;
+ run_rt3070_rf_write(sc, 5, rf);
+
+ /* set Tx power for chain 0 */
+ if (chan <= 14)
+ rf = 0x60 | txpow1;
+ else
+ rf = 0xe0 | (txpow1 & 0xc) << 1 | (txpow1 & 0x3);
+ run_rt3070_rf_write(sc, 12, rf);
+
+ /* set Tx power for chain 1 */
+ if (chan <= 14)
+ rf = 0x60 | txpow2;
+ else
+ rf = 0xe0 | (txpow2 & 0xc) << 1 | (txpow2 & 0x3);
+ run_rt3070_rf_write(sc, 13, rf);
+
+ /* set Tx/Rx streams */
+ run_rt3070_rf_read(sc, 1, &rf);
+ rf &= ~0xfc;
+ if (sc->ntxchains == 1)
+ rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */
+ else if (sc->ntxchains == 2)
+ rf |= 1 << 7; /* 2T: disable Tx chain 3 */
+ if (sc->nrxchains == 1)
+ rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */
+ else if (sc->nrxchains == 2)
+ rf |= 1 << 6; /* 2R: disable Rx chain 3 */
+ run_rt3070_rf_write(sc, 1, rf);
+
+ /* set RF offset */
+ run_rt3070_rf_read(sc, 23, &rf);
+ rf = (rf & ~0x7f) | sc->freq;
+ run_rt3070_rf_write(sc, 23, rf);
+
+ /* program RF filter */
+ rf = sc->rf24_20mhz;
+ run_rt3070_rf_write(sc, 24, rf); /* Tx */
+ run_rt3070_rf_write(sc, 31, rf); /* Rx */
+
+ /* enable RF tuning */
+ run_rt3070_rf_read(sc, 7, &rf);
+ rf = (chan <= 14) ? 0xd8 : ((rf & ~0xc8) | 0x14);
+ run_rt3070_rf_write(sc, 7, rf);
+
+ /* TSSI */
+ rf = (chan <= 14) ? 0xc3 : 0xc0;
+ run_rt3070_rf_write(sc, 9, rf);
+
+ /* set loop filter 1 */
+ run_rt3070_rf_write(sc, 10, 0xf1);
+ /* set loop filter 2 */
+ run_rt3070_rf_write(sc, 11, (chan <= 14) ? 0xb9 : 0x00);
+
+ /* set tx_mx2_ic */
+ run_rt3070_rf_write(sc, 15, (chan <= 14) ? 0x53 : 0x43);
+ /* set tx_mx1_ic */
+ if (chan <= 14)
+ rf = 0x48 | sc->txmixgain_2ghz;
+ else
+ rf = 0x78 | sc->txmixgain_5ghz;
+ run_rt3070_rf_write(sc, 16, rf);
+
+ /* set tx_lo1 */
+ run_rt3070_rf_write(sc, 17, 0x23);
+ /* set tx_lo2 */
+ if (chan <= 14)
+ rf = 0x93;
+ else if (chan <= 64)
+ rf = 0xb7;
+ else if (chan <= 128)
+ rf = 0x74;
+ else
+ rf = 0x72;
+ run_rt3070_rf_write(sc, 19, rf);
+
+ /* set rx_lo1 */
+ if (chan <= 14)
+ rf = 0xb3;
+ else if (chan <= 64)
+ rf = 0xf6;
+ else if (chan <= 128)
+ rf = 0xf4;
+ else
+ rf = 0xf3;
+ run_rt3070_rf_write(sc, 20, rf);
+
+ /* set pfd_delay */
+ if (chan <= 14)
+ rf = 0x15;
+ else if (chan <= 64)
+ rf = 0x3d;
+ else
+ rf = 0x01;
+ run_rt3070_rf_write(sc, 25, rf);
+
+ /* set rx_lo2 */
+ run_rt3070_rf_write(sc, 26, (chan <= 14) ? 0x85 : 0x87);
+ /* set ldo_rf_vc */
+ run_rt3070_rf_write(sc, 27, (chan <= 14) ? 0x00 : 0x01);
+ /* set drv_cc */
+ run_rt3070_rf_write(sc, 29, (chan <= 14) ? 0x9b : 0x9f);
+
+ run_read(sc, RT2860_GPIO_CTRL, &tmp);
+ tmp &= ~0x8080;
+ if (chan <= 14)
+ tmp |= 0x80;
+ run_write(sc, RT2860_GPIO_CTRL, tmp);
+
+ /* enable RF tuning */
+ run_rt3070_rf_read(sc, 7, &rf);
+ run_rt3070_rf_write(sc, 7, rf | 0x01);
+
+ run_delay(sc, 2);
+}
+
+static void
+run_rt3593_set_chan(struct run_softc *sc, u_int chan)
+{
+ int8_t txpow1, txpow2, txpow3;
+ uint8_t h20mhz, rf;
+ int i;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rt2860_rf2850[i].chan != chan; i++);
+
+ /* use Tx power values from EEPROM */
+ txpow1 = sc->txpow1[i];
+ txpow2 = sc->txpow2[i];
+ txpow3 = (sc->ntxchains == 3) ? sc->txpow3[i] : 0;
+
+ if (chan <= 14) {
+ run_bbp_write(sc, 25, sc->bbp25);
+ run_bbp_write(sc, 26, sc->bbp26);
+ } else {
+ /* Enable IQ phase correction. */
+ run_bbp_write(sc, 25, 0x09);
+ run_bbp_write(sc, 26, 0xff);
+ }
+
+ run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n);
+ run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f);
+ run_rt3070_rf_read(sc, 11, &rf);
+ rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03);
+ run_rt3070_rf_write(sc, 11, rf);
+
+ /* Set pll_idoh. */
+ run_rt3070_rf_read(sc, 11, &rf);
+ rf &= ~0x4c;
+ rf |= (chan <= 14) ? 0x44 : 0x48;
+ run_rt3070_rf_write(sc, 11, rf);
+
+ if (chan <= 14)
+ rf = txpow1 & 0x1f;
+ else
+ rf = 0x40 | ((txpow1 & 0x18) << 1) | (txpow1 & 0x07);
+ run_rt3070_rf_write(sc, 53, rf);
+
+ if (chan <= 14)
+ rf = txpow2 & 0x1f;
+ else
+ rf = 0x40 | ((txpow2 & 0x18) << 1) | (txpow2 & 0x07);
+ run_rt3070_rf_write(sc, 55, rf);
+
+ if (chan <= 14)
+ rf = txpow3 & 0x1f;
+ else
+ rf = 0x40 | ((txpow3 & 0x18) << 1) | (txpow3 & 0x07);
+ run_rt3070_rf_write(sc, 54, rf);
+
+ rf = RT3070_RF_BLOCK | RT3070_PLL_PD;
+ if (sc->ntxchains == 3)
+ rf |= RT3070_TX0_PD | RT3070_TX1_PD | RT3070_TX2_PD;
+ else
+ rf |= RT3070_TX0_PD | RT3070_TX1_PD;
+ rf |= RT3070_RX0_PD | RT3070_RX1_PD | RT3070_RX2_PD;
+ run_rt3070_rf_write(sc, 1, rf);
+
+ run_adjust_freq_offset(sc);
+
+ run_rt3070_rf_write(sc, 31, (chan <= 14) ? 0xa0 : 0x80);
+
+ h20mhz = (sc->rf24_20mhz & 0x20) >> 5;
+ run_rt3070_rf_read(sc, 30, &rf);
+ rf = (rf & ~0x06) | (h20mhz << 1) | (h20mhz << 2);
+ run_rt3070_rf_write(sc, 30, rf);
+
+ run_rt3070_rf_read(sc, 36, &rf);
+ if (chan <= 14)
+ rf |= 0x80;
+ else
+ rf &= ~0x80;
+ run_rt3070_rf_write(sc, 36, rf);
+
+ /* Set vcolo_bs. */
+ run_rt3070_rf_write(sc, 34, (chan <= 14) ? 0x3c : 0x20);
+ /* Set pfd_delay. */
+ run_rt3070_rf_write(sc, 12, (chan <= 14) ? 0x1a : 0x12);
+
+ /* Set vco bias current control. */
+ run_rt3070_rf_read(sc, 6, &rf);
+ rf &= ~0xc0;
+ if (chan <= 14)
+ rf |= 0x40;
+ else if (chan <= 128)
+ rf |= 0x80;
+ else
+ rf |= 0x40;
+ run_rt3070_rf_write(sc, 6, rf);
+
+ run_rt3070_rf_read(sc, 30, &rf);
+ rf = (rf & ~0x18) | 0x10;
+ run_rt3070_rf_write(sc, 30, rf);
+
+ run_rt3070_rf_write(sc, 10, (chan <= 14) ? 0xd3 : 0xd8);
+ run_rt3070_rf_write(sc, 13, (chan <= 14) ? 0x12 : 0x23);
+
+ run_rt3070_rf_read(sc, 51, &rf);
+ rf = (rf & ~0x03) | 0x01;
+ run_rt3070_rf_write(sc, 51, rf);
+ /* Set tx_mx1_cc. */
+ run_rt3070_rf_read(sc, 51, &rf);
+ rf &= ~0x1c;
+ rf |= (chan <= 14) ? 0x14 : 0x10;
+ run_rt3070_rf_write(sc, 51, rf);
+ /* Set tx_mx1_ic. */
+ run_rt3070_rf_read(sc, 51, &rf);
+ rf &= ~0xe0;
+ rf |= (chan <= 14) ? 0x60 : 0x40;
+ run_rt3070_rf_write(sc, 51, rf);
+ /* Set tx_lo1_ic. */
+ run_rt3070_rf_read(sc, 49, &rf);
+ rf &= ~0x1c;
+ rf |= (chan <= 14) ? 0x0c : 0x08;
+ run_rt3070_rf_write(sc, 49, rf);
+ /* Set tx_lo1_en. */
+ run_rt3070_rf_read(sc, 50, &rf);
+ run_rt3070_rf_write(sc, 50, rf & ~0x20);
+ /* Set drv_cc. */
+ run_rt3070_rf_read(sc, 57, &rf);
+ rf &= ~0xfc;
+ rf |= (chan <= 14) ? 0x6c : 0x3c;
+ run_rt3070_rf_write(sc, 57, rf);
+ /* Set rx_mix1_ic, rxa_lnactr, lna_vc, lna_inbias_en and lna_en. */
+ run_rt3070_rf_write(sc, 44, (chan <= 14) ? 0x93 : 0x9b);
+ /* Set drv_gnd_a, tx_vga_cc_a and tx_mx2_gain. */
+ run_rt3070_rf_write(sc, 52, (chan <= 14) ? 0x45 : 0x05);
+ /* Enable VCO calibration. */
+ run_rt3070_rf_read(sc, 3, &rf);
+ rf &= ~RT5390_VCOCAL;
+ rf |= (chan <= 14) ? RT5390_VCOCAL : 0xbe;
+ run_rt3070_rf_write(sc, 3, rf);
+
+ if (chan <= 14)
+ rf = 0x23;
+ else if (chan <= 64)
+ rf = 0x36;
+ else if (chan <= 128)
+ rf = 0x32;
+ else
+ rf = 0x30;
+ run_rt3070_rf_write(sc, 39, rf);
+ if (chan <= 14)
+ rf = 0xbb;
+ else if (chan <= 64)
+ rf = 0xeb;
+ else if (chan <= 128)
+ rf = 0xb3;
+ else
+ rf = 0x9b;
+ run_rt3070_rf_write(sc, 45, rf);
+
+ /* Set FEQ/AEQ control. */
+ run_bbp_write(sc, 105, 0x34);
+}
+
+static void
+run_rt5390_set_chan(struct run_softc *sc, u_int chan)
+{
+ int8_t txpow1, txpow2;
+ uint8_t rf;
+ int i;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rt2860_rf2850[i].chan != chan; i++);
+
+ /* use Tx power values from EEPROM */
+ txpow1 = sc->txpow1[i];
+ txpow2 = sc->txpow2[i];
+
+ run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n);
+ run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f);
+ run_rt3070_rf_read(sc, 11, &rf);
+ rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03);
+ run_rt3070_rf_write(sc, 11, rf);
+
+ run_rt3070_rf_read(sc, 49, &rf);
+ rf = (rf & ~0x3f) | (txpow1 & 0x3f);
+ /* The valid range of the RF R49 is 0x00 to 0x27. */
+ if ((rf & 0x3f) > 0x27)
+ rf = (rf & ~0x3f) | 0x27;
+ run_rt3070_rf_write(sc, 49, rf);
+
+ if (sc->mac_ver == 0x5392) {
+ run_rt3070_rf_read(sc, 50, &rf);
+ rf = (rf & ~0x3f) | (txpow2 & 0x3f);
+ /* The valid range of the RF R50 is 0x00 to 0x27. */
+ if ((rf & 0x3f) > 0x27)
+ rf = (rf & ~0x3f) | 0x27;
+ run_rt3070_rf_write(sc, 50, rf);
+ }
+
+ run_rt3070_rf_read(sc, 1, &rf);
+ rf |= RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD;
+ if (sc->mac_ver == 0x5392)
+ rf |= RT3070_RX1_PD | RT3070_TX1_PD;
+ run_rt3070_rf_write(sc, 1, rf);
+
+ if (sc->mac_ver != 0x5392) {
+ run_rt3070_rf_read(sc, 2, &rf);
+ rf |= 0x80;
+ run_rt3070_rf_write(sc, 2, rf);
+ run_delay(sc, 10);
+ rf &= 0x7f;
+ run_rt3070_rf_write(sc, 2, rf);
+ }
+
+ run_adjust_freq_offset(sc);
+
+ if (sc->mac_ver == 0x5392) {
+ /* Fix for RT5392C. */
+ if (sc->mac_rev >= 0x0223) {
+ if (chan <= 4)
+ rf = 0x0f;
+ else if (chan >= 5 && chan <= 7)
+ rf = 0x0e;
+ else
+ rf = 0x0d;
+ run_rt3070_rf_write(sc, 23, rf);
+
+ if (chan <= 4)
+ rf = 0x0c;
+ else if (chan == 5)
+ rf = 0x0b;
+ else if (chan >= 6 && chan <= 7)
+ rf = 0x0a;
+ else if (chan >= 8 && chan <= 10)
+ rf = 0x09;
+ else
+ rf = 0x08;
+ run_rt3070_rf_write(sc, 59, rf);
+ } else {
+ if (chan <= 11)
+ rf = 0x0f;
+ else
+ rf = 0x0b;
+ run_rt3070_rf_write(sc, 59, rf);
+ }
+ } else {
+ /* Fix for RT5390F. */
+ if (sc->mac_rev >= 0x0502) {
+ if (chan <= 11)
+ rf = 0x43;
+ else
+ rf = 0x23;
+ run_rt3070_rf_write(sc, 55, rf);
+
+ if (chan <= 11)
+ rf = 0x0f;
+ else if (chan == 12)
+ rf = 0x0d;
+ else
+ rf = 0x0b;
+ run_rt3070_rf_write(sc, 59, rf);
+ } else {
+ run_rt3070_rf_write(sc, 55, 0x44);
+ run_rt3070_rf_write(sc, 59, 0x8f);
+ }
+ }
+
+ /* Enable VCO calibration. */
+ run_rt3070_rf_read(sc, 3, &rf);
+ rf |= RT5390_VCOCAL;
+ run_rt3070_rf_write(sc, 3, rf);
+}
+
+static void
+run_rt5592_set_chan(struct run_softc *sc, u_int chan)
+{
+ const struct rt5592_freqs *freqs;
+ uint32_t tmp;
+ uint8_t reg, rf, txpow_bound;
+ int8_t txpow1, txpow2;
+ int i;
+
+ run_read(sc, RT5592_DEBUG_INDEX, &tmp);
+ freqs = (tmp & RT5592_SEL_XTAL) ?
+ rt5592_freqs_40mhz : rt5592_freqs_20mhz;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rt2860_rf2850[i].chan != chan; i++, freqs++);
+
+ /* use Tx power values from EEPROM */
+ txpow1 = sc->txpow1[i];
+ txpow2 = sc->txpow2[i];
+
+ run_read(sc, RT3070_LDO_CFG0, &tmp);
+ tmp &= ~0x1c000000;
+ if (chan > 14)
+ tmp |= 0x14000000;
+ run_write(sc, RT3070_LDO_CFG0, tmp);
+
+ /* N setting. */
+ run_rt3070_rf_write(sc, 8, freqs->n & 0xff);
+ run_rt3070_rf_read(sc, 9, &rf);
+ rf &= ~(1 << 4);
+ rf |= ((freqs->n & 0x0100) >> 8) << 4;
+ run_rt3070_rf_write(sc, 9, rf);
+
+ /* K setting. */
+ run_rt3070_rf_read(sc, 9, &rf);
+ rf &= ~0x0f;
+ rf |= (freqs->k & 0x0f);
+ run_rt3070_rf_write(sc, 9, rf);
+
+ /* Mode setting. */
+ run_rt3070_rf_read(sc, 11, &rf);
+ rf &= ~0x0c;
+ rf |= ((freqs->m - 0x8) & 0x3) << 2;
+ run_rt3070_rf_write(sc, 11, rf);
+ run_rt3070_rf_read(sc, 9, &rf);
+ rf &= ~(1 << 7);
+ rf |= (((freqs->m - 0x8) & 0x4) >> 2) << 7;
+ run_rt3070_rf_write(sc, 9, rf);
+
+ /* R setting. */
+ run_rt3070_rf_read(sc, 11, &rf);
+ rf &= ~0x03;
+ rf |= (freqs->r - 0x1);
+ run_rt3070_rf_write(sc, 11, rf);
+
+ if (chan <= 14) {
+ /* Initialize RF registers for 2GHZ. */
+ for (i = 0; i < nitems(rt5592_2ghz_def_rf); i++) {
+ run_rt3070_rf_write(sc, rt5592_2ghz_def_rf[i].reg,
+ rt5592_2ghz_def_rf[i].val);
+ }
+
+ rf = (chan <= 10) ? 0x07 : 0x06;
+ run_rt3070_rf_write(sc, 23, rf);
+ run_rt3070_rf_write(sc, 59, rf);
+
+ run_rt3070_rf_write(sc, 55, 0x43);
+
+ /*
+ * RF R49/R50 Tx power ALC code.
+ * G-band bit<7:6>=1:0, bit<5:0> range from 0x0 ~ 0x27.
+ */
+ reg = 2;
+ txpow_bound = 0x27;
+ } else {
+ /* Initialize RF registers for 5GHZ. */
+ for (i = 0; i < nitems(rt5592_5ghz_def_rf); i++) {
+ run_rt3070_rf_write(sc, rt5592_5ghz_def_rf[i].reg,
+ rt5592_5ghz_def_rf[i].val);
+ }
+ for (i = 0; i < nitems(rt5592_chan_5ghz); i++) {
+ if (chan >= rt5592_chan_5ghz[i].firstchan &&
+ chan <= rt5592_chan_5ghz[i].lastchan) {
+ run_rt3070_rf_write(sc, rt5592_chan_5ghz[i].reg,
+ rt5592_chan_5ghz[i].val);
+ }
+ }
+
+ /*
+ * RF R49/R50 Tx power ALC code.
+ * A-band bit<7:6>=1:1, bit<5:0> range from 0x0 ~ 0x2b.
+ */
+ reg = 3;
+ txpow_bound = 0x2b;
+ }
+
+ /* RF R49 ch0 Tx power ALC code. */
+ run_rt3070_rf_read(sc, 49, &rf);
+ rf &= ~0xc0;
+ rf |= (reg << 6);
+ rf = (rf & ~0x3f) | (txpow1 & 0x3f);
+ if ((rf & 0x3f) > txpow_bound)
+ rf = (rf & ~0x3f) | txpow_bound;
+ run_rt3070_rf_write(sc, 49, rf);
+
+ /* RF R50 ch1 Tx power ALC code. */
+ run_rt3070_rf_read(sc, 50, &rf);
+ rf &= ~(1 << 7 | 1 << 6);
+ rf |= (reg << 6);
+ rf = (rf & ~0x3f) | (txpow2 & 0x3f);
+ if ((rf & 0x3f) > txpow_bound)
+ rf = (rf & ~0x3f) | txpow_bound;
+ run_rt3070_rf_write(sc, 50, rf);
+
+ /* Enable RF_BLOCK, PLL_PD, RX0_PD, and TX0_PD. */
+ run_rt3070_rf_read(sc, 1, &rf);
+ rf |= (RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD);
+ if (sc->ntxchains > 1)
+ rf |= RT3070_TX1_PD;
+ if (sc->nrxchains > 1)
+ rf |= RT3070_RX1_PD;
+ run_rt3070_rf_write(sc, 1, rf);
+
+ run_rt3070_rf_write(sc, 6, 0xe4);
+
+ run_rt3070_rf_write(sc, 30, 0x10);
+ run_rt3070_rf_write(sc, 31, 0x80);
+ run_rt3070_rf_write(sc, 32, 0x80);
+
+ run_adjust_freq_offset(sc);
+
+ /* Enable VCO calibration. */
+ run_rt3070_rf_read(sc, 3, &rf);
+ rf |= RT5390_VCOCAL;
+ run_rt3070_rf_write(sc, 3, rf);
+}
+
+static void
+run_set_rx_antenna(struct run_softc *sc, int aux)
+{
+ uint32_t tmp;
+ uint8_t bbp152;
+
+ if (aux) {
+ if (sc->rf_rev == RT5390_RF_5370) {
+ run_bbp_read(sc, 152, &bbp152);
+ run_bbp_write(sc, 152, bbp152 & ~0x80);
+ } else {
+ run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 0);
+ run_read(sc, RT2860_GPIO_CTRL, &tmp);
+ run_write(sc, RT2860_GPIO_CTRL, (tmp & ~0x0808) | 0x08);
+ }
+ } else {
+ if (sc->rf_rev == RT5390_RF_5370) {
+ run_bbp_read(sc, 152, &bbp152);
+ run_bbp_write(sc, 152, bbp152 | 0x80);
+ } else {
+ run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 1);
+ run_read(sc, RT2860_GPIO_CTRL, &tmp);
+ run_write(sc, RT2860_GPIO_CTRL, tmp & ~0x0808);
+ }
+ }
+}
+
+static int
+run_set_chan(struct run_softc *sc, struct ieee80211_channel *c)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ u_int chan, group;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+ return (EINVAL);
+
+ if (sc->mac_ver == 0x5592)
+ run_rt5592_set_chan(sc, chan);
+ else if (sc->mac_ver >= 0x5390)
+ run_rt5390_set_chan(sc, chan);
+ else if (sc->mac_ver == 0x3593)
+ run_rt3593_set_chan(sc, chan);
+ else if (sc->mac_ver == 0x3572)
+ run_rt3572_set_chan(sc, chan);
+ else if (sc->mac_ver >= 0x3070)
+ run_rt3070_set_chan(sc, chan);
+ else
+ run_rt2870_set_chan(sc, chan);
+
+ /* determine channel group */
+ if (chan <= 14)
+ group = 0;
+ else if (chan <= 64)
+ group = 1;
+ else if (chan <= 128)
+ group = 2;
+ else
+ group = 3;
+
+ /* XXX necessary only when group has changed! */
+ run_select_chan_group(sc, group);
+
+ run_delay(sc, 10);
+
+ /* Perform IQ calibration. */
+ if (sc->mac_ver >= 0x5392)
+ run_iq_calib(sc, chan);
+
+ return (0);
+}
+
+static void
+run_set_channel(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_softc;
+
+ RUN_LOCK(sc);
+ run_set_chan(sc, ic->ic_curchan);
+ RUN_UNLOCK(sc);
+
+ return;
+}
+
+static void
+run_getradiocaps(struct ieee80211com *ic,
+ int maxchans, int *nchans, struct ieee80211_channel chans[])
+{
+ struct run_softc *sc = ic->ic_softc;
+ uint8_t bands[IEEE80211_MODE_BYTES];
+
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ ieee80211_add_channel_list_2ghz(chans, maxchans, nchans,
+ run_chan_2ghz, nitems(run_chan_2ghz), bands, 0);
+
+ if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850 ||
+ sc->rf_rev == RT3070_RF_3052 || sc->rf_rev == RT3593_RF_3053 ||
+ sc->rf_rev == RT5592_RF_5592) {
+ setbit(bands, IEEE80211_MODE_11A);
+ ieee80211_add_channel_list_5ghz(chans, maxchans, nchans,
+ run_chan_5ghz, nitems(run_chan_5ghz), bands, 0);
+ }
+}
+
+static void
+run_scan_start(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_softc;
+ uint32_t tmp;
+
+ RUN_LOCK(sc);
+
+ /* abort TSF synchronization */
+ run_read(sc, RT2860_BCN_TIME_CFG, &tmp);
+ run_write(sc, RT2860_BCN_TIME_CFG,
+ tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN |
+ RT2860_TBTT_TIMER_EN));
+ run_set_bssid(sc, ieee80211broadcastaddr);
+
+ RUN_UNLOCK(sc);
+
+ return;
+}
+
+static void
+run_scan_end(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_softc;
+
+ RUN_LOCK(sc);
+
+ run_enable_tsf_sync(sc);
+ run_set_bssid(sc, sc->sc_bssid);
+
+ RUN_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Could be called from ieee80211_node_timeout()
+ * (non-sleepable thread)
+ */
+static void
+run_update_beacon(struct ieee80211vap *vap, int item)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct run_softc *sc = ic->ic_softc;
+ struct run_vap *rvp = RUN_VAP(vap);
+ int mcast = 0;
+ uint32_t i;
+
+ switch (item) {
+ case IEEE80211_BEACON_ERP:
+ run_updateslot(ic);
+ break;
+ case IEEE80211_BEACON_HTINFO:
+ run_updateprot(ic);
+ break;
+ case IEEE80211_BEACON_TIM:
+ mcast = 1; /*TODO*/
+ break;
+ default:
+ break;
+ }
+
+ setbit(bo->bo_flags, item);
+ if (rvp->beacon_mbuf == NULL) {
+ rvp->beacon_mbuf = ieee80211_beacon_alloc(ni);
+ if (rvp->beacon_mbuf == NULL)
+ return;
+ }
+ ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast);
+
+ i = RUN_CMDQ_GET(&sc->cmdq_store);
+ RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_update_beacon_cb;
+ sc->cmdq[i].arg0 = vap;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ return;
+}
+
+static void
+run_update_beacon_cb(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct run_vap *rvp = RUN_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_softc;
+ struct rt2860_txwi txwi;
+ struct mbuf *m;
+ uint16_t txwisize;
+ uint8_t ridx;
+
+ if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+ return;
+ if (ic->ic_bsschan == IEEE80211_CHAN_ANYC)
+ return;
+
+ /*
+ * No need to call ieee80211_beacon_update(), run_update_beacon()
+ * is taking care of appropriate calls.
+ */
+ if (rvp->beacon_mbuf == NULL) {
+ rvp->beacon_mbuf = ieee80211_beacon_alloc(ni);
+ if (rvp->beacon_mbuf == NULL)
+ return;
+ }
+ m = rvp->beacon_mbuf;
+
+ memset(&txwi, 0, sizeof(txwi));
+ txwi.wcid = 0xff;
+ txwi.len = htole16(m->m_pkthdr.len);
+
+ /* send beacons at the lowest available rate */
+ ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
+ RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1;
+ txwi.phy = htole16(rt2860_rates[ridx].mcs);
+ if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM)
+ txwi.phy |= htole16(RT2860_PHY_OFDM);
+ txwi.txop = RT2860_TX_TXOP_HT;
+ txwi.flags = RT2860_TX_TS;
+ txwi.xflags = RT2860_TX_NSEQ;
+
+ txwisize = (sc->mac_ver == 0x5592) ?
+ sizeof(txwi) + sizeof(uint32_t) : sizeof(txwi);
+ run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id), (uint8_t *)&txwi,
+ txwisize);
+ run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id) + txwisize,
+ mtod(m, uint8_t *), (m->m_pkthdr.len + 1) & ~1);
+}
+
+static void
+run_updateprot(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_softc;
+ uint32_t i;
+
+ i = RUN_CMDQ_GET(&sc->cmdq_store);
+ RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_updateprot_cb;
+ sc->cmdq[i].arg0 = ic;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+}
+
+static void
+run_updateprot_cb(void *arg)
+{
+ struct ieee80211com *ic = arg;
+ struct run_softc *sc = ic->ic_softc;
+ uint32_t tmp;
+
+ tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL;
+ /* setup protection frame rate (MCS code) */
+ tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ?
+ rt2860_rates[RT2860_RIDX_OFDM6].mcs | RT2860_PHY_OFDM :
+ rt2860_rates[RT2860_RIDX_CCK11].mcs;
+
+ /* CCK frames don't require protection */
+ run_write(sc, RT2860_CCK_PROT_CFG, tmp);
+ if (ic->ic_flags & IEEE80211_F_USEPROT) {
+ if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ tmp |= RT2860_PROT_CTRL_RTS_CTS;
+ else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ tmp |= RT2860_PROT_CTRL_CTS;
+ }
+ run_write(sc, RT2860_OFDM_PROT_CFG, tmp);
+}
+
+static void
+run_usb_timeout_cb(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+ struct run_softc *sc = vap->iv_ic->ic_softc;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ if(vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_opmode != IEEE80211_M_STA)
+ run_reset_livelock(sc);
+ else if (vap->iv_state == IEEE80211_S_SCAN) {
+ RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE,
+ "timeout caused by scan\n");
+ /* cancel bgscan */
+ ieee80211_cancel_scan(vap);
+ } else
+ RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE,
+ "timeout by unknown cause\n");
+}
+
+static void
+run_reset_livelock(struct run_softc *sc)
+{
+ uint32_t tmp;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * In IBSS or HostAP modes (when the hardware sends beacons), the MAC
+ * can run into a livelock and start sending CTS-to-self frames like
+ * crazy if protection is enabled. Reset MAC/BBP for a while
+ */
+ run_read(sc, RT2860_DEBUG, &tmp);
+ RUN_DPRINTF(sc, RUN_DEBUG_RESET, "debug reg %08x\n", tmp);
+ if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) {
+ RUN_DPRINTF(sc, RUN_DEBUG_RESET,
+ "CTS-to-self livelock detected\n");
+ run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST);
+ run_delay(sc, 1);
+ run_write(sc, RT2860_MAC_SYS_CTRL,
+ RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
+ }
+}
+
+static void
+run_update_promisc_locked(struct run_softc *sc)
+{
+ uint32_t tmp;
+
+ run_read(sc, RT2860_RX_FILTR_CFG, &tmp);
+
+ tmp |= RT2860_DROP_UC_NOME;
+ if (sc->sc_ic.ic_promisc > 0)
+ tmp &= ~RT2860_DROP_UC_NOME;
+
+ run_write(sc, RT2860_RX_FILTR_CFG, tmp);
+
+ RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s promiscuous mode\n",
+ (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving");
+}
+
+static void
+run_update_promisc(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_softc;
+
+ if ((sc->sc_flags & RUN_RUNNING) == 0)
+ return;
+
+ RUN_LOCK(sc);
+ run_update_promisc_locked(sc);
+ RUN_UNLOCK(sc);
+}
+
+static void
+run_enable_tsf_sync(struct run_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+
+ RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "rvp_id=%d ic_opmode=%d\n",
+ RUN_VAP(vap)->rvp_id, ic->ic_opmode);
+
+ run_read(sc, RT2860_BCN_TIME_CFG, &tmp);
+ tmp &= ~0x1fffff;
+ tmp |= vap->iv_bss->ni_intval * 16;
+ tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN;
+
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ /*
+ * Local TSF is always updated with remote TSF on beacon
+ * reception.
+ */
+ tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT;
+ } else if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ tmp |= RT2860_BCN_TX_EN;
+ /*
+ * Local TSF is updated with remote TSF on beacon reception
+ * only if the remote TSF is greater than local TSF.
+ */
+ tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT;
+ } else if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_MBSS) {
+ tmp |= RT2860_BCN_TX_EN;
+ /* SYNC with nobody */
+ tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT;
+ } else {
+ RUN_DPRINTF(sc, RUN_DEBUG_BEACON,
+ "Enabling TSF failed. undefined opmode\n");
+ return;
+ }
+
+ run_write(sc, RT2860_BCN_TIME_CFG, tmp);
+}
+
+static void
+run_enable_tsf(struct run_softc *sc)
+{
+ uint32_t tmp;
+
+ if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) {
+ tmp &= ~(RT2860_BCN_TX_EN | RT2860_TBTT_TIMER_EN);
+ tmp |= RT2860_TSF_TIMER_EN;
+ run_write(sc, RT2860_BCN_TIME_CFG, tmp);
+ }
+}
+
+static void
+run_get_tsf(struct run_softc *sc, uint64_t *buf)
+{
+ run_read_region_1(sc, RT2860_TSF_TIMER_DW0, (uint8_t *)buf,
+ sizeof(*buf));
+}
+
+static void
+run_enable_mrr(struct run_softc *sc)
+{
+#define CCK(mcs) (mcs)
+#define OFDM(mcs) (1 << 3 | (mcs))
+ run_write(sc, RT2860_LG_FBK_CFG0,
+ OFDM(6) << 28 | /* 54->48 */
+ OFDM(5) << 24 | /* 48->36 */
+ OFDM(4) << 20 | /* 36->24 */
+ OFDM(3) << 16 | /* 24->18 */
+ OFDM(2) << 12 | /* 18->12 */
+ OFDM(1) << 8 | /* 12-> 9 */
+ OFDM(0) << 4 | /* 9-> 6 */
+ OFDM(0)); /* 6-> 6 */
+
+ run_write(sc, RT2860_LG_FBK_CFG1,
+ CCK(2) << 12 | /* 11->5.5 */
+ CCK(1) << 8 | /* 5.5-> 2 */
+ CCK(0) << 4 | /* 2-> 1 */
+ CCK(0)); /* 1-> 1 */
+#undef OFDM
+#undef CCK
+}
+
+static void
+run_set_txpreamble(struct run_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t tmp;
+
+ run_read(sc, RT2860_AUTO_RSP_CFG, &tmp);
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ tmp |= RT2860_CCK_SHORT_EN;
+ else
+ tmp &= ~RT2860_CCK_SHORT_EN;
+ run_write(sc, RT2860_AUTO_RSP_CFG, tmp);
+}
+
+static void
+run_set_basicrates(struct run_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ /* set basic rates mask */
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x003);
+ else if (ic->ic_curmode == IEEE80211_MODE_11A)
+ run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x150);
+ else /* 11g */
+ run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x15f);
+}
+
+static void
+run_set_leds(struct run_softc *sc, uint16_t which)
+{
+ (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LEDS,
+ which | (sc->leds & 0x7f));
+}
+
+static void
+run_set_bssid(struct run_softc *sc, const uint8_t *bssid)
+{
+ run_write(sc, RT2860_MAC_BSSID_DW0,
+ bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24);
+ run_write(sc, RT2860_MAC_BSSID_DW1,
+ bssid[4] | bssid[5] << 8);
+}
+
+static void
+run_set_macaddr(struct run_softc *sc, const uint8_t *addr)
+{
+ run_write(sc, RT2860_MAC_ADDR_DW0,
+ addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
+ run_write(sc, RT2860_MAC_ADDR_DW1,
+ addr[4] | addr[5] << 8 | 0xff << 16);
+}
+
+static void
+run_updateslot(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_softc;
+ uint32_t i;
+
+ i = RUN_CMDQ_GET(&sc->cmdq_store);
+ RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_updateslot_cb;
+ sc->cmdq[i].arg0 = ic;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ return;
+}
+
+/* ARGSUSED */
+static void
+run_updateslot_cb(void *arg)
+{
+ struct ieee80211com *ic = arg;
+ struct run_softc *sc = ic->ic_softc;
+ uint32_t tmp;
+
+ run_read(sc, RT2860_BKOFF_SLOT_CFG, &tmp);
+ tmp &= ~0xff;
+ tmp |= IEEE80211_GET_SLOTTIME(ic);
+ run_write(sc, RT2860_BKOFF_SLOT_CFG, tmp);
+}
+
+static void
+run_update_mcast(struct ieee80211com *ic)
+{
+}
+
+static int8_t
+run_rssi2dbm(struct run_softc *sc, uint8_t rssi, uint8_t rxchain)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_channel *c = ic->ic_curchan;
+ int delta;
+
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ u_int chan = ieee80211_chan2ieee(ic, c);
+ delta = sc->rssi_5ghz[rxchain];
+
+ /* determine channel group */
+ if (chan <= 64)
+ delta -= sc->lna[1];
+ else if (chan <= 128)
+ delta -= sc->lna[2];
+ else
+ delta -= sc->lna[3];
+ } else
+ delta = sc->rssi_2ghz[rxchain] - sc->lna[0];
+
+ return (-12 - delta - rssi);
+}
+
+static void
+run_rt5390_bbp_init(struct run_softc *sc)
+{
+ u_int i;
+ uint8_t bbp;
+
+ /* Apply maximum likelihood detection for 2 stream case. */
+ run_bbp_read(sc, 105, &bbp);
+ if (sc->nrxchains > 1)
+ run_bbp_write(sc, 105, bbp | RT5390_MLD);
+
+ /* Avoid data lost and CRC error. */
+ run_bbp_read(sc, 4, &bbp);
+ run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL);
+
+ if (sc->mac_ver == 0x5592) {
+ for (i = 0; i < nitems(rt5592_def_bbp); i++) {
+ run_bbp_write(sc, rt5592_def_bbp[i].reg,
+ rt5592_def_bbp[i].val);
+ }
+ for (i = 0; i < nitems(rt5592_bbp_r196); i++) {
+ run_bbp_write(sc, 195, i + 0x80);
+ run_bbp_write(sc, 196, rt5592_bbp_r196[i]);
+ }
+ } else {
+ for (i = 0; i < nitems(rt5390_def_bbp); i++) {
+ run_bbp_write(sc, rt5390_def_bbp[i].reg,
+ rt5390_def_bbp[i].val);
+ }
+ }
+ if (sc->mac_ver == 0x5392) {
+ run_bbp_write(sc, 88, 0x90);
+ run_bbp_write(sc, 95, 0x9a);
+ run_bbp_write(sc, 98, 0x12);
+ run_bbp_write(sc, 106, 0x12);
+ run_bbp_write(sc, 134, 0xd0);
+ run_bbp_write(sc, 135, 0xf6);
+ run_bbp_write(sc, 148, 0x84);
+ }
+
+ run_bbp_read(sc, 152, &bbp);
+ run_bbp_write(sc, 152, bbp | 0x80);
+
+ /* Fix BBP254 for RT5592C. */
+ if (sc->mac_ver == 0x5592 && sc->mac_rev >= 0x0221) {
+ run_bbp_read(sc, 254, &bbp);
+ run_bbp_write(sc, 254, bbp | 0x80);
+ }
+
+ /* Disable hardware antenna diversity. */
+ if (sc->mac_ver == 0x5390)
+ run_bbp_write(sc, 154, 0);
+
+ /* Initialize Rx CCK/OFDM frequency offset report. */
+ run_bbp_write(sc, 142, 1);
+ run_bbp_write(sc, 143, 57);
+}
+
+static int
+run_bbp_init(struct run_softc *sc)
+{
+ int i, error, ntries;
+ uint8_t bbp0;
+
+ /* wait for BBP to wake up */
+ for (ntries = 0; ntries < 20; ntries++) {
+ if ((error = run_bbp_read(sc, 0, &bbp0)) != 0)
+ return error;
+ if (bbp0 != 0 && bbp0 != 0xff)
+ break;
+ }
+ if (ntries == 20)
+ return (ETIMEDOUT);
+
+ /* initialize BBP registers to default values */
+ if (sc->mac_ver >= 0x5390)
+ run_rt5390_bbp_init(sc);
+ else {
+ for (i = 0; i < nitems(rt2860_def_bbp); i++) {
+ run_bbp_write(sc, rt2860_def_bbp[i].reg,
+ rt2860_def_bbp[i].val);
+ }
+ }
+
+ if (sc->mac_ver == 0x3593) {
+ run_bbp_write(sc, 79, 0x13);
+ run_bbp_write(sc, 80, 0x05);
+ run_bbp_write(sc, 81, 0x33);
+ run_bbp_write(sc, 86, 0x46);
+ run_bbp_write(sc, 137, 0x0f);
+ }
+
+ /* fix BBP84 for RT2860E */
+ if (sc->mac_ver == 0x2860 && sc->mac_rev != 0x0101)
+ run_bbp_write(sc, 84, 0x19);
+
+ if (sc->mac_ver >= 0x3070 && (sc->mac_ver != 0x3593 &&
+ sc->mac_ver != 0x5592)) {
+ run_bbp_write(sc, 79, 0x13);
+ run_bbp_write(sc, 80, 0x05);
+ run_bbp_write(sc, 81, 0x33);
+ } else if (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) {
+ run_bbp_write(sc, 69, 0x16);
+ run_bbp_write(sc, 73, 0x12);
+ }
+ return (0);
+}
+
+static int
+run_rt3070_rf_init(struct run_softc *sc)
+{
+ uint32_t tmp;
+ uint8_t bbp4, mingain, rf, target;
+ u_int i;
+
+ run_rt3070_rf_read(sc, 30, &rf);
+ /* toggle RF R30 bit 7 */
+ run_rt3070_rf_write(sc, 30, rf | 0x80);
+ run_delay(sc, 10);
+ run_rt3070_rf_write(sc, 30, rf & ~0x80);
+
+ /* initialize RF registers to default value */
+ if (sc->mac_ver == 0x3572) {
+ for (i = 0; i < nitems(rt3572_def_rf); i++) {
+ run_rt3070_rf_write(sc, rt3572_def_rf[i].reg,
+ rt3572_def_rf[i].val);
+ }
+ } else {
+ for (i = 0; i < nitems(rt3070_def_rf); i++) {
+ run_rt3070_rf_write(sc, rt3070_def_rf[i].reg,
+ rt3070_def_rf[i].val);
+ }
+ }
+
+ if (sc->mac_ver == 0x3070 && sc->mac_rev < 0x0201) {
+ /*
+ * Change voltage from 1.2V to 1.35V for RT3070.
+ * The DAC issue (RT3070_LDO_CFG0) has been fixed
+ * in RT3070(F).
+ */
+ run_read(sc, RT3070_LDO_CFG0, &tmp);
+ tmp = (tmp & ~0x0f000000) | 0x0d000000;
+ run_write(sc, RT3070_LDO_CFG0, tmp);
+
+ } else if (sc->mac_ver == 0x3071) {
+ run_rt3070_rf_read(sc, 6, &rf);
+ run_rt3070_rf_write(sc, 6, rf | 0x40);
+ run_rt3070_rf_write(sc, 31, 0x14);
+
+ run_read(sc, RT3070_LDO_CFG0, &tmp);
+ tmp &= ~0x1f000000;
+ if (sc->mac_rev < 0x0211)
+ tmp |= 0x0d000000; /* 1.3V */
+ else
+ tmp |= 0x01000000; /* 1.2V */
+ run_write(sc, RT3070_LDO_CFG0, tmp);
+
+ /* patch LNA_PE_G1 */
+ run_read(sc, RT3070_GPIO_SWITCH, &tmp);
+ run_write(sc, RT3070_GPIO_SWITCH, tmp & ~0x20);
+
+ } else if (sc->mac_ver == 0x3572) {
+ run_rt3070_rf_read(sc, 6, &rf);
+ run_rt3070_rf_write(sc, 6, rf | 0x40);
+
+ /* increase voltage from 1.2V to 1.35V */
+ run_read(sc, RT3070_LDO_CFG0, &tmp);
+ tmp = (tmp & ~0x1f000000) | 0x0d000000;
+ run_write(sc, RT3070_LDO_CFG0, tmp);
+
+ if (sc->mac_rev < 0x0211 || !sc->patch_dac) {
+ run_delay(sc, 1); /* wait for 1msec */
+ /* decrease voltage back to 1.2V */
+ tmp = (tmp & ~0x1f000000) | 0x01000000;
+ run_write(sc, RT3070_LDO_CFG0, tmp);
+ }
+ }
+
+ /* select 20MHz bandwidth */
+ run_rt3070_rf_read(sc, 31, &rf);
+ run_rt3070_rf_write(sc, 31, rf & ~0x20);
+
+ /* calibrate filter for 20MHz bandwidth */
+ sc->rf24_20mhz = 0x1f; /* default value */
+ target = (sc->mac_ver < 0x3071) ? 0x16 : 0x13;
+ run_rt3070_filter_calib(sc, 0x07, target, &sc->rf24_20mhz);
+
+ /* select 40MHz bandwidth */
+ run_bbp_read(sc, 4, &bbp4);
+ run_bbp_write(sc, 4, (bbp4 & ~0x18) | 0x10);
+ run_rt3070_rf_read(sc, 31, &rf);
+ run_rt3070_rf_write(sc, 31, rf | 0x20);
+
+ /* calibrate filter for 40MHz bandwidth */
+ sc->rf24_40mhz = 0x2f; /* default value */
+ target = (sc->mac_ver < 0x3071) ? 0x19 : 0x15;
+ run_rt3070_filter_calib(sc, 0x27, target, &sc->rf24_40mhz);
+
+ /* go back to 20MHz bandwidth */
+ run_bbp_read(sc, 4, &bbp4);
+ run_bbp_write(sc, 4, bbp4 & ~0x18);
+
+ if (sc->mac_ver == 0x3572) {
+ /* save default BBP registers 25 and 26 values */
+ run_bbp_read(sc, 25, &sc->bbp25);
+ run_bbp_read(sc, 26, &sc->bbp26);
+ } else if (sc->mac_rev < 0x0201 || sc->mac_rev < 0x0211)
+ run_rt3070_rf_write(sc, 27, 0x03);
+
+ run_read(sc, RT3070_OPT_14, &tmp);
+ run_write(sc, RT3070_OPT_14, tmp | 1);
+
+ if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) {
+ run_rt3070_rf_read(sc, 17, &rf);
+ rf &= ~RT3070_TX_LO1;
+ if ((sc->mac_ver == 0x3070 ||
+ (sc->mac_ver == 0x3071 && sc->mac_rev >= 0x0211)) &&
+ !sc->ext_2ghz_lna)
+ rf |= 0x20; /* fix for long range Rx issue */
+ mingain = (sc->mac_ver == 0x3070) ? 1 : 2;
+ if (sc->txmixgain_2ghz >= mingain)
+ rf = (rf & ~0x7) | sc->txmixgain_2ghz;
+ run_rt3070_rf_write(sc, 17, rf);
+ }
+
+ if (sc->mac_ver == 0x3071) {
+ run_rt3070_rf_read(sc, 1, &rf);
+ rf &= ~(RT3070_RX0_PD | RT3070_TX0_PD);
+ rf |= RT3070_RF_BLOCK | RT3070_RX1_PD | RT3070_TX1_PD;
+ run_rt3070_rf_write(sc, 1, rf);
+
+ run_rt3070_rf_read(sc, 15, &rf);
+ run_rt3070_rf_write(sc, 15, rf & ~RT3070_TX_LO2);
+
+ run_rt3070_rf_read(sc, 20, &rf);
+ run_rt3070_rf_write(sc, 20, rf & ~RT3070_RX_LO1);
+
+ run_rt3070_rf_read(sc, 21, &rf);
+ run_rt3070_rf_write(sc, 21, rf & ~RT3070_RX_LO2);
+ }
+
+ if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) {
+ /* fix Tx to Rx IQ glitch by raising RF voltage */
+ run_rt3070_rf_read(sc, 27, &rf);
+ rf &= ~0x77;
+ if (sc->mac_rev < 0x0211)
+ rf |= 0x03;
+ run_rt3070_rf_write(sc, 27, rf);
+ }
+ return (0);
+}
+
+static void
+run_rt3593_rf_init(struct run_softc *sc)
+{
+ uint32_t tmp;
+ uint8_t rf;
+ u_int i;
+
+ /* Disable the GPIO bits 4 and 7 for LNA PE control. */
+ run_read(sc, RT3070_GPIO_SWITCH, &tmp);
+ tmp &= ~(1 << 4 | 1 << 7);
+ run_write(sc, RT3070_GPIO_SWITCH, tmp);
+
+ /* Initialize RF registers to default value. */
+ for (i = 0; i < nitems(rt3593_def_rf); i++) {
+ run_rt3070_rf_write(sc, rt3593_def_rf[i].reg,
+ rt3593_def_rf[i].val);
+ }
+
+ /* Toggle RF R2 to initiate calibration. */
+ run_rt3070_rf_write(sc, 2, RT5390_RESCAL);
+
+ /* Initialize RF frequency offset. */
+ run_adjust_freq_offset(sc);
+
+ run_rt3070_rf_read(sc, 18, &rf);
+ run_rt3070_rf_write(sc, 18, rf | RT3593_AUTOTUNE_BYPASS);
+
+ /*
+ * Increase voltage from 1.2V to 1.35V, wait for 1 msec to
+ * decrease voltage back to 1.2V.
+ */
+ run_read(sc, RT3070_LDO_CFG0, &tmp);
+ tmp = (tmp & ~0x1f000000) | 0x0d000000;
+ run_write(sc, RT3070_LDO_CFG0, tmp);
+ run_delay(sc, 1);
+ tmp = (tmp & ~0x1f000000) | 0x01000000;
+ run_write(sc, RT3070_LDO_CFG0, tmp);
+
+ sc->rf24_20mhz = 0x1f;
+ sc->rf24_40mhz = 0x2f;
+
+ /* Save default BBP registers 25 and 26 values. */
+ run_bbp_read(sc, 25, &sc->bbp25);
+ run_bbp_read(sc, 26, &sc->bbp26);
+
+ run_read(sc, RT3070_OPT_14, &tmp);
+ run_write(sc, RT3070_OPT_14, tmp | 1);
+}
+
+static void
+run_rt5390_rf_init(struct run_softc *sc)
+{
+ uint32_t tmp;
+ uint8_t rf;
+ u_int i;
+
+ /* Toggle RF R2 to initiate calibration. */
+ if (sc->mac_ver == 0x5390) {
+ run_rt3070_rf_read(sc, 2, &rf);
+ run_rt3070_rf_write(sc, 2, rf | RT5390_RESCAL);
+ run_delay(sc, 10);
+ run_rt3070_rf_write(sc, 2, rf & ~RT5390_RESCAL);
+ } else {
+ run_rt3070_rf_write(sc, 2, RT5390_RESCAL);
+ run_delay(sc, 10);
+ }
+
+ /* Initialize RF registers to default value. */
+ if (sc->mac_ver == 0x5592) {
+ for (i = 0; i < nitems(rt5592_def_rf); i++) {
+ run_rt3070_rf_write(sc, rt5592_def_rf[i].reg,
+ rt5592_def_rf[i].val);
+ }
+ /* Initialize RF frequency offset. */
+ run_adjust_freq_offset(sc);
+ } else if (sc->mac_ver == 0x5392) {
+ for (i = 0; i < nitems(rt5392_def_rf); i++) {
+ run_rt3070_rf_write(sc, rt5392_def_rf[i].reg,
+ rt5392_def_rf[i].val);
+ }
+ if (sc->mac_rev >= 0x0223) {
+ run_rt3070_rf_write(sc, 23, 0x0f);
+ run_rt3070_rf_write(sc, 24, 0x3e);
+ run_rt3070_rf_write(sc, 51, 0x32);
+ run_rt3070_rf_write(sc, 53, 0x22);
+ run_rt3070_rf_write(sc, 56, 0xc1);
+ run_rt3070_rf_write(sc, 59, 0x0f);
+ }
+ } else {
+ for (i = 0; i < nitems(rt5390_def_rf); i++) {
+ run_rt3070_rf_write(sc, rt5390_def_rf[i].reg,
+ rt5390_def_rf[i].val);
+ }
+ if (sc->mac_rev >= 0x0502) {
+ run_rt3070_rf_write(sc, 6, 0xe0);
+ run_rt3070_rf_write(sc, 25, 0x80);
+ run_rt3070_rf_write(sc, 46, 0x73);
+ run_rt3070_rf_write(sc, 53, 0x00);
+ run_rt3070_rf_write(sc, 56, 0x42);
+ run_rt3070_rf_write(sc, 61, 0xd1);
+ }
+ }
+
+ sc->rf24_20mhz = 0x1f; /* default value */
+ sc->rf24_40mhz = (sc->mac_ver == 0x5592) ? 0 : 0x2f;
+
+ if (sc->mac_rev < 0x0211)
+ run_rt3070_rf_write(sc, 27, 0x3);
+
+ run_read(sc, RT3070_OPT_14, &tmp);
+ run_write(sc, RT3070_OPT_14, tmp | 1);
+}
+
+static int
+run_rt3070_filter_calib(struct run_softc *sc, uint8_t init, uint8_t target,
+ uint8_t *val)
+{
+ uint8_t rf22, rf24;
+ uint8_t bbp55_pb, bbp55_sb, delta;
+ int ntries;
+
+ /* program filter */
+ run_rt3070_rf_read(sc, 24, &rf24);
+ rf24 = (rf24 & 0xc0) | init; /* initial filter value */
+ run_rt3070_rf_write(sc, 24, rf24);
+
+ /* enable baseband loopback mode */
+ run_rt3070_rf_read(sc, 22, &rf22);
+ run_rt3070_rf_write(sc, 22, rf22 | 0x01);
+
+ /* set power and frequency of passband test tone */
+ run_bbp_write(sc, 24, 0x00);
+ for (ntries = 0; ntries < 100; ntries++) {
+ /* transmit test tone */
+ run_bbp_write(sc, 25, 0x90);
+ run_delay(sc, 10);
+ /* read received power */
+ run_bbp_read(sc, 55, &bbp55_pb);
+ if (bbp55_pb != 0)
+ break;
+ }
+ if (ntries == 100)
+ return (ETIMEDOUT);
+
+ /* set power and frequency of stopband test tone */
+ run_bbp_write(sc, 24, 0x06);
+ for (ntries = 0; ntries < 100; ntries++) {
+ /* transmit test tone */
+ run_bbp_write(sc, 25, 0x90);
+ run_delay(sc, 10);
+ /* read received power */
+ run_bbp_read(sc, 55, &bbp55_sb);
+
+ delta = bbp55_pb - bbp55_sb;
+ if (delta > target)
+ break;
+
+ /* reprogram filter */
+ rf24++;
+ run_rt3070_rf_write(sc, 24, rf24);
+ }
+ if (ntries < 100) {
+ if (rf24 != init)
+ rf24--; /* backtrack */
+ *val = rf24;
+ run_rt3070_rf_write(sc, 24, rf24);
+ }
+
+ /* restore initial state */
+ run_bbp_write(sc, 24, 0x00);
+
+ /* disable baseband loopback mode */
+ run_rt3070_rf_read(sc, 22, &rf22);
+ run_rt3070_rf_write(sc, 22, rf22 & ~0x01);
+
+ return (0);
+}
+
+static void
+run_rt3070_rf_setup(struct run_softc *sc)
+{
+ uint8_t bbp, rf;
+ int i;
+
+ if (sc->mac_ver == 0x3572) {
+ /* enable DC filter */
+ if (sc->mac_rev >= 0x0201)
+ run_bbp_write(sc, 103, 0xc0);
+
+ run_bbp_read(sc, 138, &bbp);
+ if (sc->ntxchains == 1)
+ bbp |= 0x20; /* turn off DAC1 */
+ if (sc->nrxchains == 1)
+ bbp &= ~0x02; /* turn off ADC1 */
+ run_bbp_write(sc, 138, bbp);
+
+ if (sc->mac_rev >= 0x0211) {
+ /* improve power consumption */
+ run_bbp_read(sc, 31, &bbp);
+ run_bbp_write(sc, 31, bbp & ~0x03);
+ }
+
+ run_rt3070_rf_read(sc, 16, &rf);
+ rf = (rf & ~0x07) | sc->txmixgain_2ghz;
+ run_rt3070_rf_write(sc, 16, rf);
+
+ } else if (sc->mac_ver == 0x3071) {
+ if (sc->mac_rev >= 0x0211) {
+ /* enable DC filter */
+ run_bbp_write(sc, 103, 0xc0);
+
+ /* improve power consumption */
+ run_bbp_read(sc, 31, &bbp);
+ run_bbp_write(sc, 31, bbp & ~0x03);
+ }
+
+ run_bbp_read(sc, 138, &bbp);
+ if (sc->ntxchains == 1)
+ bbp |= 0x20; /* turn off DAC1 */
+ if (sc->nrxchains == 1)
+ bbp &= ~0x02; /* turn off ADC1 */
+ run_bbp_write(sc, 138, bbp);
+
+ run_write(sc, RT2860_TX_SW_CFG1, 0);
+ if (sc->mac_rev < 0x0211) {
+ run_write(sc, RT2860_TX_SW_CFG2,
+ sc->patch_dac ? 0x2c : 0x0f);
+ } else
+ run_write(sc, RT2860_TX_SW_CFG2, 0);
+
+ } else if (sc->mac_ver == 0x3070) {
+ if (sc->mac_rev >= 0x0201) {
+ /* enable DC filter */
+ run_bbp_write(sc, 103, 0xc0);
+
+ /* improve power consumption */
+ run_bbp_read(sc, 31, &bbp);
+ run_bbp_write(sc, 31, bbp & ~0x03);
+ }
+
+ if (sc->mac_rev < 0x0201) {
+ run_write(sc, RT2860_TX_SW_CFG1, 0);
+ run_write(sc, RT2860_TX_SW_CFG2, 0x2c);
+ } else
+ run_write(sc, RT2860_TX_SW_CFG2, 0);
+ }
+
+ /* initialize RF registers from ROM for >=RT3071*/
+ if (sc->mac_ver >= 0x3071) {
+ for (i = 0; i < 10; i++) {
+ if (sc->rf[i].reg == 0 || sc->rf[i].reg == 0xff)
+ continue;
+ run_rt3070_rf_write(sc, sc->rf[i].reg, sc->rf[i].val);
+ }
+ }
+}
+
+static void
+run_rt3593_rf_setup(struct run_softc *sc)
+{
+ uint8_t bbp, rf;
+
+ if (sc->mac_rev >= 0x0211) {
+ /* Enable DC filter. */
+ run_bbp_write(sc, 103, 0xc0);
+ }
+ run_write(sc, RT2860_TX_SW_CFG1, 0);
+ if (sc->mac_rev < 0x0211) {
+ run_write(sc, RT2860_TX_SW_CFG2,
+ sc->patch_dac ? 0x2c : 0x0f);
+ } else
+ run_write(sc, RT2860_TX_SW_CFG2, 0);
+
+ run_rt3070_rf_read(sc, 50, &rf);
+ run_rt3070_rf_write(sc, 50, rf & ~RT3593_TX_LO2);
+
+ run_rt3070_rf_read(sc, 51, &rf);
+ rf = (rf & ~(RT3593_TX_LO1 | 0x0c)) |
+ ((sc->txmixgain_2ghz & 0x07) << 2);
+ run_rt3070_rf_write(sc, 51, rf);
+
+ run_rt3070_rf_read(sc, 38, &rf);
+ run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1);
+
+ run_rt3070_rf_read(sc, 39, &rf);
+ run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2);
+
+ run_rt3070_rf_read(sc, 1, &rf);
+ run_rt3070_rf_write(sc, 1, rf & ~(RT3070_RF_BLOCK | RT3070_PLL_PD));
+
+ run_rt3070_rf_read(sc, 30, &rf);
+ rf = (rf & ~0x18) | 0x10;
+ run_rt3070_rf_write(sc, 30, rf);
+
+ /* Apply maximum likelihood detection for 2 stream case. */
+ run_bbp_read(sc, 105, &bbp);
+ if (sc->nrxchains > 1)
+ run_bbp_write(sc, 105, bbp | RT5390_MLD);
+
+ /* Avoid data lost and CRC error. */
+ run_bbp_read(sc, 4, &bbp);
+ run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL);
+
+ run_bbp_write(sc, 92, 0x02);
+ run_bbp_write(sc, 82, 0x82);
+ run_bbp_write(sc, 106, 0x05);
+ run_bbp_write(sc, 104, 0x92);
+ run_bbp_write(sc, 88, 0x90);
+ run_bbp_write(sc, 148, 0xc8);
+ run_bbp_write(sc, 47, 0x48);
+ run_bbp_write(sc, 120, 0x50);
+
+ run_bbp_write(sc, 163, 0x9d);
+
+ /* SNR mapping. */
+ run_bbp_write(sc, 142, 0x06);
+ run_bbp_write(sc, 143, 0xa0);
+ run_bbp_write(sc, 142, 0x07);
+ run_bbp_write(sc, 143, 0xa1);
+ run_bbp_write(sc, 142, 0x08);
+ run_bbp_write(sc, 143, 0xa2);
+
+ run_bbp_write(sc, 31, 0x08);
+ run_bbp_write(sc, 68, 0x0b);
+ run_bbp_write(sc, 105, 0x04);
+}
+
+static void
+run_rt5390_rf_setup(struct run_softc *sc)
+{
+ uint8_t bbp, rf;
+
+ if (sc->mac_rev >= 0x0211) {
+ /* Enable DC filter. */
+ run_bbp_write(sc, 103, 0xc0);
+
+ if (sc->mac_ver != 0x5592) {
+ /* Improve power consumption. */
+ run_bbp_read(sc, 31, &bbp);
+ run_bbp_write(sc, 31, bbp & ~0x03);
+ }
+ }
+
+ run_bbp_read(sc, 138, &bbp);
+ if (sc->ntxchains == 1)
+ bbp |= 0x20; /* turn off DAC1 */
+ if (sc->nrxchains == 1)
+ bbp &= ~0x02; /* turn off ADC1 */
+ run_bbp_write(sc, 138, bbp);
+
+ run_rt3070_rf_read(sc, 38, &rf);
+ run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1);
+
+ run_rt3070_rf_read(sc, 39, &rf);
+ run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2);
+
+ /* Avoid data lost and CRC error. */
+ run_bbp_read(sc, 4, &bbp);
+ run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL);
+
+ run_rt3070_rf_read(sc, 30, &rf);
+ rf = (rf & ~0x18) | 0x10;
+ run_rt3070_rf_write(sc, 30, rf);
+
+ if (sc->mac_ver != 0x5592) {
+ run_write(sc, RT2860_TX_SW_CFG1, 0);
+ if (sc->mac_rev < 0x0211) {
+ run_write(sc, RT2860_TX_SW_CFG2,
+ sc->patch_dac ? 0x2c : 0x0f);
+ } else
+ run_write(sc, RT2860_TX_SW_CFG2, 0);
+ }
+}
+
+static int
+run_txrx_enable(struct run_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t tmp;
+ int error, ntries;
+
+ run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN);
+ for (ntries = 0; ntries < 200; ntries++) {
+ if ((error = run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp)) != 0)
+ return (error);
+ if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
+ break;
+ run_delay(sc, 50);
+ }
+ if (ntries == 200)
+ return (ETIMEDOUT);
+
+ run_delay(sc, 50);
+
+ tmp |= RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | RT2860_TX_WB_DDONE;
+ run_write(sc, RT2860_WPDMA_GLO_CFG, tmp);
+
+ /* enable Rx bulk aggregation (set timeout and limit) */
+ tmp = RT2860_USB_TX_EN | RT2860_USB_RX_EN | RT2860_USB_RX_AGG_EN |
+ RT2860_USB_RX_AGG_TO(128) | RT2860_USB_RX_AGG_LMT(2);
+ run_write(sc, RT2860_USB_DMA_CFG, tmp);
+
+ /* set Rx filter */
+ tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR;
+ if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL |
+ RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK |
+ RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV |
+ RT2860_DROP_CFACK | RT2860_DROP_CFEND;
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL;
+ }
+ run_write(sc, RT2860_RX_FILTR_CFG, tmp);
+
+ run_write(sc, RT2860_MAC_SYS_CTRL,
+ RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
+
+ return (0);
+}
+
+static void
+run_adjust_freq_offset(struct run_softc *sc)
+{
+ uint8_t rf, tmp;
+
+ run_rt3070_rf_read(sc, 17, &rf);
+ tmp = rf;
+ rf = (rf & ~0x7f) | (sc->freq & 0x7f);
+ rf = MIN(rf, 0x5f);
+
+ if (tmp != rf)
+ run_mcu_cmd(sc, 0x74, (tmp << 8 ) | rf);
+}
+
+static void
+run_init_locked(struct run_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+ uint8_t bbp1, bbp3;
+ int i;
+ int ridx;
+ int ntries;
+
+ if (ic->ic_nrunning > 1)
+ return;
+
+ run_stop(sc);
+
+ if (run_load_microcode(sc) != 0) {
+ device_printf(sc->sc_dev, "could not load 8051 microcode\n");
+ goto fail;
+ }
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (run_read(sc, RT2860_ASIC_VER_ID, &tmp) != 0)
+ goto fail;
+ if (tmp != 0 && tmp != 0xffffffff)
+ break;
+ run_delay(sc, 10);
+ }
+ if (ntries == 100)
+ goto fail;
+
+ for (i = 0; i != RUN_EP_QUEUES; i++)
+ run_setup_tx_list(sc, &sc->sc_epq[i]);
+
+ run_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0)
+ goto fail;
+ if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
+ break;
+ run_delay(sc, 10);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for DMA engine\n");
+ goto fail;
+ }
+ tmp &= 0xff0;
+ tmp |= RT2860_TX_WB_DDONE;
+ run_write(sc, RT2860_WPDMA_GLO_CFG, tmp);
+
+ /* turn off PME_OEN to solve high-current issue */
+ run_read(sc, RT2860_SYS_CTRL, &tmp);
+ run_write(sc, RT2860_SYS_CTRL, tmp & ~RT2860_PME_OEN);
+
+ run_write(sc, RT2860_MAC_SYS_CTRL,
+ RT2860_BBP_HRST | RT2860_MAC_SRST);
+ run_write(sc, RT2860_USB_DMA_CFG, 0);
+
+ if (run_reset(sc) != 0) {
+ device_printf(sc->sc_dev, "could not reset chipset\n");
+ goto fail;
+ }
+
+ run_write(sc, RT2860_MAC_SYS_CTRL, 0);
+
+ /* init Tx power for all Tx rates (from EEPROM) */
+ for (ridx = 0; ridx < 5; ridx++) {
+ if (sc->txpow20mhz[ridx] == 0xffffffff)
+ continue;
+ run_write(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]);
+ }
+
+ for (i = 0; i < nitems(rt2870_def_mac); i++)
+ run_write(sc, rt2870_def_mac[i].reg, rt2870_def_mac[i].val);
+ run_write(sc, RT2860_WMM_AIFSN_CFG, 0x00002273);
+ run_write(sc, RT2860_WMM_CWMIN_CFG, 0x00002344);
+ run_write(sc, RT2860_WMM_CWMAX_CFG, 0x000034aa);
+
+ if (sc->mac_ver >= 0x5390) {
+ run_write(sc, RT2860_TX_SW_CFG0,
+ 4 << RT2860_DLY_PAPE_EN_SHIFT | 4);
+ if (sc->mac_ver >= 0x5392) {
+ run_write(sc, RT2860_MAX_LEN_CFG, 0x00002fff);
+ if (sc->mac_ver == 0x5592) {
+ run_write(sc, RT2860_HT_FBK_CFG1, 0xedcba980);
+ run_write(sc, RT2860_TXOP_HLDR_ET, 0x00000082);
+ } else {
+ run_write(sc, RT2860_HT_FBK_CFG1, 0xedcb4980);
+ run_write(sc, RT2860_LG_FBK_CFG0, 0xedcba322);
+ }
+ }
+ } else if (sc->mac_ver == 0x3593) {
+ run_write(sc, RT2860_TX_SW_CFG0,
+ 4 << RT2860_DLY_PAPE_EN_SHIFT | 2);
+ } else if (sc->mac_ver >= 0x3070) {
+ /* set delay of PA_PE assertion to 1us (unit of 0.25us) */
+ run_write(sc, RT2860_TX_SW_CFG0,
+ 4 << RT2860_DLY_PAPE_EN_SHIFT);
+ }
+
+ /* wait while MAC is busy */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (run_read(sc, RT2860_MAC_STATUS_REG, &tmp) != 0)
+ goto fail;
+ if (!(tmp & (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY)))
+ break;
+ run_delay(sc, 10);
+ }
+ if (ntries == 100)
+ goto fail;
+
+ /* clear Host to MCU mailbox */
+ run_write(sc, RT2860_H2M_BBPAGENT, 0);
+ run_write(sc, RT2860_H2M_MAILBOX, 0);
+ run_delay(sc, 10);
+
+ if (run_bbp_init(sc) != 0) {
+ device_printf(sc->sc_dev, "could not initialize BBP\n");
+ goto fail;
+ }
+
+ /* abort TSF synchronization */
+ run_read(sc, RT2860_BCN_TIME_CFG, &tmp);
+ tmp &= ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN |
+ RT2860_TBTT_TIMER_EN);
+ run_write(sc, RT2860_BCN_TIME_CFG, tmp);
+
+ /* clear RX WCID search table */
+ run_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512);
+ /* clear WCID attribute table */
+ run_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 8 * 32);
+
+ /* hostapd sets a key before init. So, don't clear it. */
+ if (sc->cmdq_key_set != RUN_CMDQ_GO) {
+ /* clear shared key table */
+ run_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32);
+ /* clear shared key mode */
+ run_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4);
+ }
+
+ run_read(sc, RT2860_US_CYC_CNT, &tmp);
+ tmp = (tmp & ~0xff) | 0x1e;
+ run_write(sc, RT2860_US_CYC_CNT, tmp);
+
+ if (sc->mac_rev != 0x0101)
+ run_write(sc, RT2860_TXOP_CTRL_CFG, 0x0000583f);
+
+ run_write(sc, RT2860_WMM_TXOP0_CFG, 0);
+ run_write(sc, RT2860_WMM_TXOP1_CFG, 48 << 16 | 96);
+
+ /* write vendor-specific BBP values (from EEPROM) */
+ if (sc->mac_ver < 0x3593) {
+ for (i = 0; i < 10; i++) {
+ if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff)
+ continue;
+ run_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val);
+ }
+ }
+
+ /* select Main antenna for 1T1R devices */
+ if (sc->rf_rev == RT3070_RF_3020 || sc->rf_rev == RT5390_RF_5370)
+ run_set_rx_antenna(sc, 0);
+
+ /* send LEDs operating mode to microcontroller */
+ (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]);
+ (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]);
+ (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]);
+
+ if (sc->mac_ver >= 0x5390)
+ run_rt5390_rf_init(sc);
+ else if (sc->mac_ver == 0x3593)
+ run_rt3593_rf_init(sc);
+ else if (sc->mac_ver >= 0x3070)
+ run_rt3070_rf_init(sc);
+
+ /* disable non-existing Rx chains */
+ run_bbp_read(sc, 3, &bbp3);
+ bbp3 &= ~(1 << 3 | 1 << 4);
+ if (sc->nrxchains == 2)
+ bbp3 |= 1 << 3;
+ else if (sc->nrxchains == 3)
+ bbp3 |= 1 << 4;
+ run_bbp_write(sc, 3, bbp3);
+
+ /* disable non-existing Tx chains */
+ run_bbp_read(sc, 1, &bbp1);
+ if (sc->ntxchains == 1)
+ bbp1 &= ~(1 << 3 | 1 << 4);
+ run_bbp_write(sc, 1, bbp1);
+
+ if (sc->mac_ver >= 0x5390)
+ run_rt5390_rf_setup(sc);
+ else if (sc->mac_ver == 0x3593)
+ run_rt3593_rf_setup(sc);
+ else if (sc->mac_ver >= 0x3070)
+ run_rt3070_rf_setup(sc);
+
+ /* select default channel */
+ run_set_chan(sc, ic->ic_curchan);
+
+ /* setup initial protection mode */
+ run_updateprot_cb(ic);
+
+ /* turn radio LED on */
+ run_set_leds(sc, RT2860_LED_RADIO);
+
+ sc->sc_flags |= RUN_RUNNING;
+ sc->cmdq_run = RUN_CMDQ_GO;
+
+ for (i = 0; i != RUN_N_XFER; i++)
+ usbd_xfer_set_stall(sc->sc_xfer[i]);
+
+ usbd_transfer_start(sc->sc_xfer[RUN_BULK_RX]);
+
+ if (run_txrx_enable(sc) != 0)
+ goto fail;
+
+ return;
+
+fail:
+ run_stop(sc);
+}
+
+static void
+run_stop(void *arg)
+{
+ struct run_softc *sc = (struct run_softc *)arg;
+ uint32_t tmp;
+ int i;
+ int ntries;
+
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (sc->sc_flags & RUN_RUNNING)
+ run_set_leds(sc, 0); /* turn all LEDs off */
+
+ sc->sc_flags &= ~RUN_RUNNING;
+
+ sc->ratectl_run = RUN_RATECTL_OFF;
+ sc->cmdq_run = sc->cmdq_key_set;
+
+ RUN_UNLOCK(sc);
+
+ for(i = 0; i < RUN_N_XFER; i++)
+ usbd_transfer_drain(sc->sc_xfer[i]);
+
+ RUN_LOCK(sc);
+
+ run_drain_mbufq(sc);
+
+ if (sc->rx_m != NULL) {
+ m_free(sc->rx_m);
+ sc->rx_m = NULL;
+ }
+
+ /* Disable Tx/Rx DMA. */
+ if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0)
+ return;
+ tmp &= ~(RT2860_RX_DMA_EN | RT2860_TX_DMA_EN);
+ run_write(sc, RT2860_WPDMA_GLO_CFG, tmp);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0)
+ return;
+ if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
+ break;
+ run_delay(sc, 10);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for DMA engine\n");
+ return;
+ }
+
+ /* disable Tx/Rx */
+ run_read(sc, RT2860_MAC_SYS_CTRL, &tmp);
+ tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
+ run_write(sc, RT2860_MAC_SYS_CTRL, tmp);
+
+ /* wait for pending Tx to complete */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (run_read(sc, RT2860_TXRXQ_PCNT, &tmp) != 0) {
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET,
+ "Cannot read Tx queue count\n");
+ break;
+ }
+ if ((tmp & RT2860_TX2Q_PCNT_MASK) == 0) {
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET,
+ "All Tx cleared\n");
+ break;
+ }
+ run_delay(sc, 10);
+ }
+ if (ntries >= 100)
+ RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET,
+ "There are still pending Tx\n");
+ run_delay(sc, 10);
+ run_write(sc, RT2860_USB_DMA_CFG, 0);
+
+ run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST);
+ run_write(sc, RT2860_MAC_SYS_CTRL, 0);
+
+ for (i = 0; i != RUN_EP_QUEUES; i++)
+ run_unsetup_tx_list(sc, &sc->sc_epq[i]);
+}
+
+static void
+run_delay(struct run_softc *sc, u_int ms)
+{
+ usb_pause_mtx(mtx_owned(&sc->sc_mtx) ?
+ &sc->sc_mtx : NULL, USB_MS_TO_TICKS(ms));
+}
+
+static device_method_t run_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, run_match),
+ DEVMETHOD(device_attach, run_attach),
+ DEVMETHOD(device_detach, run_detach),
+ DEVMETHOD_END
+};
+
+static driver_t run_driver = {
+ .name = "run",
+ .methods = run_methods,
+ .size = sizeof(struct run_softc)
+};
+
+static devclass_t run_devclass;
+
+DRIVER_MODULE(run, uhub, run_driver, run_devclass, run_driver_loaded, NULL);
+MODULE_DEPEND(run, wlan, 1, 1, 1);
+MODULE_DEPEND(run, usb, 1, 1, 1);
+MODULE_DEPEND(run, firmware, 1, 1, 1);
+MODULE_VERSION(run, 1);
+USB_PNP_HOST_INFO(run_devs);
diff --git a/freebsd/sys/dev/usb/wlan/if_runreg.h b/freebsd/sys/dev/usb/wlan/if_runreg.h
new file mode 100644
index 00000000..c09aac8f
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_runreg.h
@@ -0,0 +1,1669 @@
+/* $OpenBSD: rt2860reg.h,v 1.19 2009/05/18 19:25:07 damien Exp $ */
+
+/*-
+ * Copyright (c) 2007
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IF_RUNREG_H_
+#define _IF_RUNREG_H_
+
+#define RT2860_CONFIG_NO 1
+#define RT2860_IFACE_INDEX 0
+
+#define RT3070_OPT_14 0x0114
+
+/* SCH/DMA registers */
+#define RT2860_INT_STATUS 0x0200
+#define RT2860_INT_MASK 0x0204
+#define RT2860_WPDMA_GLO_CFG 0x0208
+#define RT2860_WPDMA_RST_IDX 0x020c
+#define RT2860_DELAY_INT_CFG 0x0210
+#define RT2860_WMM_AIFSN_CFG 0x0214
+#define RT2860_WMM_CWMIN_CFG 0x0218
+#define RT2860_WMM_CWMAX_CFG 0x021c
+#define RT2860_WMM_TXOP0_CFG 0x0220
+#define RT2860_WMM_TXOP1_CFG 0x0224
+#define RT2860_GPIO_CTRL 0x0228
+#define RT2860_MCU_CMD_REG 0x022c
+#define RT2860_TX_BASE_PTR(qid) (0x0230 + (qid) * 16)
+#define RT2860_TX_MAX_CNT(qid) (0x0234 + (qid) * 16)
+#define RT2860_TX_CTX_IDX(qid) (0x0238 + (qid) * 16)
+#define RT2860_TX_DTX_IDX(qid) (0x023c + (qid) * 16)
+#define RT2860_RX_BASE_PTR 0x0290
+#define RT2860_RX_MAX_CNT 0x0294
+#define RT2860_RX_CALC_IDX 0x0298
+#define RT2860_FS_DRX_IDX 0x029c
+#define RT2860_USB_DMA_CFG 0x02a0 /* RT2870 only */
+#define RT2860_US_CYC_CNT 0x02a4
+
+/* PBF registers */
+#define RT2860_SYS_CTRL 0x0400
+#define RT2860_HOST_CMD 0x0404
+#define RT2860_PBF_CFG 0x0408
+#define RT2860_MAX_PCNT 0x040c
+#define RT2860_BUF_CTRL 0x0410
+#define RT2860_MCU_INT_STA 0x0414
+#define RT2860_MCU_INT_ENA 0x0418
+#define RT2860_TXQ_IO(qid) (0x041c + (qid) * 4)
+#define RT2860_RX0Q_IO 0x0424
+#define RT2860_BCN_OFFSET0 0x042c
+#define RT2860_BCN_OFFSET1 0x0430
+#define RT2860_TXRXQ_STA 0x0434
+#define RT2860_TXRXQ_PCNT 0x0438
+#define RT2860_PBF_DBG 0x043c
+#define RT2860_CAP_CTRL 0x0440
+
+/* RT3070 registers */
+#define RT3070_RF_CSR_CFG 0x0500
+#define RT3070_EFUSE_CTRL 0x0580
+#define RT3070_EFUSE_DATA0 0x0590
+#define RT3070_EFUSE_DATA1 0x0594
+#define RT3070_EFUSE_DATA2 0x0598
+#define RT3070_EFUSE_DATA3 0x059c
+#define RT3070_LDO_CFG0 0x05d4
+#define RT3070_GPIO_SWITCH 0x05dc
+
+/* RT5592 registers */
+#define RT5592_DEBUG_INDEX 0x05e8
+
+/* MAC registers */
+#define RT2860_ASIC_VER_ID 0x1000
+#define RT2860_MAC_SYS_CTRL 0x1004
+#define RT2860_MAC_ADDR_DW0 0x1008
+#define RT2860_MAC_ADDR_DW1 0x100c
+#define RT2860_MAC_BSSID_DW0 0x1010
+#define RT2860_MAC_BSSID_DW1 0x1014
+#define RT2860_MAX_LEN_CFG 0x1018
+#define RT2860_BBP_CSR_CFG 0x101c
+#define RT2860_RF_CSR_CFG0 0x1020
+#define RT2860_RF_CSR_CFG1 0x1024
+#define RT2860_RF_CSR_CFG2 0x1028
+#define RT2860_LED_CFG 0x102c
+
+/* undocumented registers */
+#define RT2860_DEBUG 0x10f4
+
+/* MAC Timing control registers */
+#define RT2860_XIFS_TIME_CFG 0x1100
+#define RT2860_BKOFF_SLOT_CFG 0x1104
+#define RT2860_NAV_TIME_CFG 0x1108
+#define RT2860_CH_TIME_CFG 0x110c
+#define RT2860_PBF_LIFE_TIMER 0x1110
+#define RT2860_BCN_TIME_CFG 0x1114
+#define RT2860_TBTT_SYNC_CFG 0x1118
+#define RT2860_TSF_TIMER_DW0 0x111c
+#define RT2860_TSF_TIMER_DW1 0x1120
+#define RT2860_TBTT_TIMER 0x1124
+#define RT2860_INT_TIMER_CFG 0x1128
+#define RT2860_INT_TIMER_EN 0x112c
+#define RT2860_CH_IDLE_TIME 0x1130
+
+/* MAC Power Save configuration registers */
+#define RT2860_MAC_STATUS_REG 0x1200
+#define RT2860_PWR_PIN_CFG 0x1204
+#define RT2860_AUTO_WAKEUP_CFG 0x1208
+
+/* MAC TX configuration registers */
+#define RT2860_EDCA_AC_CFG(aci) (0x1300 + (aci) * 4)
+#define RT2860_EDCA_TID_AC_MAP 0x1310
+#define RT2860_TX_PWR_CFG(ridx) (0x1314 + (ridx) * 4)
+#define RT2860_TX_PIN_CFG 0x1328
+#define RT2860_TX_BAND_CFG 0x132c
+#define RT2860_TX_SW_CFG0 0x1330
+#define RT2860_TX_SW_CFG1 0x1334
+#define RT2860_TX_SW_CFG2 0x1338
+#define RT2860_TXOP_THRES_CFG 0x133c
+#define RT2860_TXOP_CTRL_CFG 0x1340
+#define RT2860_TX_RTS_CFG 0x1344
+#define RT2860_TX_TIMEOUT_CFG 0x1348
+#define RT2860_TX_RTY_CFG 0x134c
+#define RT2860_TX_LINK_CFG 0x1350
+#define RT2860_HT_FBK_CFG0 0x1354
+#define RT2860_HT_FBK_CFG1 0x1358
+#define RT2860_LG_FBK_CFG0 0x135c
+#define RT2860_LG_FBK_CFG1 0x1360
+#define RT2860_CCK_PROT_CFG 0x1364
+#define RT2860_OFDM_PROT_CFG 0x1368
+#define RT2860_MM20_PROT_CFG 0x136c
+#define RT2860_MM40_PROT_CFG 0x1370
+#define RT2860_GF20_PROT_CFG 0x1374
+#define RT2860_GF40_PROT_CFG 0x1378
+#define RT2860_EXP_CTS_TIME 0x137c
+#define RT2860_EXP_ACK_TIME 0x1380
+
+/* MAC RX configuration registers */
+#define RT2860_RX_FILTR_CFG 0x1400
+#define RT2860_AUTO_RSP_CFG 0x1404
+#define RT2860_LEGACY_BASIC_RATE 0x1408
+#define RT2860_HT_BASIC_RATE 0x140c
+#define RT2860_HT_CTRL_CFG 0x1410
+#define RT2860_SIFS_COST_CFG 0x1414
+#define RT2860_RX_PARSER_CFG 0x1418
+
+/* MAC Security configuration registers */
+#define RT2860_TX_SEC_CNT0 0x1500
+#define RT2860_RX_SEC_CNT0 0x1504
+#define RT2860_CCMP_FC_MUTE 0x1508
+
+/* MAC HCCA/PSMP configuration registers */
+#define RT2860_TXOP_HLDR_ADDR0 0x1600
+#define RT2860_TXOP_HLDR_ADDR1 0x1604
+#define RT2860_TXOP_HLDR_ET 0x1608
+#define RT2860_QOS_CFPOLL_RA_DW0 0x160c
+#define RT2860_QOS_CFPOLL_A1_DW1 0x1610
+#define RT2860_QOS_CFPOLL_QC 0x1614
+
+/* MAC Statistics Counters */
+#define RT2860_RX_STA_CNT0 0x1700
+#define RT2860_RX_STA_CNT1 0x1704
+#define RT2860_RX_STA_CNT2 0x1708
+#define RT2860_TX_STA_CNT0 0x170c
+#define RT2860_TX_STA_CNT1 0x1710
+#define RT2860_TX_STA_CNT2 0x1714
+#define RT2860_TX_STAT_FIFO 0x1718
+
+/* RX WCID search table */
+#define RT2860_WCID_ENTRY(wcid) (0x1800 + (wcid) * 8)
+
+#define RT2860_FW_BASE 0x2000
+#define RT2870_FW_BASE 0x3000
+
+/* Pair-wise key table */
+#define RT2860_PKEY(wcid) (0x4000 + (wcid) * 32)
+
+/* IV/EIV table */
+#define RT2860_IVEIV(wcid) (0x6000 + (wcid) * 8)
+
+/* WCID attribute table */
+#define RT2860_WCID_ATTR(wcid) (0x6800 + (wcid) * 4)
+
+/* Shared Key Table */
+#define RT2860_SKEY(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32)
+
+/* Shared Key Mode */
+#define RT2860_SKEY_MODE_0_7 0x7000
+#define RT2860_SKEY_MODE_8_15 0x7004
+#define RT2860_SKEY_MODE_16_23 0x7008
+#define RT2860_SKEY_MODE_24_31 0x700c
+
+/* Shared Memory between MCU and host */
+#define RT2860_H2M_MAILBOX 0x7010
+#define RT2860_H2M_MAILBOX_CID 0x7014
+#define RT2860_H2M_MAILBOX_STATUS 0x701c
+#define RT2860_H2M_INTSRC 0x7024
+#define RT2860_H2M_BBPAGENT 0x7028
+#define RT2860_BCN_BASE(vap) (0x7800 + (vap) * 512)
+
+
+/* possible flags for register RT2860_PCI_EECTRL */
+#define RT2860_C (1 << 0)
+#define RT2860_S (1 << 1)
+#define RT2860_D (1 << 2)
+#define RT2860_SHIFT_D 2
+#define RT2860_Q (1 << 3)
+#define RT2860_SHIFT_Q 3
+
+/* possible flags for registers INT_STATUS/INT_MASK */
+#define RT2860_TX_COHERENT (1 << 17)
+#define RT2860_RX_COHERENT (1 << 16)
+#define RT2860_MAC_INT_4 (1 << 15)
+#define RT2860_MAC_INT_3 (1 << 14)
+#define RT2860_MAC_INT_2 (1 << 13)
+#define RT2860_MAC_INT_1 (1 << 12)
+#define RT2860_MAC_INT_0 (1 << 11)
+#define RT2860_TX_RX_COHERENT (1 << 10)
+#define RT2860_MCU_CMD_INT (1 << 9)
+#define RT2860_TX_DONE_INT5 (1 << 8)
+#define RT2860_TX_DONE_INT4 (1 << 7)
+#define RT2860_TX_DONE_INT3 (1 << 6)
+#define RT2860_TX_DONE_INT2 (1 << 5)
+#define RT2860_TX_DONE_INT1 (1 << 4)
+#define RT2860_TX_DONE_INT0 (1 << 3)
+#define RT2860_RX_DONE_INT (1 << 2)
+#define RT2860_TX_DLY_INT (1 << 1)
+#define RT2860_RX_DLY_INT (1 << 0)
+
+/* possible flags for register WPDMA_GLO_CFG */
+#define RT2860_HDR_SEG_LEN_SHIFT 8
+#define RT2860_BIG_ENDIAN (1 << 7)
+#define RT2860_TX_WB_DDONE (1 << 6)
+#define RT2860_WPDMA_BT_SIZE_SHIFT 4
+#define RT2860_WPDMA_BT_SIZE16 0
+#define RT2860_WPDMA_BT_SIZE32 1
+#define RT2860_WPDMA_BT_SIZE64 2
+#define RT2860_WPDMA_BT_SIZE128 3
+#define RT2860_RX_DMA_BUSY (1 << 3)
+#define RT2860_RX_DMA_EN (1 << 2)
+#define RT2860_TX_DMA_BUSY (1 << 1)
+#define RT2860_TX_DMA_EN (1 << 0)
+
+/* possible flags for register DELAY_INT_CFG */
+#define RT2860_TXDLY_INT_EN (1U << 31)
+#define RT2860_TXMAX_PINT_SHIFT 24
+#define RT2860_TXMAX_PTIME_SHIFT 16
+#define RT2860_RXDLY_INT_EN (1 << 15)
+#define RT2860_RXMAX_PINT_SHIFT 8
+#define RT2860_RXMAX_PTIME_SHIFT 0
+
+/* possible flags for register GPIO_CTRL */
+#define RT2860_GPIO_D_SHIFT 8
+#define RT2860_GPIO_O_SHIFT 0
+
+/* possible flags for register USB_DMA_CFG */
+#define RT2860_USB_TX_BUSY (1U << 31)
+#define RT2860_USB_RX_BUSY (1 << 30)
+#define RT2860_USB_EPOUT_VLD_SHIFT 24
+#define RT2860_USB_TX_EN (1 << 23)
+#define RT2860_USB_RX_EN (1 << 22)
+#define RT2860_USB_RX_AGG_EN (1 << 21)
+#define RT2860_USB_TXOP_HALT (1 << 20)
+#define RT2860_USB_TX_CLEAR (1 << 19)
+#define RT2860_USB_PHY_WD_EN (1 << 16)
+#define RT2860_USB_RX_AGG_LMT(x) ((x) << 8) /* in unit of 1KB */
+#define RT2860_USB_RX_AGG_TO(x) ((x) & 0xff) /* in unit of 33ns */
+
+/* possible flags for register US_CYC_CNT */
+#define RT2860_TEST_EN (1 << 24)
+#define RT2860_TEST_SEL_SHIFT 16
+#define RT2860_BT_MODE_EN (1 << 8)
+#define RT2860_US_CYC_CNT_SHIFT 0
+
+/* possible flags for register SYS_CTRL */
+#define RT2860_HST_PM_SEL (1 << 16)
+#define RT2860_CAP_MODE (1 << 14)
+#define RT2860_PME_OEN (1 << 13)
+#define RT2860_CLKSELECT (1 << 12)
+#define RT2860_PBF_CLK_EN (1 << 11)
+#define RT2860_MAC_CLK_EN (1 << 10)
+#define RT2860_DMA_CLK_EN (1 << 9)
+#define RT2860_MCU_READY (1 << 7)
+#define RT2860_ASY_RESET (1 << 4)
+#define RT2860_PBF_RESET (1 << 3)
+#define RT2860_MAC_RESET (1 << 2)
+#define RT2860_DMA_RESET (1 << 1)
+#define RT2860_MCU_RESET (1 << 0)
+
+/* possible values for register HOST_CMD */
+#define RT2860_MCU_CMD_SLEEP 0x30
+#define RT2860_MCU_CMD_WAKEUP 0x31
+#define RT2860_MCU_CMD_LEDS 0x50
+#define RT2860_MCU_CMD_LED_RSSI 0x51
+#define RT2860_MCU_CMD_LED1 0x52
+#define RT2860_MCU_CMD_LED2 0x53
+#define RT2860_MCU_CMD_LED3 0x54
+#define RT2860_MCU_CMD_RFRESET 0x72
+#define RT2860_MCU_CMD_ANTSEL 0x73
+#define RT2860_MCU_CMD_BBP 0x80
+#define RT2860_MCU_CMD_PSLEVEL 0x83
+
+/* possible flags for register PBF_CFG */
+#define RT2860_TX1Q_NUM_SHIFT 21
+#define RT2860_TX2Q_NUM_SHIFT 16
+#define RT2860_NULL0_MODE (1 << 15)
+#define RT2860_NULL1_MODE (1 << 14)
+#define RT2860_RX_DROP_MODE (1 << 13)
+#define RT2860_TX0Q_MANUAL (1 << 12)
+#define RT2860_TX1Q_MANUAL (1 << 11)
+#define RT2860_TX2Q_MANUAL (1 << 10)
+#define RT2860_RX0Q_MANUAL (1 << 9)
+#define RT2860_HCCA_EN (1 << 8)
+#define RT2860_TX0Q_EN (1 << 4)
+#define RT2860_TX1Q_EN (1 << 3)
+#define RT2860_TX2Q_EN (1 << 2)
+#define RT2860_RX0Q_EN (1 << 1)
+
+/* possible flags for register BUF_CTRL */
+#define RT2860_WRITE_TXQ(qid) (1 << (11 - (qid)))
+#define RT2860_NULL0_KICK (1 << 7)
+#define RT2860_NULL1_KICK (1 << 6)
+#define RT2860_BUF_RESET (1 << 5)
+#define RT2860_READ_TXQ(qid) (1 << (3 - (qid))
+#define RT2860_READ_RX0Q (1 << 0)
+
+/* possible flags for registers MCU_INT_STA/MCU_INT_ENA */
+#define RT2860_MCU_MAC_INT_8 (1 << 24)
+#define RT2860_MCU_MAC_INT_7 (1 << 23)
+#define RT2860_MCU_MAC_INT_6 (1 << 22)
+#define RT2860_MCU_MAC_INT_4 (1 << 20)
+#define RT2860_MCU_MAC_INT_3 (1 << 19)
+#define RT2860_MCU_MAC_INT_2 (1 << 18)
+#define RT2860_MCU_MAC_INT_1 (1 << 17)
+#define RT2860_MCU_MAC_INT_0 (1 << 16)
+#define RT2860_DTX0_INT (1 << 11)
+#define RT2860_DTX1_INT (1 << 10)
+#define RT2860_DTX2_INT (1 << 9)
+#define RT2860_DRX0_INT (1 << 8)
+#define RT2860_HCMD_INT (1 << 7)
+#define RT2860_N0TX_INT (1 << 6)
+#define RT2860_N1TX_INT (1 << 5)
+#define RT2860_BCNTX_INT (1 << 4)
+#define RT2860_MTX0_INT (1 << 3)
+#define RT2860_MTX1_INT (1 << 2)
+#define RT2860_MTX2_INT (1 << 1)
+#define RT2860_MRX0_INT (1 << 0)
+
+/* possible flags for register TXRXQ_PCNT */
+#define RT2860_RX0Q_PCNT_MASK 0xff000000
+#define RT2860_TX2Q_PCNT_MASK 0x00ff0000
+#define RT2860_TX1Q_PCNT_MASK 0x0000ff00
+#define RT2860_TX0Q_PCNT_MASK 0x000000ff
+
+/* possible flags for register CAP_CTRL */
+#define RT2860_CAP_ADC_FEQ (1U << 31)
+#define RT2860_CAP_START (1 << 30)
+#define RT2860_MAN_TRIG (1 << 29)
+#define RT2860_TRIG_OFFSET_SHIFT 16
+#define RT2860_START_ADDR_SHIFT 0
+
+/* possible flags for register RF_CSR_CFG */
+#define RT3070_RF_KICK (1 << 17)
+#define RT3070_RF_WRITE (1 << 16)
+
+/* possible flags for register EFUSE_CTRL */
+#define RT3070_SEL_EFUSE (1U << 31)
+#define RT3070_EFSROM_KICK (1 << 30)
+#define RT3070_EFSROM_AIN_MASK 0x03ff0000
+#define RT3070_EFSROM_AIN_SHIFT 16
+#define RT3070_EFSROM_MODE_MASK 0x000000c0
+#define RT3070_EFUSE_AOUT_MASK 0x0000003f
+
+/* possible flag for register DEBUG_INDEX */
+#define RT5592_SEL_XTAL (1U << 31)
+
+/* possible flags for register MAC_SYS_CTRL */
+#define RT2860_RX_TS_EN (1 << 7)
+#define RT2860_WLAN_HALT_EN (1 << 6)
+#define RT2860_PBF_LOOP_EN (1 << 5)
+#define RT2860_CONT_TX_TEST (1 << 4)
+#define RT2860_MAC_RX_EN (1 << 3)
+#define RT2860_MAC_TX_EN (1 << 2)
+#define RT2860_BBP_HRST (1 << 1)
+#define RT2860_MAC_SRST (1 << 0)
+
+/* possible flags for register MAC_BSSID_DW1 */
+#define RT2860_MULTI_BCN_NUM_SHIFT 18
+#define RT2860_MULTI_BSSID_MODE_SHIFT 16
+
+/* possible flags for register MAX_LEN_CFG */
+#define RT2860_MIN_MPDU_LEN_SHIFT 16
+#define RT2860_MAX_PSDU_LEN_SHIFT 12
+#define RT2860_MAX_PSDU_LEN8K 0
+#define RT2860_MAX_PSDU_LEN16K 1
+#define RT2860_MAX_PSDU_LEN32K 2
+#define RT2860_MAX_PSDU_LEN64K 3
+#define RT2860_MAX_MPDU_LEN_SHIFT 0
+
+/* possible flags for registers BBP_CSR_CFG/H2M_BBPAGENT */
+#define RT2860_BBP_RW_PARALLEL (1 << 19)
+#define RT2860_BBP_PAR_DUR_112_5 (1 << 18)
+#define RT2860_BBP_CSR_KICK (1 << 17)
+#define RT2860_BBP_CSR_READ (1 << 16)
+#define RT2860_BBP_ADDR_SHIFT 8
+#define RT2860_BBP_DATA_SHIFT 0
+
+/* possible flags for register RF_CSR_CFG0 */
+#define RT2860_RF_REG_CTRL (1U << 31)
+#define RT2860_RF_LE_SEL1 (1 << 30)
+#define RT2860_RF_LE_STBY (1 << 29)
+#define RT2860_RF_REG_WIDTH_SHIFT 24
+#define RT2860_RF_REG_0_SHIFT 0
+
+/* possible flags for register RF_CSR_CFG1 */
+#define RT2860_RF_DUR_5 (1 << 24)
+#define RT2860_RF_REG_1_SHIFT 0
+
+/* possible flags for register LED_CFG */
+#define RT2860_LED_POL (1 << 30)
+#define RT2860_Y_LED_MODE_SHIFT 28
+#define RT2860_G_LED_MODE_SHIFT 26
+#define RT2860_R_LED_MODE_SHIFT 24
+#define RT2860_LED_MODE_OFF 0
+#define RT2860_LED_MODE_BLINK_TX 1
+#define RT2860_LED_MODE_SLOW_BLINK 2
+#define RT2860_LED_MODE_ON 3
+#define RT2860_SLOW_BLK_TIME_SHIFT 16
+#define RT2860_LED_OFF_TIME_SHIFT 8
+#define RT2860_LED_ON_TIME_SHIFT 0
+
+/* possible flags for register XIFS_TIME_CFG */
+#define RT2860_BB_RXEND_EN (1 << 29)
+#define RT2860_EIFS_TIME_SHIFT 20
+#define RT2860_OFDM_XIFS_TIME_SHIFT 16
+#define RT2860_OFDM_SIFS_TIME_SHIFT 8
+#define RT2860_CCK_SIFS_TIME_SHIFT 0
+
+/* possible flags for register BKOFF_SLOT_CFG */
+#define RT2860_CC_DELAY_TIME_SHIFT 8
+#define RT2860_SLOT_TIME 0
+
+/* possible flags for register NAV_TIME_CFG */
+#define RT2860_NAV_UPD (1U << 31)
+#define RT2860_NAV_UPD_VAL_SHIFT 16
+#define RT2860_NAV_CLR_EN (1 << 15)
+#define RT2860_NAV_TIMER_SHIFT 0
+
+/* possible flags for register CH_TIME_CFG */
+#define RT2860_EIFS_AS_CH_BUSY (1 << 4)
+#define RT2860_NAV_AS_CH_BUSY (1 << 3)
+#define RT2860_RX_AS_CH_BUSY (1 << 2)
+#define RT2860_TX_AS_CH_BUSY (1 << 1)
+#define RT2860_CH_STA_TIMER_EN (1 << 0)
+
+/* possible values for register BCN_TIME_CFG */
+#define RT2860_TSF_INS_COMP_SHIFT 24
+#define RT2860_BCN_TX_EN (1 << 20)
+#define RT2860_TBTT_TIMER_EN (1 << 19)
+#define RT2860_TSF_SYNC_MODE_SHIFT 17
+#define RT2860_TSF_SYNC_MODE_DIS 0
+#define RT2860_TSF_SYNC_MODE_STA 1
+#define RT2860_TSF_SYNC_MODE_IBSS 2
+#define RT2860_TSF_SYNC_MODE_HOSTAP 3
+#define RT2860_TSF_TIMER_EN (1 << 16)
+#define RT2860_BCN_INTVAL_SHIFT 0
+
+/* possible flags for register TBTT_SYNC_CFG */
+#define RT2860_BCN_CWMIN_SHIFT 20
+#define RT2860_BCN_AIFSN_SHIFT 16
+#define RT2860_BCN_EXP_WIN_SHIFT 8
+#define RT2860_TBTT_ADJUST_SHIFT 0
+
+/* possible flags for register INT_TIMER_CFG */
+#define RT2860_GP_TIMER_SHIFT 16
+#define RT2860_PRE_TBTT_TIMER_SHIFT 0
+
+/* possible flags for register INT_TIMER_EN */
+#define RT2860_GP_TIMER_EN (1 << 1)
+#define RT2860_PRE_TBTT_INT_EN (1 << 0)
+
+/* possible flags for register MAC_STATUS_REG */
+#define RT2860_RX_STATUS_BUSY (1 << 1)
+#define RT2860_TX_STATUS_BUSY (1 << 0)
+
+/* possible flags for register PWR_PIN_CFG */
+#define RT2860_IO_ADDA_PD (1 << 3)
+#define RT2860_IO_PLL_PD (1 << 2)
+#define RT2860_IO_RA_PE (1 << 1)
+#define RT2860_IO_RF_PE (1 << 0)
+
+/* possible flags for register AUTO_WAKEUP_CFG */
+#define RT2860_AUTO_WAKEUP_EN (1 << 15)
+#define RT2860_SLEEP_TBTT_NUM_SHIFT 8
+#define RT2860_WAKEUP_LEAD_TIME_SHIFT 0
+
+/* possible flags for register TX_PIN_CFG */
+#define RT2860_TRSW_POL (1 << 19)
+#define RT2860_TRSW_EN (1 << 18)
+#define RT2860_RFTR_POL (1 << 17)
+#define RT2860_RFTR_EN (1 << 16)
+#define RT2860_LNA_PE_G1_POL (1 << 15)
+#define RT2860_LNA_PE_A1_POL (1 << 14)
+#define RT2860_LNA_PE_G0_POL (1 << 13)
+#define RT2860_LNA_PE_A0_POL (1 << 12)
+#define RT2860_LNA_PE_G1_EN (1 << 11)
+#define RT2860_LNA_PE_A1_EN (1 << 10)
+#define RT2860_LNA_PE1_EN (RT2860_LNA_PE_A1_EN | RT2860_LNA_PE_G1_EN)
+#define RT2860_LNA_PE_G0_EN (1 << 9)
+#define RT2860_LNA_PE_A0_EN (1 << 8)
+#define RT2860_LNA_PE0_EN (RT2860_LNA_PE_A0_EN | RT2860_LNA_PE_G0_EN)
+#define RT2860_PA_PE_G1_POL (1 << 7)
+#define RT2860_PA_PE_A1_POL (1 << 6)
+#define RT2860_PA_PE_G0_POL (1 << 5)
+#define RT2860_PA_PE_A0_POL (1 << 4)
+#define RT2860_PA_PE_G1_EN (1 << 3)
+#define RT2860_PA_PE_A1_EN (1 << 2)
+#define RT2860_PA_PE_G0_EN (1 << 1)
+#define RT2860_PA_PE_A0_EN (1 << 0)
+
+/* possible flags for register TX_BAND_CFG */
+#define RT2860_5G_BAND_SEL_N (1 << 2)
+#define RT2860_5G_BAND_SEL_P (1 << 1)
+#define RT2860_TX_BAND_SEL (1 << 0)
+
+/* possible flags for register TX_SW_CFG0 */
+#define RT2860_DLY_RFTR_EN_SHIFT 24
+#define RT2860_DLY_TRSW_EN_SHIFT 16
+#define RT2860_DLY_PAPE_EN_SHIFT 8
+#define RT2860_DLY_TXPE_EN_SHIFT 0
+
+/* possible flags for register TX_SW_CFG1 */
+#define RT2860_DLY_RFTR_DIS_SHIFT 16
+#define RT2860_DLY_TRSW_DIS_SHIFT 8
+#define RT2860_DLY_PAPE_DIS SHIFT 0
+
+/* possible flags for register TX_SW_CFG2 */
+#define RT2860_DLY_LNA_EN_SHIFT 24
+#define RT2860_DLY_LNA_DIS_SHIFT 16
+#define RT2860_DLY_DAC_EN_SHIFT 8
+#define RT2860_DLY_DAC_DIS_SHIFT 0
+
+/* possible flags for register TXOP_THRES_CFG */
+#define RT2860_TXOP_REM_THRES_SHIFT 24
+#define RT2860_CF_END_THRES_SHIFT 16
+#define RT2860_RDG_IN_THRES 8
+#define RT2860_RDG_OUT_THRES 0
+
+/* possible flags for register TXOP_CTRL_CFG */
+#define RT2860_EXT_CW_MIN_SHIFT 16
+#define RT2860_EXT_CCA_DLY_SHIFT 8
+#define RT2860_EXT_CCA_EN (1 << 7)
+#define RT2860_LSIG_TXOP_EN (1 << 6)
+#define RT2860_TXOP_TRUN_EN_MIMOPS (1 << 4)
+#define RT2860_TXOP_TRUN_EN_TXOP (1 << 3)
+#define RT2860_TXOP_TRUN_EN_RATE (1 << 2)
+#define RT2860_TXOP_TRUN_EN_AC (1 << 1)
+#define RT2860_TXOP_TRUN_EN_TIMEOUT (1 << 0)
+
+/* possible flags for register TX_RTS_CFG */
+#define RT2860_RTS_FBK_EN (1 << 24)
+#define RT2860_RTS_THRES_SHIFT 8
+#define RT2860_RTS_RTY_LIMIT_SHIFT 0
+
+/* possible flags for register TX_TIMEOUT_CFG */
+#define RT2860_TXOP_TIMEOUT_SHIFT 16
+#define RT2860_RX_ACK_TIMEOUT_SHIFT 8
+#define RT2860_MPDU_LIFE_TIME_SHIFT 4
+
+/* possible flags for register TX_RTY_CFG */
+#define RT2860_TX_AUTOFB_EN (1 << 30)
+#define RT2860_AGG_RTY_MODE_TIMER (1 << 29)
+#define RT2860_NAG_RTY_MODE_TIMER (1 << 28)
+#define RT2860_LONG_RTY_THRES_SHIFT 16
+#define RT2860_LONG_RTY_LIMIT_SHIFT 8
+#define RT2860_SHORT_RTY_LIMIT_SHIFT 0
+
+/* possible flags for register TX_LINK_CFG */
+#define RT2860_REMOTE_MFS_SHIFT 24
+#define RT2860_REMOTE_MFB_SHIFT 16
+#define RT2860_TX_CFACK_EN (1 << 12)
+#define RT2860_TX_RDG_EN (1 << 11)
+#define RT2860_TX_MRQ_EN (1 << 10)
+#define RT2860_REMOTE_UMFS_EN (1 << 9)
+#define RT2860_TX_MFB_EN (1 << 8)
+#define RT2860_REMOTE_MFB_LT_SHIFT 0
+
+/* possible flags for registers *_PROT_CFG */
+#define RT2860_RTSTH_EN (1 << 26)
+#define RT2860_TXOP_ALLOW_GF40 (1 << 25)
+#define RT2860_TXOP_ALLOW_GF20 (1 << 24)
+#define RT2860_TXOP_ALLOW_MM40 (1 << 23)
+#define RT2860_TXOP_ALLOW_MM20 (1 << 22)
+#define RT2860_TXOP_ALLOW_OFDM (1 << 21)
+#define RT2860_TXOP_ALLOW_CCK (1 << 20)
+#define RT2860_TXOP_ALLOW_ALL (0x3f << 20)
+#define RT2860_PROT_NAV_SHORT (1 << 18)
+#define RT2860_PROT_NAV_LONG (2 << 18)
+#define RT2860_PROT_CTRL_RTS_CTS (1 << 16)
+#define RT2860_PROT_CTRL_CTS (2 << 16)
+
+/* possible flags for registers EXP_{CTS,ACK}_TIME */
+#define RT2860_EXP_OFDM_TIME_SHIFT 16
+#define RT2860_EXP_CCK_TIME_SHIFT 0
+
+/* possible flags for register RX_FILTR_CFG */
+#define RT2860_DROP_CTRL_RSV (1 << 16)
+#define RT2860_DROP_BAR (1 << 15)
+#define RT2860_DROP_BA (1 << 14)
+#define RT2860_DROP_PSPOLL (1 << 13)
+#define RT2860_DROP_RTS (1 << 12)
+#define RT2860_DROP_CTS (1 << 11)
+#define RT2860_DROP_ACK (1 << 10)
+#define RT2860_DROP_CFEND (1 << 9)
+#define RT2860_DROP_CFACK (1 << 8)
+#define RT2860_DROP_DUPL (1 << 7)
+#define RT2860_DROP_BC (1 << 6)
+#define RT2860_DROP_MC (1 << 5)
+#define RT2860_DROP_VER_ERR (1 << 4)
+#define RT2860_DROP_NOT_MYBSS (1 << 3)
+#define RT2860_DROP_UC_NOME (1 << 2)
+#define RT2860_DROP_PHY_ERR (1 << 1)
+#define RT2860_DROP_CRC_ERR (1 << 0)
+
+/* possible flags for register AUTO_RSP_CFG */
+#define RT2860_CTRL_PWR_BIT (1 << 7)
+#define RT2860_BAC_ACK_POLICY (1 << 6)
+#define RT2860_CCK_SHORT_EN (1 << 4)
+#define RT2860_CTS_40M_REF_EN (1 << 3)
+#define RT2860_CTS_40M_MODE_EN (1 << 2)
+#define RT2860_BAC_ACKPOLICY_EN (1 << 1)
+#define RT2860_AUTO_RSP_EN (1 << 0)
+
+/* possible flags for register SIFS_COST_CFG */
+#define RT2860_OFDM_SIFS_COST_SHIFT 8
+#define RT2860_CCK_SIFS_COST_SHIFT 0
+
+/* possible flags for register TXOP_HLDR_ET */
+#define RT2860_TXOP_ETM1_EN (1 << 25)
+#define RT2860_TXOP_ETM0_EN (1 << 24)
+#define RT2860_TXOP_ETM_THRES_SHIFT 16
+#define RT2860_TXOP_ETO_EN (1 << 8)
+#define RT2860_TXOP_ETO_THRES_SHIFT 1
+#define RT2860_PER_RX_RST_EN (1 << 0)
+
+/* possible flags for register TX_STAT_FIFO */
+#define RT2860_TXQ_MCS_SHIFT 16
+#define RT2860_TXQ_WCID_SHIFT 8
+#define RT2860_TXQ_ACKREQ (1 << 7)
+#define RT2860_TXQ_AGG (1 << 6)
+#define RT2860_TXQ_OK (1 << 5)
+#define RT2860_TXQ_PID_SHIFT 1
+#define RT2860_TXQ_VLD (1 << 0)
+
+/* possible flags for register WCID_ATTR */
+#define RT2860_MODE_NOSEC 0
+#define RT2860_MODE_WEP40 1
+#define RT2860_MODE_WEP104 2
+#define RT2860_MODE_TKIP 3
+#define RT2860_MODE_AES_CCMP 4
+#define RT2860_MODE_CKIP40 5
+#define RT2860_MODE_CKIP104 6
+#define RT2860_MODE_CKIP128 7
+#define RT2860_RX_PKEY_EN (1 << 0)
+
+/* possible flags for register H2M_MAILBOX */
+#define RT2860_H2M_BUSY (1 << 24)
+#define RT2860_TOKEN_NO_INTR 0xff
+
+/* possible flags for MCU command RT2860_MCU_CMD_LEDS */
+#define RT2860_LED_RADIO (1 << 13)
+#define RT2860_LED_LINK_2GHZ (1 << 14)
+#define RT2860_LED_LINK_5GHZ (1 << 15)
+
+/* possible flags for RT3020 RF register 1 */
+#define RT3070_RF_BLOCK (1 << 0)
+#define RT3070_PLL_PD (1 << 1)
+#define RT3070_RX0_PD (1 << 2)
+#define RT3070_TX0_PD (1 << 3)
+#define RT3070_RX1_PD (1 << 4)
+#define RT3070_TX1_PD (1 << 5)
+#define RT3070_RX2_PD (1 << 6)
+#define RT3070_TX2_PD (1 << 7)
+
+/* possible flags for RT3020 RF register 15 */
+#define RT3070_TX_LO2 (1 << 3)
+
+/* possible flags for RT3020 RF register 17 */
+#define RT3070_TX_LO1 (1 << 3)
+
+/* possible flags for RT3020 RF register 20 */
+#define RT3070_RX_LO1 (1 << 3)
+
+/* possible flags for RT3020 RF register 21 */
+#define RT3070_RX_LO2 (1 << 3)
+
+/* possible flags for RT3053 RF register 18 */
+#define RT3593_AUTOTUNE_BYPASS (1 << 6)
+
+/* possible flags for RT3053 RF register 50 */
+#define RT3593_TX_LO2 (1 << 4)
+
+/* possible flags for RT3053 RF register 51 */
+#define RT3593_TX_LO1 (1 << 4)
+
+/* Possible flags for RT5390 RF register 2. */
+#define RT5390_RESCAL (1 << 7)
+
+/* Possible flags for RT5390 RF register 3. */
+#define RT5390_VCOCAL (1 << 7)
+
+/* Possible flags for RT5390 RF register 38. */
+#define RT5390_RX_LO1 (1 << 5)
+
+/* Possible flags for RT5390 RF register 39. */
+#define RT5390_RX_LO2 (1 << 7)
+
+/* Possible flags for RT5390 BBP register 4. */
+#define RT5390_MAC_IF_CTRL (1 << 6)
+
+/* Possible flags for RT5390 BBP register 105. */
+#define RT5390_MLD (1 << 2)
+#define RT5390_EN_SIG_MODULATION (1 << 3)
+
+/* RT2860 TX descriptor */
+struct rt2860_txd {
+ uint32_t sdp0; /* Segment Data Pointer 0 */
+ uint16_t sdl1; /* Segment Data Length 1 */
+#define RT2860_TX_BURST (1 << 15)
+#define RT2860_TX_LS1 (1 << 14) /* SDP1 is the last segment */
+
+ uint16_t sdl0; /* Segment Data Length 0 */
+#define RT2860_TX_DDONE (1 << 15)
+#define RT2860_TX_LS0 (1 << 14) /* SDP0 is the last segment */
+
+ uint32_t sdp1; /* Segment Data Pointer 1 */
+ uint8_t reserved[3];
+ uint8_t flags;
+#define RT2860_TX_QSEL_SHIFT 1
+#define RT2860_TX_QSEL_MGMT (0 << 1)
+#define RT2860_TX_QSEL_HCCA (1 << 1)
+#define RT2860_TX_QSEL_EDCA (2 << 1)
+#define RT2860_TX_WIV (1 << 0)
+} __packed;
+
+/* RT2870 TX descriptor */
+struct rt2870_txd {
+ uint16_t len;
+ uint8_t pad;
+ uint8_t flags;
+} __packed;
+
+/* TX Wireless Information */
+struct rt2860_txwi {
+ uint8_t flags;
+#define RT2860_TX_MPDU_DSITY_SHIFT 5
+#define RT2860_TX_AMPDU (1 << 4)
+#define RT2860_TX_TS (1 << 3)
+#define RT2860_TX_CFACK (1 << 2)
+#define RT2860_TX_MMPS (1 << 1)
+#define RT2860_TX_FRAG (1 << 0)
+
+ uint8_t txop;
+#define RT2860_TX_TXOP_HT 0
+#define RT2860_TX_TXOP_PIFS 1
+#define RT2860_TX_TXOP_SIFS 2
+#define RT2860_TX_TXOP_BACKOFF 3
+
+ uint16_t phy;
+#define RT2860_PHY_MODE 0xc000
+#define RT2860_PHY_CCK (0 << 14)
+#define RT2860_PHY_OFDM (1 << 14)
+#define RT2860_PHY_HT (2 << 14)
+#define RT2860_PHY_HT_GF (3 << 14)
+#define RT2860_PHY_SGI (1 << 8)
+#define RT2860_PHY_BW40 (1 << 7)
+#define RT2860_PHY_MCS 0x7f
+#define RT2860_PHY_SHPRE (1 << 3)
+
+ uint8_t xflags;
+#define RT2860_TX_BAWINSIZE_SHIFT 2
+#define RT2860_TX_NSEQ (1 << 1)
+#define RT2860_TX_ACK (1 << 0)
+
+ uint8_t wcid; /* Wireless Client ID */
+ uint16_t len;
+#define RT2860_TX_PID_SHIFT 12
+
+ uint32_t iv;
+ uint32_t eiv;
+} __packed;
+
+/* RT2860 RX descriptor */
+struct rt2860_rxd {
+ uint32_t sdp0;
+ uint16_t sdl1; /* unused */
+ uint16_t sdl0;
+#define RT2860_RX_DDONE (1 << 15)
+#define RT2860_RX_LS0 (1 << 14)
+
+ uint32_t sdp1; /* unused */
+ uint32_t flags;
+#define RT2860_RX_DEC (1 << 16)
+#define RT2860_RX_AMPDU (1 << 15)
+#define RT2860_RX_L2PAD (1 << 14)
+#define RT2860_RX_RSSI (1 << 13)
+#define RT2860_RX_HTC (1 << 12)
+#define RT2860_RX_AMSDU (1 << 11)
+#define RT2860_RX_MICERR (1 << 10)
+#define RT2860_RX_ICVERR (1 << 9)
+#define RT2860_RX_CRCERR (1 << 8)
+#define RT2860_RX_MYBSS (1 << 7)
+#define RT2860_RX_BC (1 << 6)
+#define RT2860_RX_MC (1 << 5)
+#define RT2860_RX_UC2ME (1 << 4)
+#define RT2860_RX_FRAG (1 << 3)
+#define RT2860_RX_NULL (1 << 2)
+#define RT2860_RX_DATA (1 << 1)
+#define RT2860_RX_BA (1 << 0)
+} __packed;
+
+/* RT2870 RX descriptor */
+struct rt2870_rxd {
+ /* single 32-bit field */
+ uint32_t flags;
+} __packed;
+
+/* RX Wireless Information */
+struct rt2860_rxwi {
+ uint8_t wcid;
+ uint8_t keyidx;
+#define RT2860_RX_UDF_SHIFT 5
+#define RT2860_RX_BSS_IDX_SHIFT 2
+
+ uint16_t len;
+#define RT2860_RX_TID_SHIFT 12
+
+ uint16_t seq;
+ uint16_t phy;
+ uint8_t rssi[3];
+ uint8_t reserved1;
+ uint8_t snr[2];
+ uint16_t reserved2;
+} __packed;
+
+#define RT2860_RF_2820 0x0001 /* 2T3R */
+#define RT2860_RF_2850 0x0002 /* dual-band 2T3R */
+#define RT2860_RF_2720 0x0003 /* 1T2R */
+#define RT2860_RF_2750 0x0004 /* dual-band 1T2R */
+#define RT3070_RF_3020 0x0005 /* 1T1R */
+#define RT3070_RF_2020 0x0006 /* b/g */
+#define RT3070_RF_3021 0x0007 /* 1T2R */
+#define RT3070_RF_3022 0x0008 /* 2T2R */
+#define RT3070_RF_3052 0x0009 /* dual-band 2T2R */
+#define RT3593_RF_3053 0x000d /* dual-band 3T3R */
+#define RT5592_RF_5592 0x000f /* dual-band 2T2R */
+#define RT5390_RF_5370 0x5370 /* 1T1R */
+#define RT5390_RF_5372 0x5372 /* 2T2R */
+
+/* USB commands for RT2870 only */
+#define RT2870_RESET 1
+#define RT2870_WRITE_2 2
+#define RT2870_WRITE_REGION_1 6
+#define RT2870_READ_REGION_1 7
+#define RT2870_EEPROM_READ 9
+
+#define RT2860_EEPROM_DELAY 1 /* minimum hold time (microsecond) */
+
+#define RT2860_EEPROM_VERSION 0x01
+#define RT2860_EEPROM_MAC01 0x02
+#define RT2860_EEPROM_MAC23 0x03
+#define RT2860_EEPROM_MAC45 0x04
+#define RT2860_EEPROM_PCIE_PSLEVEL 0x11
+#define RT2860_EEPROM_REV 0x12
+#define RT2860_EEPROM_ANTENNA 0x1a
+#define RT2860_EEPROM_CONFIG 0x1b
+#define RT2860_EEPROM_COUNTRY 0x1c
+#define RT2860_EEPROM_FREQ_LEDS 0x1d
+#define RT2860_EEPROM_LED1 0x1e
+#define RT2860_EEPROM_LED2 0x1f
+#define RT2860_EEPROM_LED3 0x20
+#define RT2860_EEPROM_LNA 0x22
+#define RT2860_EEPROM_RSSI1_2GHZ 0x23
+#define RT2860_EEPROM_RSSI2_2GHZ 0x24
+#define RT2860_EEPROM_RSSI1_5GHZ 0x25
+#define RT2860_EEPROM_RSSI2_5GHZ 0x26
+#define RT2860_EEPROM_DELTAPWR 0x28
+#define RT2860_EEPROM_PWR2GHZ_BASE1 0x29
+#define RT2860_EEPROM_PWR2GHZ_BASE2 0x30
+#define RT2860_EEPROM_TSSI1_2GHZ 0x37
+#define RT2860_EEPROM_TSSI2_2GHZ 0x38
+#define RT2860_EEPROM_TSSI3_2GHZ 0x39
+#define RT2860_EEPROM_TSSI4_2GHZ 0x3a
+#define RT2860_EEPROM_TSSI5_2GHZ 0x3b
+#define RT2860_EEPROM_PWR5GHZ_BASE1 0x3c
+#define RT2860_EEPROM_PWR5GHZ_BASE2 0x53
+#define RT2860_EEPROM_TSSI1_5GHZ 0x6a
+#define RT2860_EEPROM_TSSI2_5GHZ 0x6b
+#define RT2860_EEPROM_TSSI3_5GHZ 0x6c
+#define RT2860_EEPROM_TSSI4_5GHZ 0x6d
+#define RT2860_EEPROM_TSSI5_5GHZ 0x6e
+#define RT2860_EEPROM_RPWR 0x6f
+#define RT2860_EEPROM_BBP_BASE 0x78
+#define RT3071_EEPROM_RF_BASE 0x82
+
+/* EEPROM registers for RT3593. */
+#define RT3593_EEPROM_FREQ_LEDS 0x21
+#define RT3593_EEPROM_FREQ 0x22
+#define RT3593_EEPROM_LED1 0x22
+#define RT3593_EEPROM_LED2 0x23
+#define RT3593_EEPROM_LED3 0x24
+#define RT3593_EEPROM_LNA 0x26
+#define RT3593_EEPROM_LNA_5GHZ 0x27
+#define RT3593_EEPROM_RSSI1_2GHZ 0x28
+#define RT3593_EEPROM_RSSI2_2GHZ 0x29
+#define RT3593_EEPROM_RSSI1_5GHZ 0x2a
+#define RT3593_EEPROM_RSSI2_5GHZ 0x2b
+#define RT3593_EEPROM_PWR2GHZ_BASE1 0x30
+#define RT3593_EEPROM_PWR2GHZ_BASE2 0x37
+#define RT3593_EEPROM_PWR2GHZ_BASE3 0x3e
+#define RT3593_EEPROM_PWR5GHZ_BASE1 0x4b
+#define RT3593_EEPROM_PWR5GHZ_BASE2 0x65
+#define RT3593_EEPROM_PWR5GHZ_BASE3 0x7f
+
+/*
+ * EEPROM IQ calibration.
+ */
+#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ 0x130
+#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ 0x131
+#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ 0x133
+#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ 0x134
+#define RT5390_EEPROM_RF_IQ_COMPENSATION_CTL 0x13c
+#define RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL 0x13d
+#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ 0x144
+#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ 0x145
+#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ 0x146
+#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ 0x147
+#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ 0x148
+#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ 0x149
+#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ 0x14a
+#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ 0x14b
+#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ 0x14c
+#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ 0x14d
+#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ 0x14e
+#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ 0x14f
+
+#define RT2860_RIDX_CCK1 0
+#define RT2860_RIDX_CCK11 3
+#define RT2860_RIDX_OFDM6 4
+#define RT2860_RIDX_MAX 12
+
+/*
+ * EEPROM access macro.
+ */
+#define RT2860_EEPROM_CTL(sc, val) do { \
+ RAL_WRITE((sc), RT2860_PCI_EECTRL, (val)); \
+ RAL_BARRIER_READ_WRITE((sc)); \
+ DELAY(RT2860_EEPROM_DELAY); \
+} while (/* CONSTCOND */0)
+
+/*
+ * Default values for MAC registers; values taken from the reference driver.
+ */
+#define RT2870_DEF_MAC \
+ { RT2860_BCN_OFFSET0, 0xf8f0e8e0 }, \
+ { RT2860_BCN_OFFSET1, 0x6f77d0c8 }, \
+ { RT2860_LEGACY_BASIC_RATE, 0x0000013f }, \
+ { RT2860_HT_BASIC_RATE, 0x00008003 }, \
+ { RT2860_MAC_SYS_CTRL, 0x00000000 }, \
+ { RT2860_BKOFF_SLOT_CFG, 0x00000209 }, \
+ { RT2860_TX_SW_CFG0, 0x00000000 }, \
+ { RT2860_TX_SW_CFG1, 0x00080606 }, \
+ { RT2860_TX_LINK_CFG, 0x00001020 }, \
+ { RT2860_TX_TIMEOUT_CFG, 0x000a2090 }, \
+ { RT2860_MAX_LEN_CFG, 0x00001f00 }, \
+ { RT2860_LED_CFG, 0x7f031e46 }, \
+ { RT2860_WMM_AIFSN_CFG, 0x00002273 }, \
+ { RT2860_WMM_CWMIN_CFG, 0x00002344 }, \
+ { RT2860_WMM_CWMAX_CFG, 0x000034aa }, \
+ { RT2860_MAX_PCNT, 0x1f3fbf9f }, \
+ { RT2860_TX_RTY_CFG, 0x47d01f0f }, \
+ { RT2860_AUTO_RSP_CFG, 0x00000013 }, \
+ { RT2860_CCK_PROT_CFG, 0x05740003 }, \
+ { RT2860_OFDM_PROT_CFG, 0x05740003 }, \
+ { RT2860_PBF_CFG, 0x00f40006 }, \
+ { RT2860_WPDMA_GLO_CFG, 0x00000030 }, \
+ { RT2860_GF20_PROT_CFG, 0x01744004 }, \
+ { RT2860_GF40_PROT_CFG, 0x03f44084 }, \
+ { RT2860_MM20_PROT_CFG, 0x01744004 }, \
+ { RT2860_MM40_PROT_CFG, 0x03f44084 }, \
+ { RT2860_TXOP_CTRL_CFG, 0x0000583f }, \
+ { RT2860_TXOP_HLDR_ET, 0x00000002 }, \
+ { RT2860_TX_RTS_CFG, 0x00092b20 }, \
+ { RT2860_EXP_ACK_TIME, 0x002400ca }, \
+ { RT2860_XIFS_TIME_CFG, 0x33a41010 }, \
+ { RT2860_PWR_PIN_CFG, 0x00000003 }
+
+/*
+ * Default values for BBP registers; values taken from the reference driver.
+ */
+#define RT2860_DEF_BBP \
+ { 65, 0x2c }, \
+ { 66, 0x38 }, \
+ { 68, 0x0b }, \
+ { 69, 0x12 }, \
+ { 70, 0x0a }, \
+ { 73, 0x10 }, \
+ { 81, 0x37 }, \
+ { 82, 0x62 }, \
+ { 83, 0x6a }, \
+ { 84, 0x99 }, \
+ { 86, 0x00 }, \
+ { 91, 0x04 }, \
+ { 92, 0x00 }, \
+ { 103, 0x00 }, \
+ { 105, 0x05 }, \
+ { 106, 0x35 }
+
+#define RT5390_DEF_BBP \
+ { 31, 0x08 }, \
+ { 65, 0x2c }, \
+ { 66, 0x38 }, \
+ { 68, 0x0b }, \
+ { 69, 0x0d }, \
+ { 70, 0x06 }, \
+ { 73, 0x13 }, \
+ { 75, 0x46 }, \
+ { 76, 0x28 }, \
+ { 77, 0x59 }, \
+ { 81, 0x37 }, \
+ { 82, 0x62 }, \
+ { 83, 0x7a }, \
+ { 84, 0x9a }, \
+ { 86, 0x38 }, \
+ { 91, 0x04 }, \
+ { 92, 0x02 }, \
+ { 103, 0xc0 }, \
+ { 104, 0x92 }, \
+ { 105, 0x3c }, \
+ { 106, 0x03 }, \
+ { 128, 0x12 }
+
+#define RT5592_DEF_BBP \
+ { 20, 0x06 }, \
+ { 31, 0x08 }, \
+ { 65, 0x2c }, \
+ { 66, 0x38 }, \
+ { 68, 0xdd }, \
+ { 69, 0x1a }, \
+ { 70, 0x05 }, \
+ { 73, 0x13 }, \
+ { 74, 0x0f }, \
+ { 75, 0x4f }, \
+ { 76, 0x28 }, \
+ { 77, 0x59 }, \
+ { 81, 0x37 }, \
+ { 82, 0x62 }, \
+ { 83, 0x6a }, \
+ { 84, 0x9a }, \
+ { 86, 0x38 }, \
+ { 88, 0x90 }, \
+ { 91, 0x04 }, \
+ { 92, 0x02 }, \
+ { 95, 0x9a }, \
+ { 98, 0x12 }, \
+ { 103, 0xc0 }, \
+ { 104, 0x92 }, \
+ { 105, 0x3c }, \
+ { 106, 0x35 }, \
+ { 128, 0x12 }, \
+ { 134, 0xd0 }, \
+ { 135, 0xf6 }, \
+ { 137, 0x0f }
+
+/*
+ * Channel map for run(4) driver; taken from the table below.
+ */
+static const uint8_t run_chan_2ghz[] =
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+
+static const uint8_t run_chan_5ghz[] =
+ { 36, 38, 40, 44, 46, 48, 52, 54, 56, 60, 62, 64, 100, 102, 104,
+ 108, 110, 112, 116, 118, 120, 124, 126, 128, 132, 134, 136, 140,
+ 149, 151, 153, 157, 159, 161, 165, 167, 169, 171, 173,
+ 184, 188, 192, 196, 208, 212, 216 };
+
+/*
+ * Default settings for RF registers; values derived from the reference driver.
+ */
+#define RT2860_RF2850 \
+ { 1, 0x98402ecc, 0x984c0786, 0x9816b455, 0x9800510b }, \
+ { 2, 0x98402ecc, 0x984c0786, 0x98168a55, 0x9800519f }, \
+ { 3, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800518b }, \
+ { 4, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800519f }, \
+ { 5, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800518b }, \
+ { 6, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800519f }, \
+ { 7, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800518b }, \
+ { 8, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800519f }, \
+ { 9, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800518b }, \
+ { 10, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800519f }, \
+ { 11, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800518b }, \
+ { 12, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800519f }, \
+ { 13, 0x98402ecc, 0x984c079e, 0x98168a55, 0x9800518b }, \
+ { 14, 0x98402ecc, 0x984c07a2, 0x98168a55, 0x98005193 }, \
+ { 36, 0x98402ecc, 0x984c099a, 0x98158a55, 0x980ed1a3 }, \
+ { 38, 0x98402ecc, 0x984c099e, 0x98158a55, 0x980ed193 }, \
+ { 40, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed183 }, \
+ { 44, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed1a3 }, \
+ { 46, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed18b }, \
+ { 48, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed19b }, \
+ { 52, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed193 }, \
+ { 54, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed1a3 }, \
+ { 56, 0x98402ec8, 0x984c068e, 0x98158a55, 0x980ed18b }, \
+ { 60, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed183 }, \
+ { 62, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed193 }, \
+ { 64, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed1a3 }, \
+ { 100, 0x98402ec8, 0x984c06b2, 0x98178a55, 0x980ed783 }, \
+ { 102, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed793 }, \
+ { 104, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed1a3 }, \
+ { 108, 0x98402ecc, 0x985c0a32, 0x98578a55, 0x980ed193 }, \
+ { 110, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed183 }, \
+ { 112, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed19b }, \
+ { 116, 0x98402ecc, 0x984c0a3a, 0x98178a55, 0x980ed1a3 }, \
+ { 118, 0x98402ecc, 0x984c0a3e, 0x98178a55, 0x980ed193 }, \
+ { 120, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed183 }, \
+ { 124, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed193 }, \
+ { 126, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed15b }, \
+ { 128, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed1a3 }, \
+ { 132, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed18b }, \
+ { 134, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed193 }, \
+ { 136, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed19b }, \
+ { 140, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed183 }, \
+ { 149, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed1a7 }, \
+ { 151, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed187 }, \
+ { 153, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed18f }, \
+ { 157, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed19f }, \
+ { 159, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed1a7 }, \
+ { 161, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed187 }, \
+ { 165, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed197 }, \
+ { 167, 0x98402ec4, 0x984c03d2, 0x98179855, 0x9815531f }, \
+ { 169, 0x98402ec4, 0x984c03d2, 0x98179855, 0x98155327 }, \
+ { 171, 0x98402ec4, 0x984c03d6, 0x98179855, 0x98155307 }, \
+ { 173, 0x98402ec4, 0x984c03d6, 0x98179855, 0x9815530f }, \
+ { 184, 0x95002ccc, 0x9500491e, 0x9509be55, 0x950c0a0b }, \
+ { 188, 0x95002ccc, 0x95004922, 0x9509be55, 0x950c0a13 }, \
+ { 192, 0x95002ccc, 0x95004926, 0x9509be55, 0x950c0a1b }, \
+ { 196, 0x95002ccc, 0x9500492a, 0x9509be55, 0x950c0a23 }, \
+ { 208, 0x95002ccc, 0x9500493a, 0x9509be55, 0x950c0a13 }, \
+ { 212, 0x95002ccc, 0x9500493e, 0x9509be55, 0x950c0a1b }, \
+ { 216, 0x95002ccc, 0x95004982, 0x9509be55, 0x950c0a23 }
+
+#define RT3070_RF3052 \
+ { 0xf1, 2, 2 }, \
+ { 0xf1, 2, 7 }, \
+ { 0xf2, 2, 2 }, \
+ { 0xf2, 2, 7 }, \
+ { 0xf3, 2, 2 }, \
+ { 0xf3, 2, 7 }, \
+ { 0xf4, 2, 2 }, \
+ { 0xf4, 2, 7 }, \
+ { 0xf5, 2, 2 }, \
+ { 0xf5, 2, 7 }, \
+ { 0xf6, 2, 2 }, \
+ { 0xf6, 2, 7 }, \
+ { 0xf7, 2, 2 }, \
+ { 0xf8, 2, 4 }, \
+ { 0x56, 0, 4 }, \
+ { 0x56, 0, 6 }, \
+ { 0x56, 0, 8 }, \
+ { 0x57, 0, 0 }, \
+ { 0x57, 0, 2 }, \
+ { 0x57, 0, 4 }, \
+ { 0x57, 0, 8 }, \
+ { 0x57, 0, 10 }, \
+ { 0x58, 0, 0 }, \
+ { 0x58, 0, 4 }, \
+ { 0x58, 0, 6 }, \
+ { 0x58, 0, 8 }, \
+ { 0x5b, 0, 8 }, \
+ { 0x5b, 0, 10 }, \
+ { 0x5c, 0, 0 }, \
+ { 0x5c, 0, 4 }, \
+ { 0x5c, 0, 6 }, \
+ { 0x5c, 0, 8 }, \
+ { 0x5d, 0, 0 }, \
+ { 0x5d, 0, 2 }, \
+ { 0x5d, 0, 4 }, \
+ { 0x5d, 0, 8 }, \
+ { 0x5d, 0, 10 }, \
+ { 0x5e, 0, 0 }, \
+ { 0x5e, 0, 4 }, \
+ { 0x5e, 0, 6 }, \
+ { 0x5e, 0, 8 }, \
+ { 0x5f, 0, 0 }, \
+ { 0x5f, 0, 9 }, \
+ { 0x5f, 0, 11 }, \
+ { 0x60, 0, 1 }, \
+ { 0x60, 0, 5 }, \
+ { 0x60, 0, 7 }, \
+ { 0x60, 0, 9 }, \
+ { 0x61, 0, 1 }, \
+ { 0x61, 0, 3 }, \
+ { 0x61, 0, 5 }, \
+ { 0x61, 0, 7 }, \
+ { 0x61, 0, 9 }
+
+#define RT5592_RF5592_20MHZ \
+ { 0x1e2, 4, 10, 3 }, \
+ { 0x1e3, 4, 10, 3 }, \
+ { 0x1e4, 4, 10, 3 }, \
+ { 0x1e5, 4, 10, 3 }, \
+ { 0x1e6, 4, 10, 3 }, \
+ { 0x1e7, 4, 10, 3 }, \
+ { 0x1e8, 4, 10, 3 }, \
+ { 0x1e9, 4, 10, 3 }, \
+ { 0x1ea, 4, 10, 3 }, \
+ { 0x1eb, 4, 10, 3 }, \
+ { 0x1ec, 4, 10, 3 }, \
+ { 0x1ed, 4, 10, 3 }, \
+ { 0x1ee, 4, 10, 3 }, \
+ { 0x1f0, 8, 10, 3 }, \
+ { 0xac, 8, 12, 1 }, \
+ { 0xad, 0, 12, 1 }, \
+ { 0xad, 4, 12, 1 }, \
+ { 0xae, 0, 12, 1 }, \
+ { 0xae, 4, 12, 1 }, \
+ { 0xae, 8, 12, 1 }, \
+ { 0xaf, 4, 12, 1 }, \
+ { 0xaf, 8, 12, 1 }, \
+ { 0xb0, 0, 12, 1 }, \
+ { 0xb0, 8, 12, 1 }, \
+ { 0xb1, 0, 12, 1 }, \
+ { 0xb1, 4, 12, 1 }, \
+ { 0xb7, 4, 12, 1 }, \
+ { 0xb7, 8, 12, 1 }, \
+ { 0xb8, 0, 12, 1 }, \
+ { 0xb8, 8, 12, 1 }, \
+ { 0xb9, 0, 12, 1 }, \
+ { 0xb9, 4, 12, 1 }, \
+ { 0xba, 0, 12, 1 }, \
+ { 0xba, 4, 12, 1 }, \
+ { 0xba, 8, 12, 1 }, \
+ { 0xbb, 4, 12, 1 }, \
+ { 0xbb, 8, 12, 1 }, \
+ { 0xbc, 0, 12, 1 }, \
+ { 0xbc, 8, 12, 1 }, \
+ { 0xbd, 0, 12, 1 }, \
+ { 0xbd, 4, 12, 1 }, \
+ { 0xbe, 0, 12, 1 }, \
+ { 0xbf, 6, 12, 1 }, \
+ { 0xbf, 10, 12, 1 }, \
+ { 0xc0, 2, 12, 1 }, \
+ { 0xc0, 10, 12, 1 }, \
+ { 0xc1, 2, 12, 1 }, \
+ { 0xc1, 6, 12, 1 }, \
+ { 0xc2, 2, 12, 1 }, \
+ { 0xa4, 0, 12, 1 }, \
+ { 0xa4, 4, 12, 1 }, \
+ { 0xa5, 8, 12, 1 }, \
+ { 0xa6, 0, 12, 1 }
+
+#define RT5592_RF5592_40MHZ \
+ { 0xf1, 2, 10, 3 }, \
+ { 0xf1, 7, 10, 3 }, \
+ { 0xf2, 2, 10, 3 }, \
+ { 0xf2, 7, 10, 3 }, \
+ { 0xf3, 2, 10, 3 }, \
+ { 0xf3, 7, 10, 3 }, \
+ { 0xf4, 2, 10, 3 }, \
+ { 0xf4, 7, 10, 3 }, \
+ { 0xf5, 2, 10, 3 }, \
+ { 0xf5, 7, 10, 3 }, \
+ { 0xf6, 2, 10, 3 }, \
+ { 0xf6, 7, 10, 3 }, \
+ { 0xf7, 2, 10, 3 }, \
+ { 0xf8, 4, 10, 3 }, \
+ { 0x56, 4, 12, 1 }, \
+ { 0x56, 6, 12, 1 }, \
+ { 0x56, 8, 12, 1 }, \
+ { 0x57, 0, 12, 1 }, \
+ { 0x57, 2, 12, 1 }, \
+ { 0x57, 4, 12, 1 }, \
+ { 0x57, 8, 12, 1 }, \
+ { 0x57, 10, 12, 1 }, \
+ { 0x58, 0, 12, 1 }, \
+ { 0x58, 4, 12, 1 }, \
+ { 0x58, 6, 12, 1 }, \
+ { 0x58, 8, 12, 1 }, \
+ { 0x5b, 8, 12, 1 }, \
+ { 0x5b, 10, 12, 1 }, \
+ { 0x5c, 0, 12, 1 }, \
+ { 0x5c, 4, 12, 1 }, \
+ { 0x5c, 6, 12, 1 }, \
+ { 0x5c, 8, 12, 1 }, \
+ { 0x5d, 0, 12, 1 }, \
+ { 0x5d, 2, 12, 1 }, \
+ { 0x5d, 4, 12, 1 }, \
+ { 0x5d, 8, 12, 1 }, \
+ { 0x5d, 10, 12, 1 }, \
+ { 0x5e, 0, 12, 1 }, \
+ { 0x5e, 4, 12, 1 }, \
+ { 0x5e, 6, 12, 1 }, \
+ { 0x5e, 8, 12, 1 }, \
+ { 0x5f, 0, 12, 1 }, \
+ { 0x5f, 9, 12, 1 }, \
+ { 0x5f, 11, 12, 1 }, \
+ { 0x60, 1, 12, 1 }, \
+ { 0x60, 5, 12, 1 }, \
+ { 0x60, 7, 12, 1 }, \
+ { 0x60, 9, 12, 1 }, \
+ { 0x61, 1, 12, 1 }, \
+ { 0x52, 0, 12, 1 }, \
+ { 0x52, 4, 12, 1 }, \
+ { 0x52, 8, 12, 1 }, \
+ { 0x53, 0, 12, 1 }
+
+#define RT3070_DEF_RF \
+ { 4, 0x40 }, \
+ { 5, 0x03 }, \
+ { 6, 0x02 }, \
+ { 7, 0x60 }, \
+ { 9, 0x0f }, \
+ { 10, 0x41 }, \
+ { 11, 0x21 }, \
+ { 12, 0x7b }, \
+ { 14, 0x90 }, \
+ { 15, 0x58 }, \
+ { 16, 0xb3 }, \
+ { 17, 0x92 }, \
+ { 18, 0x2c }, \
+ { 19, 0x02 }, \
+ { 20, 0xba }, \
+ { 21, 0xdb }, \
+ { 24, 0x16 }, \
+ { 25, 0x03 }, \
+ { 29, 0x1f }
+
+#define RT3572_DEF_RF \
+ { 0, 0x70 }, \
+ { 1, 0x81 }, \
+ { 2, 0xf1 }, \
+ { 3, 0x02 }, \
+ { 4, 0x4c }, \
+ { 5, 0x05 }, \
+ { 6, 0x4a }, \
+ { 7, 0xd8 }, \
+ { 9, 0xc3 }, \
+ { 10, 0xf1 }, \
+ { 11, 0xb9 }, \
+ { 12, 0x70 }, \
+ { 13, 0x65 }, \
+ { 14, 0xa0 }, \
+ { 15, 0x53 }, \
+ { 16, 0x4c }, \
+ { 17, 0x23 }, \
+ { 18, 0xac }, \
+ { 19, 0x93 }, \
+ { 20, 0xb3 }, \
+ { 21, 0xd0 }, \
+ { 22, 0x00 }, \
+ { 23, 0x3c }, \
+ { 24, 0x16 }, \
+ { 25, 0x15 }, \
+ { 26, 0x85 }, \
+ { 27, 0x00 }, \
+ { 28, 0x00 }, \
+ { 29, 0x9b }, \
+ { 30, 0x09 }, \
+ { 31, 0x10 }
+
+#define RT3593_DEF_RF \
+ { 1, 0x03 }, \
+ { 3, 0x80 }, \
+ { 5, 0x00 }, \
+ { 6, 0x40 }, \
+ { 8, 0xf1 }, \
+ { 9, 0x02 }, \
+ { 10, 0xd3 }, \
+ { 11, 0x40 }, \
+ { 12, 0x4e }, \
+ { 13, 0x12 }, \
+ { 18, 0x40 }, \
+ { 22, 0x20 }, \
+ { 30, 0x10 }, \
+ { 31, 0x80 }, \
+ { 32, 0x78 }, \
+ { 33, 0x3b }, \
+ { 34, 0x3c }, \
+ { 35, 0xe0 }, \
+ { 38, 0x86 }, \
+ { 39, 0x23 }, \
+ { 44, 0xd3 }, \
+ { 45, 0xbb }, \
+ { 46, 0x60 }, \
+ { 49, 0x81 }, \
+ { 50, 0x86 }, \
+ { 51, 0x75 }, \
+ { 52, 0x45 }, \
+ { 53, 0x18 }, \
+ { 54, 0x18 }, \
+ { 55, 0x18 }, \
+ { 56, 0xdb }, \
+ { 57, 0x6e }
+
+#define RT5390_DEF_RF \
+ { 1, 0x0f }, \
+ { 2, 0x80 }, \
+ { 3, 0x88 }, \
+ { 5, 0x10 }, \
+ { 6, 0xa0 }, \
+ { 7, 0x00 }, \
+ { 10, 0x53 }, \
+ { 11, 0x4a }, \
+ { 12, 0x46 }, \
+ { 13, 0x9f }, \
+ { 14, 0x00 }, \
+ { 15, 0x00 }, \
+ { 16, 0x00 }, \
+ { 18, 0x03 }, \
+ { 19, 0x00 }, \
+ { 20, 0x00 }, \
+ { 21, 0x00 }, \
+ { 22, 0x20 }, \
+ { 23, 0x00 }, \
+ { 24, 0x00 }, \
+ { 25, 0xc0 }, \
+ { 26, 0x00 }, \
+ { 27, 0x09 }, \
+ { 28, 0x00 }, \
+ { 29, 0x10 }, \
+ { 30, 0x10 }, \
+ { 31, 0x80 }, \
+ { 32, 0x80 }, \
+ { 33, 0x00 }, \
+ { 34, 0x07 }, \
+ { 35, 0x12 }, \
+ { 36, 0x00 }, \
+ { 37, 0x08 }, \
+ { 38, 0x85 }, \
+ { 39, 0x1b }, \
+ { 40, 0x0b }, \
+ { 41, 0xbb }, \
+ { 42, 0xd2 }, \
+ { 43, 0x9a }, \
+ { 44, 0x0e }, \
+ { 45, 0xa2 }, \
+ { 46, 0x7b }, \
+ { 47, 0x00 }, \
+ { 48, 0x10 }, \
+ { 49, 0x94 }, \
+ { 52, 0x38 }, \
+ { 53, 0x84 }, \
+ { 54, 0x78 }, \
+ { 55, 0x44 }, \
+ { 56, 0x22 }, \
+ { 57, 0x80 }, \
+ { 58, 0x7f }, \
+ { 59, 0x8f }, \
+ { 60, 0x45 }, \
+ { 61, 0xdd }, \
+ { 62, 0x00 }, \
+ { 63, 0x00 }
+
+#define RT5392_DEF_RF \
+ { 1, 0x17 }, \
+ { 3, 0x88 }, \
+ { 5, 0x10 }, \
+ { 6, 0xe0 }, \
+ { 7, 0x00 }, \
+ { 10, 0x53 }, \
+ { 11, 0x4a }, \
+ { 12, 0x46 }, \
+ { 13, 0x9f }, \
+ { 14, 0x00 }, \
+ { 15, 0x00 }, \
+ { 16, 0x00 }, \
+ { 18, 0x03 }, \
+ { 19, 0x4d }, \
+ { 20, 0x00 }, \
+ { 21, 0x8d }, \
+ { 22, 0x20 }, \
+ { 23, 0x0b }, \
+ { 24, 0x44 }, \
+ { 25, 0x80 }, \
+ { 26, 0x82 }, \
+ { 27, 0x09 }, \
+ { 28, 0x00 }, \
+ { 29, 0x10 }, \
+ { 30, 0x10 }, \
+ { 31, 0x80 }, \
+ { 32, 0x20 }, \
+ { 33, 0xc0 }, \
+ { 34, 0x07 }, \
+ { 35, 0x12 }, \
+ { 36, 0x00 }, \
+ { 37, 0x08 }, \
+ { 38, 0x89 }, \
+ { 39, 0x1b }, \
+ { 40, 0x0f }, \
+ { 41, 0xbb }, \
+ { 42, 0xd5 }, \
+ { 43, 0x9b }, \
+ { 44, 0x0e }, \
+ { 45, 0xa2 }, \
+ { 46, 0x73 }, \
+ { 47, 0x0c }, \
+ { 48, 0x10 }, \
+ { 49, 0x94 }, \
+ { 50, 0x94 }, \
+ { 51, 0x3a }, \
+ { 52, 0x48 }, \
+ { 53, 0x44 }, \
+ { 54, 0x38 }, \
+ { 55, 0x43 }, \
+ { 56, 0xa1 }, \
+ { 57, 0x00 }, \
+ { 58, 0x39 }, \
+ { 59, 0x07 }, \
+ { 60, 0x45 }, \
+ { 61, 0x91 }, \
+ { 62, 0x39 }, \
+ { 63, 0x07 }
+
+#define RT5592_DEF_RF \
+ { 1, 0x3f }, \
+ { 3, 0x08 }, \
+ { 5, 0x10 }, \
+ { 6, 0xe4 }, \
+ { 7, 0x00 }, \
+ { 14, 0x00 }, \
+ { 15, 0x00 }, \
+ { 16, 0x00 }, \
+ { 18, 0x03 }, \
+ { 19, 0x4d }, \
+ { 20, 0x10 }, \
+ { 21, 0x8d }, \
+ { 26, 0x82 }, \
+ { 28, 0x00 }, \
+ { 29, 0x10 }, \
+ { 33, 0xc0 }, \
+ { 34, 0x07 }, \
+ { 35, 0x12 }, \
+ { 47, 0x0c }, \
+ { 53, 0x22 }, \
+ { 63, 0x07 }
+
+#define RT5592_2GHZ_DEF_RF \
+ { 10, 0x90 }, \
+ { 11, 0x4a }, \
+ { 12, 0x52 }, \
+ { 13, 0x42 }, \
+ { 22, 0x40 }, \
+ { 24, 0x4a }, \
+ { 25, 0x80 }, \
+ { 27, 0x42 }, \
+ { 36, 0x80 }, \
+ { 37, 0x08 }, \
+ { 38, 0x89 }, \
+ { 39, 0x1b }, \
+ { 40, 0x0d }, \
+ { 41, 0x9b }, \
+ { 42, 0xd5 }, \
+ { 43, 0x72 }, \
+ { 44, 0x0e }, \
+ { 45, 0xa2 }, \
+ { 46, 0x6b }, \
+ { 48, 0x10 }, \
+ { 51, 0x3e }, \
+ { 52, 0x48 }, \
+ { 54, 0x38 }, \
+ { 56, 0xa1 }, \
+ { 57, 0x00 }, \
+ { 58, 0x39 }, \
+ { 60, 0x45 }, \
+ { 61, 0x91 }, \
+ { 62, 0x39 }
+
+#define RT5592_5GHZ_DEF_RF \
+ { 10, 0x97 }, \
+ { 11, 0x40 }, \
+ { 25, 0xbf }, \
+ { 27, 0x42 }, \
+ { 36, 0x00 }, \
+ { 37, 0x04 }, \
+ { 38, 0x85 }, \
+ { 40, 0x42 }, \
+ { 41, 0xbb }, \
+ { 42, 0xd7 }, \
+ { 45, 0x41 }, \
+ { 48, 0x00 }, \
+ { 57, 0x77 }, \
+ { 60, 0x05 }, \
+ { 61, 0x01 }
+
+#define RT5592_CHAN_5GHZ \
+ { 36, 64, 12, 0x2e }, \
+ { 100, 165, 12, 0x0e }, \
+ { 36, 64, 13, 0x22 }, \
+ { 100, 165, 13, 0x42 }, \
+ { 36, 64, 22, 0x60 }, \
+ { 100, 165, 22, 0x40 }, \
+ { 36, 64, 23, 0x7f }, \
+ { 100, 153, 23, 0x3c }, \
+ { 155, 165, 23, 0x38 }, \
+ { 36, 50, 24, 0x09 }, \
+ { 52, 64, 24, 0x07 }, \
+ { 100, 153, 24, 0x06 }, \
+ { 155, 165, 24, 0x05 }, \
+ { 36, 64, 39, 0x1c }, \
+ { 100, 138, 39, 0x1a }, \
+ { 140, 165, 39, 0x18 }, \
+ { 36, 64, 43, 0x5b }, \
+ { 100, 138, 43, 0x3b }, \
+ { 140, 165, 43, 0x1b }, \
+ { 36, 64, 44, 0x40 }, \
+ { 100, 138, 44, 0x20 }, \
+ { 140, 165, 44, 0x10 }, \
+ { 36, 64, 46, 0x00 }, \
+ { 100, 138, 46, 0x18 }, \
+ { 140, 165, 46, 0x08 }, \
+ { 36, 64, 51, 0xfe }, \
+ { 100, 124, 51, 0xfc }, \
+ { 126, 165, 51, 0xec }, \
+ { 36, 64, 52, 0x0c }, \
+ { 100, 138, 52, 0x06 }, \
+ { 140, 165, 52, 0x06 }, \
+ { 36, 64, 54, 0xf8 }, \
+ { 100, 165, 54, 0xeb }, \
+ { 36, 50, 55, 0x06 }, \
+ { 52, 64, 55, 0x04 }, \
+ { 100, 138, 55, 0x01 }, \
+ { 140, 165, 55, 0x00 }, \
+ { 36, 50, 56, 0xd3 }, \
+ { 52, 128, 56, 0xbb }, \
+ { 130, 165, 56, 0xab }, \
+ { 36, 64, 58, 0x15 }, \
+ { 100, 116, 58, 0x1d }, \
+ { 118, 165, 58, 0x15 }, \
+ { 36, 64, 59, 0x7f }, \
+ { 100, 138, 59, 0x3f }, \
+ { 140, 165, 59, 0x7c }, \
+ { 36, 64, 62, 0x15 }, \
+ { 100, 116, 62, 0x1d }, \
+ { 118, 165, 62, 0x15 }
+
+union run_stats {
+ uint32_t raw;
+ struct {
+ uint16_t fail;
+ uint16_t pad;
+ } error;
+ struct {
+ uint16_t success;
+ uint16_t retry;
+ } tx;
+} __aligned(4);
+
+#endif /* _IF_RUNREG_H_ */
diff --git a/freebsd/sys/dev/usb/wlan/if_runvar.h b/freebsd/sys/dev/usb/wlan/if_runvar.h
new file mode 100644
index 00000000..7209bfc7
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_runvar.h
@@ -0,0 +1,269 @@
+/* $OpenBSD: if_runvar.h,v 1.3 2009/03/26 20:17:27 damien Exp $ */
+
+/*-
+ * Copyright (c) 2008,2009 Damien Bergamini <damien.bergamini@free.fr>
+ * ported to FreeBSD by Akinori Furukoshi <moonlightakkiy@yahoo.ca>
+ * USB Consulting, Hans Petter Selasky <hselasky@freebsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IF_RUNVAR_H_
+#define _IF_RUNVAR_H_
+
+#define RUN_MAX_RXSZ \
+ MIN(4096, MJUMPAGESIZE)
+
+/* NB: "11" is the maximum number of padding bytes needed for Tx */
+#define RUN_MAX_TXSZ \
+ (sizeof (struct rt2870_txd) + \
+ sizeof (struct rt2860_txwi) + \
+ MCLBYTES + 11)
+
+#define RUN_TX_TIMEOUT 5000 /* ms */
+
+/* Tx ring count was 8/endpoint, now 32 for all 4 (or 6) endpoints. */
+#define RUN_TX_RING_COUNT 32
+#define RUN_RX_RING_COUNT 1
+
+#define RT2870_WCID_MAX 64
+#define RUN_AID2WCID(aid) ((aid) & 0xff)
+
+#define RUN_VAP_MAX 8
+
+struct run_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint64_t wr_tsf;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_dbm_antsignal;
+ uint8_t wr_antenna;
+ uint8_t wr_antsignal;
+} __packed __aligned(8);
+
+#define RUN_RX_RADIOTAP_PRESENT \
+ (1 << IEEE80211_RADIOTAP_TSFT | \
+ 1 << IEEE80211_RADIOTAP_FLAGS | \
+ 1 << IEEE80211_RADIOTAP_RATE | \
+ 1 << IEEE80211_RADIOTAP_CHANNEL | \
+ 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL | \
+ 1 << IEEE80211_RADIOTAP_ANTENNA | \
+ 1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)
+
+struct run_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+ uint8_t wt_hwqueue;
+} __packed __aligned(8);
+
+#define IEEE80211_RADIOTAP_HWQUEUE 15
+
+#define RUN_TX_RADIOTAP_PRESENT \
+ (1 << IEEE80211_RADIOTAP_FLAGS | \
+ 1 << IEEE80211_RADIOTAP_RATE | \
+ 1 << IEEE80211_RADIOTAP_CHANNEL | \
+ 1 << IEEE80211_RADIOTAP_HWQUEUE)
+
+struct run_softc;
+
+struct run_tx_data {
+ STAILQ_ENTRY(run_tx_data) next;
+ struct run_softc *sc;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ uint32_t align[0]; /* dummy field */
+ uint8_t desc[sizeof(struct rt2870_txd) +
+ sizeof(struct rt2860_txwi)];
+ uint8_t ridx;
+};
+STAILQ_HEAD(run_tx_data_head, run_tx_data);
+
+struct run_node {
+ struct ieee80211_node ni;
+ uint8_t ridx[IEEE80211_RATE_MAXSIZE];
+ uint8_t ctl_ridx[IEEE80211_RATE_MAXSIZE];
+ uint8_t amrr_ridx;
+ uint8_t mgt_ridx;
+ uint8_t fix_ridx;
+};
+#define RUN_NODE(ni) ((struct run_node *)(ni))
+
+struct run_cmdq {
+ void *arg0;
+ void *arg1;
+ void (*func)(void *);
+ struct ieee80211_key *k;
+ struct ieee80211_key key;
+ uint8_t mac[IEEE80211_ADDR_LEN];
+ uint8_t wcid;
+};
+
+struct run_vap {
+ struct ieee80211vap vap;
+ struct mbuf *beacon_mbuf;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ void (*recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int,
+ const struct ieee80211_rx_stats *,
+ int, int);
+
+ uint8_t rvp_id;
+};
+#define RUN_VAP(vap) ((struct run_vap *)(vap))
+
+/*
+ * There are 7 bulk endpoints: 1 for RX
+ * and 6 for TX (4 EDCAs + HCCA + Prio).
+ * Update 03-14-2009: some devices like the Planex GW-US300MiniS
+ * seem to have only 4 TX bulk endpoints (Fukaumi Naoki).
+ */
+enum {
+ RUN_BULK_TX_BE, /* = WME_AC_BE */
+ RUN_BULK_TX_BK, /* = WME_AC_BK */
+ RUN_BULK_TX_VI, /* = WME_AC_VI */
+ RUN_BULK_TX_VO, /* = WME_AC_VO */
+ RUN_BULK_TX_HCCA,
+ RUN_BULK_TX_PRIO,
+ RUN_BULK_RX,
+ RUN_N_XFER,
+};
+
+#define RUN_EP_QUEUES RUN_BULK_RX
+
+struct run_endpoint_queue {
+ struct run_tx_data tx_data[RUN_TX_RING_COUNT];
+ struct run_tx_data_head tx_qh;
+ struct run_tx_data_head tx_fh;
+ uint32_t tx_nfree;
+};
+
+struct run_softc {
+ struct mtx sc_mtx;
+ struct ieee80211com sc_ic;
+ struct ieee80211_ratectl_tx_stats sc_txs;
+ struct mbufq sc_snd;
+ device_t sc_dev;
+ struct usb_device *sc_udev;
+ int sc_need_fwload;
+
+ int sc_flags;
+#define RUN_FLAG_FWLOAD_NEEDED 0x01
+#define RUN_RUNNING 0x02
+
+ uint16_t wcid_stats[RT2870_WCID_MAX + 1][3];
+#define RUN_TXCNT 0
+#define RUN_SUCCESS 1
+#define RUN_RETRY 2
+
+ int (*sc_srom_read)(struct run_softc *,
+ uint16_t, uint16_t *);
+
+ uint16_t mac_ver;
+ uint16_t mac_rev;
+ uint16_t rf_rev;
+ uint8_t freq;
+ uint8_t ntxchains;
+ uint8_t nrxchains;
+
+ uint8_t bbp25;
+ uint8_t bbp26;
+ uint8_t rf24_20mhz;
+ uint8_t rf24_40mhz;
+ uint8_t patch_dac;
+ uint8_t rfswitch;
+ uint8_t ext_2ghz_lna;
+ uint8_t ext_5ghz_lna;
+ uint8_t calib_2ghz;
+ uint8_t calib_5ghz;
+ uint8_t txmixgain_2ghz;
+ uint8_t txmixgain_5ghz;
+ int8_t txpow1[54];
+ int8_t txpow2[54];
+ int8_t txpow3[54];
+ int8_t rssi_2ghz[3];
+ int8_t rssi_5ghz[3];
+ uint8_t lna[4];
+
+ struct {
+ uint8_t reg;
+ uint8_t val;
+ } bbp[10], rf[10];
+ uint8_t leds;
+ uint16_t led[3];
+ uint32_t txpow20mhz[5];
+ uint32_t txpow40mhz_2ghz[5];
+ uint32_t txpow40mhz_5ghz[5];
+
+ struct run_endpoint_queue sc_epq[RUN_EP_QUEUES];
+
+ struct task ratectl_task;
+ struct usb_callout ratectl_ch;
+ uint8_t ratectl_run;
+#define RUN_RATECTL_OFF 0
+
+/* need to be power of 2, otherwise RUN_CMDQ_GET fails */
+#define RUN_CMDQ_MAX 16
+#define RUN_CMDQ_MASQ (RUN_CMDQ_MAX - 1)
+ struct run_cmdq cmdq[RUN_CMDQ_MAX];
+ struct task cmdq_task;
+ uint32_t cmdq_store;
+ uint8_t cmdq_exec;
+ uint8_t cmdq_run;
+ uint8_t cmdq_key_set;
+#define RUN_CMDQ_ABORT 0
+#define RUN_CMDQ_GO 1
+
+ struct usb_xfer *sc_xfer[RUN_N_XFER];
+
+ struct mbuf *rx_m;
+
+ uint8_t fifo_cnt;
+
+ uint8_t running;
+ uint8_t runbmap;
+ uint8_t ap_running;
+ uint8_t adhoc_running;
+ uint8_t sta_running;
+ uint8_t rvp_cnt;
+ uint8_t rvp_bmap;
+ uint8_t sc_detached;
+
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
+
+ union {
+ struct run_rx_radiotap_header th;
+ uint8_t pad[64];
+ } sc_rxtapu;
+#define sc_rxtap sc_rxtapu.th
+
+ union {
+ struct run_tx_radiotap_header th;
+ uint8_t pad[64];
+ } sc_txtapu;
+#define sc_txtap sc_txtapu.th
+};
+
+#define RUN_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RUN_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RUN_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t)
+
+#endif /* _IF_RUNVAR_H_ */
diff --git a/freebsd/sys/dev/usb/wlan/if_uath.c b/freebsd/sys/dev/usb/wlan/if_uath.c
new file mode 100644
index 00000000..c0c339e0
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_uath.c
@@ -0,0 +1,2800 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2006 Sam Leffler, Errno Consulting
+ * Copyright (c) 2008-2009 Weongyo Jeong <weongyo@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,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+/*
+ * This driver is distantly derived from a driver of the same name
+ * by Damien Bergamini. The original copyright is included below:
+ *
+ * Copyright (c) 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Driver for Atheros AR5523 USB parts.
+ *
+ * The driver requires firmware to be loaded into the device. This
+ * is done on device discovery from a user application (uathload)
+ * that is launched by devd when a device with suitable product ID
+ * is recognized. Once firmware has been loaded the device will
+ * reset the USB port and re-attach with the original product ID+1
+ * and this driver will be attached. The firmware is licensed for
+ * general use (royalty free) and may be incorporated in products.
+ * Note that the firmware normally packaged with the NDIS drivers
+ * for these devices does not work in this way and so does not work
+ * with this driver.
+ */
+#include <rtems/bsd/sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#include <dev/usb/wlan/if_uathreg.h>
+#include <dev/usb/wlan/if_uathvar.h>
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uath, CTLFLAG_RW, 0, "USB Atheros");
+
+static int uath_countrycode = CTRY_DEFAULT; /* country code */
+SYSCTL_INT(_hw_usb_uath, OID_AUTO, countrycode, CTLFLAG_RWTUN, &uath_countrycode,
+ 0, "country code");
+static int uath_regdomain = 0; /* regulatory domain */
+SYSCTL_INT(_hw_usb_uath, OID_AUTO, regdomain, CTLFLAG_RD, &uath_regdomain,
+ 0, "regulatory domain");
+
+#ifdef UATH_DEBUG
+int uath_debug = 0;
+SYSCTL_INT(_hw_usb_uath, OID_AUTO, debug, CTLFLAG_RWTUN, &uath_debug, 0,
+ "uath debug level");
+enum {
+ UATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ UATH_DEBUG_XMIT_DUMP = 0x00000002, /* xmit dump */
+ UATH_DEBUG_RECV = 0x00000004, /* basic recv operation */
+ UATH_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */
+ UATH_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */
+ UATH_DEBUG_RECV_ALL = 0x00000020, /* trace all frames (beacons) */
+ UATH_DEBUG_INIT = 0x00000040, /* initialization of dev */
+ UATH_DEBUG_DEVCAP = 0x00000080, /* dev caps */
+ UATH_DEBUG_CMDS = 0x00000100, /* commands */
+ UATH_DEBUG_CMDS_DUMP = 0x00000200, /* command buffer dump */
+ UATH_DEBUG_RESET = 0x00000400, /* reset processing */
+ UATH_DEBUG_STATE = 0x00000800, /* 802.11 state transitions */
+ UATH_DEBUG_MULTICAST = 0x00001000, /* multicast */
+ UATH_DEBUG_WME = 0x00002000, /* WME */
+ UATH_DEBUG_CHANNEL = 0x00004000, /* channel */
+ UATH_DEBUG_RATES = 0x00008000, /* rates */
+ UATH_DEBUG_CRYPTO = 0x00010000, /* crypto */
+ UATH_DEBUG_LED = 0x00020000, /* LED */
+ UATH_DEBUG_ANY = 0xffffffff
+};
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (sc->sc_debug & (m)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#endif
+
+/* recognized device vendors/products */
+static const STRUCT_USB_HOST_ID uath_devs[] = {
+#define UATH_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+ UATH_DEV(ACCTON, SMCWUSBTG2),
+ UATH_DEV(ATHEROS, AR5523),
+ UATH_DEV(ATHEROS2, AR5523_1),
+ UATH_DEV(ATHEROS2, AR5523_2),
+ UATH_DEV(ATHEROS2, AR5523_3),
+ UATH_DEV(CONCEPTRONIC, AR5523_1),
+ UATH_DEV(CONCEPTRONIC, AR5523_2),
+ UATH_DEV(DLINK, DWLAG122),
+ UATH_DEV(DLINK, DWLAG132),
+ UATH_DEV(DLINK, DWLG132),
+ UATH_DEV(DLINK2, DWA120),
+ UATH_DEV(GIGASET, AR5523),
+ UATH_DEV(GIGASET, SMCWUSBTG),
+ UATH_DEV(GLOBALSUN, AR5523_1),
+ UATH_DEV(GLOBALSUN, AR5523_2),
+ UATH_DEV(NETGEAR, WG111U),
+ UATH_DEV(NETGEAR3, WG111T),
+ UATH_DEV(NETGEAR3, WPN111),
+ UATH_DEV(NETGEAR3, WPN111_2),
+ UATH_DEV(UMEDIA, TEW444UBEU),
+ UATH_DEV(UMEDIA, AR5523_2),
+ UATH_DEV(WISTRONNEWEB, AR5523_1),
+ UATH_DEV(WISTRONNEWEB, AR5523_2),
+ UATH_DEV(ZCOM, AR5523)
+#undef UATH_DEV
+};
+
+static usb_callback_t uath_intr_rx_callback;
+static usb_callback_t uath_intr_tx_callback;
+static usb_callback_t uath_bulk_rx_callback;
+static usb_callback_t uath_bulk_tx_callback;
+
+static const struct usb_config uath_usbconfig[UATH_N_XFERS] = {
+ [UATH_INTR_RX] = {
+ .type = UE_BULK,
+ .endpoint = 0x1,
+ .direction = UE_DIR_IN,
+ .bufsize = UATH_MAX_CMDSZ,
+ .flags = {
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = uath_intr_rx_callback
+ },
+ [UATH_INTR_TX] = {
+ .type = UE_BULK,
+ .endpoint = 0x1,
+ .direction = UE_DIR_OUT,
+ .bufsize = UATH_MAX_CMDSZ * UATH_CMD_LIST_COUNT,
+ .flags = {
+ .force_short_xfer = 1,
+ .pipe_bof = 1,
+ },
+ .callback = uath_intr_tx_callback,
+ .timeout = UATH_CMD_TIMEOUT
+ },
+ [UATH_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = 0x2,
+ .direction = UE_DIR_IN,
+ .bufsize = MCLBYTES,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = uath_bulk_rx_callback
+ },
+ [UATH_BULK_TX] = {
+ .type = UE_BULK,
+ .endpoint = 0x2,
+ .direction = UE_DIR_OUT,
+ .bufsize = UATH_MAX_TXBUFSZ * UATH_TX_DATA_LIST_COUNT,
+ .flags = {
+ .force_short_xfer = 1,
+ .pipe_bof = 1
+ },
+ .callback = uath_bulk_tx_callback,
+ .timeout = UATH_DATA_TIMEOUT
+ }
+};
+
+static struct ieee80211vap *uath_vap_create(struct ieee80211com *,
+ const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void uath_vap_delete(struct ieee80211vap *);
+static int uath_alloc_cmd_list(struct uath_softc *, struct uath_cmd []);
+static void uath_free_cmd_list(struct uath_softc *, struct uath_cmd []);
+static int uath_host_available(struct uath_softc *);
+static int uath_get_capability(struct uath_softc *, uint32_t, uint32_t *);
+static int uath_get_devcap(struct uath_softc *);
+static struct uath_cmd *
+ uath_get_cmdbuf(struct uath_softc *);
+static int uath_cmd_read(struct uath_softc *, uint32_t, const void *,
+ int, void *, int, int);
+static int uath_cmd_write(struct uath_softc *, uint32_t, const void *,
+ int, int);
+static void uath_stat(void *);
+#ifdef UATH_DEBUG
+static void uath_dump_cmd(const uint8_t *, int, char);
+static const char *
+ uath_codename(int);
+#endif
+static int uath_get_devstatus(struct uath_softc *,
+ uint8_t macaddr[IEEE80211_ADDR_LEN]);
+static int uath_get_status(struct uath_softc *, uint32_t, void *, int);
+static int uath_alloc_rx_data_list(struct uath_softc *);
+static int uath_alloc_tx_data_list(struct uath_softc *);
+static void uath_free_rx_data_list(struct uath_softc *);
+static void uath_free_tx_data_list(struct uath_softc *);
+static int uath_init(struct uath_softc *);
+static void uath_stop(struct uath_softc *);
+static void uath_parent(struct ieee80211com *);
+static int uath_transmit(struct ieee80211com *, struct mbuf *);
+static void uath_start(struct uath_softc *);
+static int uath_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void uath_scan_start(struct ieee80211com *);
+static void uath_scan_end(struct ieee80211com *);
+static void uath_set_channel(struct ieee80211com *);
+static void uath_update_mcast(struct ieee80211com *);
+static void uath_update_promisc(struct ieee80211com *);
+static int uath_config(struct uath_softc *, uint32_t, uint32_t);
+static int uath_config_multi(struct uath_softc *, uint32_t, const void *,
+ int);
+static int uath_switch_channel(struct uath_softc *,
+ struct ieee80211_channel *);
+static int uath_set_rxfilter(struct uath_softc *, uint32_t, uint32_t);
+static void uath_watchdog(void *);
+static void uath_abort_xfers(struct uath_softc *);
+static int uath_dataflush(struct uath_softc *);
+static int uath_cmdflush(struct uath_softc *);
+static int uath_flush(struct uath_softc *);
+static int uath_set_ledstate(struct uath_softc *, int);
+static int uath_set_chan(struct uath_softc *, struct ieee80211_channel *);
+static int uath_reset_tx_queues(struct uath_softc *);
+static int uath_wme_init(struct uath_softc *);
+static struct uath_data *
+ uath_getbuf(struct uath_softc *);
+static int uath_newstate(struct ieee80211vap *, enum ieee80211_state,
+ int);
+static int uath_set_key(struct uath_softc *,
+ const struct ieee80211_key *, int);
+static int uath_set_keys(struct uath_softc *, struct ieee80211vap *);
+static void uath_sysctl_node(struct uath_softc *);
+
+static int
+uath_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != UATH_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != UATH_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(uath_devs, sizeof(uath_devs), uaa));
+}
+
+static int
+uath_attach(device_t dev)
+{
+ struct uath_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t bands[IEEE80211_MODE_BYTES];
+ uint8_t iface_index = UATH_IFACE_INDEX; /* XXX */
+ usb_error_t error;
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+#ifdef UATH_DEBUG
+ sc->sc_debug = uath_debug;
+#endif
+ device_set_usb_desc(dev);
+
+ /*
+ * Only post-firmware devices here.
+ */
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
+ MTX_DEF);
+ callout_init(&sc->stat_ch, 0);
+ callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0);
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ uath_usbconfig, UATH_N_XFERS, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "could not allocate USB transfers, "
+ "err=%s\n", usbd_errstr(error));
+ goto fail;
+ }
+
+ sc->sc_cmd_dma_buf =
+ usbd_xfer_get_frame_buffer(sc->sc_xfer[UATH_INTR_TX], 0);
+ sc->sc_tx_dma_buf =
+ usbd_xfer_get_frame_buffer(sc->sc_xfer[UATH_BULK_TX], 0);
+
+ /*
+ * Setup buffers for firmware commands.
+ */
+ error = uath_alloc_cmd_list(sc, sc->sc_cmd);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not allocate Tx command list\n");
+ goto fail1;
+ }
+
+ /*
+ * We're now ready to send+receive firmware commands.
+ */
+ UATH_LOCK(sc);
+ error = uath_host_available(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not initialize adapter\n");
+ goto fail2;
+ }
+ error = uath_get_devcap(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not get device capabilities\n");
+ goto fail2;
+ }
+ UATH_UNLOCK(sc);
+
+ /* Create device sysctl node. */
+ uath_sysctl_node(sc);
+
+ UATH_LOCK(sc);
+ error = uath_get_devstatus(sc, ic->ic_macaddr);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not get device status\n");
+ goto fail2;
+ }
+
+ /*
+ * Allocate xfers for Rx/Tx data pipes.
+ */
+ error = uath_alloc_rx_data_list(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not allocate Rx data list\n");
+ goto fail2;
+ }
+ error = uath_alloc_tx_data_list(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not allocate Tx data list\n");
+ goto fail2;
+ }
+ UATH_UNLOCK(sc);
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(dev);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA | /* station mode */
+ IEEE80211_C_MONITOR | /* monitor mode supported */
+ IEEE80211_C_TXPMGT | /* tx power management */
+ IEEE80211_C_SHPREAMBLE | /* short preamble supported */
+ IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_WPA | /* 802.11i */
+ IEEE80211_C_BGSCAN | /* capable of bg scanning */
+ IEEE80211_C_TXFRAG; /* handle tx frags */
+
+ /* put a regulatory domain to reveal informations. */
+ uath_regdomain = sc->sc_devcap.regDomain;
+
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ if ((sc->sc_devcap.analog5GhzRevision & 0xf0) == 0x30)
+ setbit(bands, IEEE80211_MODE_11A);
+ /* XXX turbo */
+ ieee80211_init_channels(ic, NULL, bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_raw_xmit = uath_raw_xmit;
+ ic->ic_scan_start = uath_scan_start;
+ ic->ic_scan_end = uath_scan_end;
+ ic->ic_set_channel = uath_set_channel;
+ ic->ic_vap_create = uath_vap_create;
+ ic->ic_vap_delete = uath_vap_delete;
+ ic->ic_update_mcast = uath_update_mcast;
+ ic->ic_update_promisc = uath_update_promisc;
+ ic->ic_transmit = uath_transmit;
+ ic->ic_parent = uath_parent;
+
+ ieee80211_radiotap_attach(ic,
+ &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
+ UATH_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ UATH_RX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return (0);
+
+fail2: UATH_UNLOCK(sc);
+ uath_free_cmd_list(sc, sc->sc_cmd);
+fail1: usbd_transfer_unsetup(sc->sc_xfer, UATH_N_XFERS);
+fail:
+ return (error);
+}
+
+static int
+uath_detach(device_t dev)
+{
+ struct uath_softc *sc = device_get_softc(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ unsigned int x;
+
+ /*
+ * Prevent further allocations from RX/TX/CMD
+ * data lists and ioctls
+ */
+ UATH_LOCK(sc);
+ sc->sc_flags |= UATH_FLAG_INVALID;
+
+ STAILQ_INIT(&sc->sc_rx_active);
+ STAILQ_INIT(&sc->sc_rx_inactive);
+
+ STAILQ_INIT(&sc->sc_tx_active);
+ STAILQ_INIT(&sc->sc_tx_inactive);
+ STAILQ_INIT(&sc->sc_tx_pending);
+
+ STAILQ_INIT(&sc->sc_cmd_active);
+ STAILQ_INIT(&sc->sc_cmd_pending);
+ STAILQ_INIT(&sc->sc_cmd_waiting);
+ STAILQ_INIT(&sc->sc_cmd_inactive);
+
+ uath_stop(sc);
+ UATH_UNLOCK(sc);
+
+ callout_drain(&sc->stat_ch);
+ callout_drain(&sc->watchdog_ch);
+
+ /* drain USB transfers */
+ for (x = 0; x != UATH_N_XFERS; x++)
+ usbd_transfer_drain(sc->sc_xfer[x]);
+
+ /* free data buffers */
+ UATH_LOCK(sc);
+ uath_free_rx_data_list(sc);
+ uath_free_tx_data_list(sc);
+ uath_free_cmd_list(sc, sc->sc_cmd);
+ UATH_UNLOCK(sc);
+
+ /* free USB transfers and some data buffers */
+ usbd_transfer_unsetup(sc->sc_xfer, UATH_N_XFERS);
+
+ ieee80211_ifdetach(ic);
+ mbufq_drain(&sc->sc_snd);
+ mtx_destroy(&sc->sc_mtx);
+ return (0);
+}
+
+static void
+uath_free_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[])
+{
+ int i;
+
+ for (i = 0; i != UATH_CMD_LIST_COUNT; i++)
+ cmds[i].buf = NULL;
+}
+
+static int
+uath_alloc_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[])
+{
+ int i;
+
+ STAILQ_INIT(&sc->sc_cmd_active);
+ STAILQ_INIT(&sc->sc_cmd_pending);
+ STAILQ_INIT(&sc->sc_cmd_waiting);
+ STAILQ_INIT(&sc->sc_cmd_inactive);
+
+ for (i = 0; i != UATH_CMD_LIST_COUNT; i++) {
+ struct uath_cmd *cmd = &cmds[i];
+
+ cmd->sc = sc; /* backpointer for callbacks */
+ cmd->msgid = i;
+ cmd->buf = ((uint8_t *)sc->sc_cmd_dma_buf) +
+ (i * UATH_MAX_CMDSZ);
+ STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next);
+ UATH_STAT_INC(sc, st_cmd_inactive);
+ }
+ return (0);
+}
+
+static int
+uath_host_available(struct uath_softc *sc)
+{
+ struct uath_cmd_host_available setup;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ /* inform target the host is available */
+ setup.sw_ver_major = htobe32(ATH_SW_VER_MAJOR);
+ setup.sw_ver_minor = htobe32(ATH_SW_VER_MINOR);
+ setup.sw_ver_patch = htobe32(ATH_SW_VER_PATCH);
+ setup.sw_ver_build = htobe32(ATH_SW_VER_BUILD);
+ return uath_cmd_read(sc, WDCMSG_HOST_AVAILABLE,
+ &setup, sizeof setup, NULL, 0, 0);
+}
+
+#ifdef UATH_DEBUG
+static void
+uath_dump_cmd(const uint8_t *buf, int len, char prefix)
+{
+ const char *sep = "";
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if ((i % 16) == 0) {
+ printf("%s%c ", sep, prefix);
+ sep = "\n";
+ }
+ else if ((i % 4) == 0)
+ printf(" ");
+ printf("%02x", buf[i]);
+ }
+ printf("\n");
+}
+
+static const char *
+uath_codename(int code)
+{
+ static const char *names[] = {
+ "0x00",
+ "HOST_AVAILABLE",
+ "BIND",
+ "TARGET_RESET",
+ "TARGET_GET_CAPABILITY",
+ "TARGET_SET_CONFIG",
+ "TARGET_GET_STATUS",
+ "TARGET_GET_STATS",
+ "TARGET_START",
+ "TARGET_STOP",
+ "TARGET_ENABLE",
+ "TARGET_DISABLE",
+ "CREATE_CONNECTION",
+ "UPDATE_CONNECT_ATTR",
+ "DELETE_CONNECT",
+ "SEND",
+ "FLUSH",
+ "STATS_UPDATE",
+ "BMISS",
+ "DEVICE_AVAIL",
+ "SEND_COMPLETE",
+ "DATA_AVAIL",
+ "SET_PWR_MODE",
+ "BMISS_ACK",
+ "SET_LED_STEADY",
+ "SET_LED_BLINK",
+ "SETUP_BEACON_DESC",
+ "BEACON_INIT",
+ "RESET_KEY_CACHE",
+ "RESET_KEY_CACHE_ENTRY",
+ "SET_KEY_CACHE_ENTRY",
+ "SET_DECOMP_MASK",
+ "SET_REGULATORY_DOMAIN",
+ "SET_LED_STATE",
+ "WRITE_ASSOCID",
+ "SET_STA_BEACON_TIMERS",
+ "GET_TSF",
+ "RESET_TSF",
+ "SET_ADHOC_MODE",
+ "SET_BASIC_RATE",
+ "MIB_CONTROL",
+ "GET_CHANNEL_DATA",
+ "GET_CUR_RSSI",
+ "SET_ANTENNA_SWITCH",
+ "0x2c", "0x2d", "0x2e",
+ "USE_SHORT_SLOT_TIME",
+ "SET_POWER_MODE",
+ "SETUP_PSPOLL_DESC",
+ "SET_RX_MULTICAST_FILTER",
+ "RX_FILTER",
+ "PER_CALIBRATION",
+ "RESET",
+ "DISABLE",
+ "PHY_DISABLE",
+ "SET_TX_POWER_LIMIT",
+ "SET_TX_QUEUE_PARAMS",
+ "SETUP_TX_QUEUE",
+ "RELEASE_TX_QUEUE",
+ };
+ static char buf[8];
+
+ if (code < nitems(names))
+ return names[code];
+ if (code == WDCMSG_SET_DEFAULT_KEY)
+ return "SET_DEFAULT_KEY";
+ snprintf(buf, sizeof(buf), "0x%02x", code);
+ return buf;
+}
+#endif
+
+/*
+ * Low-level function to send read or write commands to the firmware.
+ */
+static int
+uath_cmdsend(struct uath_softc *sc, uint32_t code, const void *idata, int ilen,
+ void *odata, int olen, int flags)
+{
+ struct uath_cmd_hdr *hdr;
+ struct uath_cmd *cmd;
+ int error;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ /* grab a xfer */
+ cmd = uath_get_cmdbuf(sc);
+ if (cmd == NULL) {
+ device_printf(sc->sc_dev, "%s: empty inactive queue\n",
+ __func__);
+ return (ENOBUFS);
+ }
+ cmd->flags = flags;
+ /* always bulk-out a multiple of 4 bytes */
+ cmd->buflen = roundup2(sizeof(struct uath_cmd_hdr) + ilen, 4);
+
+ hdr = (struct uath_cmd_hdr *)cmd->buf;
+ memset(hdr, 0, sizeof(struct uath_cmd_hdr));
+ hdr->len = htobe32(cmd->buflen);
+ hdr->code = htobe32(code);
+ hdr->msgid = cmd->msgid; /* don't care about endianness */
+ hdr->magic = htobe32((cmd->flags & UATH_CMD_FLAG_MAGIC) ? 1 << 24 : 0);
+ memcpy((uint8_t *)(hdr + 1), idata, ilen);
+
+#ifdef UATH_DEBUG
+ if (sc->sc_debug & UATH_DEBUG_CMDS) {
+ printf("%s: send %s [flags 0x%x] olen %d\n",
+ __func__, uath_codename(code), cmd->flags, olen);
+ if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP)
+ uath_dump_cmd(cmd->buf, cmd->buflen, '+');
+ }
+#endif
+ cmd->odata = odata;
+ KASSERT(odata == NULL ||
+ olen < UATH_MAX_CMDSZ - sizeof(*hdr) + sizeof(uint32_t),
+ ("odata %p olen %u", odata, olen));
+ cmd->olen = olen;
+
+ STAILQ_INSERT_TAIL(&sc->sc_cmd_pending, cmd, next);
+ UATH_STAT_INC(sc, st_cmd_pending);
+ usbd_transfer_start(sc->sc_xfer[UATH_INTR_TX]);
+
+ if (cmd->flags & UATH_CMD_FLAG_READ) {
+ usbd_transfer_start(sc->sc_xfer[UATH_INTR_RX]);
+
+ /* wait at most two seconds for command reply */
+ error = mtx_sleep(cmd, &sc->sc_mtx, 0, "uathcmd", 2 * hz);
+ cmd->odata = NULL; /* in case reply comes too late */
+ if (error != 0) {
+ device_printf(sc->sc_dev, "timeout waiting for reply "
+ "to cmd 0x%x (%u)\n", code, code);
+ } else if (cmd->olen != olen) {
+ device_printf(sc->sc_dev, "unexpected reply data count "
+ "to cmd 0x%x (%u), got %u, expected %u\n",
+ code, code, cmd->olen, olen);
+ error = EINVAL;
+ }
+ return (error);
+ }
+ return (0);
+}
+
+static int
+uath_cmd_read(struct uath_softc *sc, uint32_t code, const void *idata,
+ int ilen, void *odata, int olen, int flags)
+{
+
+ flags |= UATH_CMD_FLAG_READ;
+ return uath_cmdsend(sc, code, idata, ilen, odata, olen, flags);
+}
+
+static int
+uath_cmd_write(struct uath_softc *sc, uint32_t code, const void *data, int len,
+ int flags)
+{
+
+ flags &= ~UATH_CMD_FLAG_READ;
+ return uath_cmdsend(sc, code, data, len, NULL, 0, flags);
+}
+
+static struct uath_cmd *
+uath_get_cmdbuf(struct uath_softc *sc)
+{
+ struct uath_cmd *uc;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ uc = STAILQ_FIRST(&sc->sc_cmd_inactive);
+ if (uc != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_cmd_inactive, next);
+ UATH_STAT_DEC(sc, st_cmd_inactive);
+ } else
+ uc = NULL;
+ if (uc == NULL)
+ DPRINTF(sc, UATH_DEBUG_XMIT, "%s: %s\n", __func__,
+ "out of command xmit buffers");
+ return (uc);
+}
+
+/*
+ * This function is called periodically (every second) when associated to
+ * query device statistics.
+ */
+static void
+uath_stat(void *arg)
+{
+ struct uath_softc *sc = arg;
+ int error;
+
+ UATH_LOCK(sc);
+ /*
+ * Send request for statistics asynchronously. The timer will be
+ * restarted when we'll get the stats notification.
+ */
+ error = uath_cmd_write(sc, WDCMSG_TARGET_GET_STATS, NULL, 0,
+ UATH_CMD_FLAG_ASYNC);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not query stats, error %d\n", error);
+ }
+ UATH_UNLOCK(sc);
+}
+
+static int
+uath_get_capability(struct uath_softc *sc, uint32_t cap, uint32_t *val)
+{
+ int error;
+
+ cap = htobe32(cap);
+ error = uath_cmd_read(sc, WDCMSG_TARGET_GET_CAPABILITY,
+ &cap, sizeof cap, val, sizeof(uint32_t), UATH_CMD_FLAG_MAGIC);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read capability %u\n",
+ be32toh(cap));
+ return (error);
+ }
+ *val = be32toh(*val);
+ return (error);
+}
+
+static int
+uath_get_devcap(struct uath_softc *sc)
+{
+#define GETCAP(x, v) do { \
+ error = uath_get_capability(sc, x, &v); \
+ if (error != 0) \
+ return (error); \
+ DPRINTF(sc, UATH_DEBUG_DEVCAP, \
+ "%s: %s=0x%08x\n", __func__, #x, v); \
+} while (0)
+ struct uath_devcap *cap = &sc->sc_devcap;
+ int error;
+
+ /* collect device capabilities */
+ GETCAP(CAP_TARGET_VERSION, cap->targetVersion);
+ GETCAP(CAP_TARGET_REVISION, cap->targetRevision);
+ GETCAP(CAP_MAC_VERSION, cap->macVersion);
+ GETCAP(CAP_MAC_REVISION, cap->macRevision);
+ GETCAP(CAP_PHY_REVISION, cap->phyRevision);
+ GETCAP(CAP_ANALOG_5GHz_REVISION, cap->analog5GhzRevision);
+ GETCAP(CAP_ANALOG_2GHz_REVISION, cap->analog2GhzRevision);
+
+ GETCAP(CAP_REG_DOMAIN, cap->regDomain);
+ GETCAP(CAP_REG_CAP_BITS, cap->regCapBits);
+#if 0
+ /* NB: not supported in rev 1.5 */
+ GETCAP(CAP_COUNTRY_CODE, cap->countryCode);
+#endif
+ GETCAP(CAP_WIRELESS_MODES, cap->wirelessModes);
+ GETCAP(CAP_CHAN_SPREAD_SUPPORT, cap->chanSpreadSupport);
+ GETCAP(CAP_COMPRESS_SUPPORT, cap->compressSupport);
+ GETCAP(CAP_BURST_SUPPORT, cap->burstSupport);
+ GETCAP(CAP_FAST_FRAMES_SUPPORT, cap->fastFramesSupport);
+ GETCAP(CAP_CHAP_TUNING_SUPPORT, cap->chapTuningSupport);
+ GETCAP(CAP_TURBOG_SUPPORT, cap->turboGSupport);
+ GETCAP(CAP_TURBO_PRIME_SUPPORT, cap->turboPrimeSupport);
+ GETCAP(CAP_DEVICE_TYPE, cap->deviceType);
+ GETCAP(CAP_WME_SUPPORT, cap->wmeSupport);
+ GETCAP(CAP_TOTAL_QUEUES, cap->numTxQueues);
+ GETCAP(CAP_CONNECTION_ID_MAX, cap->connectionIdMax);
+
+ GETCAP(CAP_LOW_5GHZ_CHAN, cap->low5GhzChan);
+ GETCAP(CAP_HIGH_5GHZ_CHAN, cap->high5GhzChan);
+ GETCAP(CAP_LOW_2GHZ_CHAN, cap->low2GhzChan);
+ GETCAP(CAP_HIGH_2GHZ_CHAN, cap->high2GhzChan);
+ GETCAP(CAP_TWICE_ANTENNAGAIN_5G, cap->twiceAntennaGain5G);
+ GETCAP(CAP_TWICE_ANTENNAGAIN_2G, cap->twiceAntennaGain2G);
+
+ GETCAP(CAP_CIPHER_AES_CCM, cap->supportCipherAES_CCM);
+ GETCAP(CAP_CIPHER_TKIP, cap->supportCipherTKIP);
+ GETCAP(CAP_MIC_TKIP, cap->supportMicTKIP);
+
+ cap->supportCipherWEP = 1; /* NB: always available */
+
+ return (0);
+}
+
+static int
+uath_get_devstatus(struct uath_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+ int error;
+
+ /* retrieve MAC address */
+ error = uath_get_status(sc, ST_MAC_ADDR, macaddr, IEEE80211_ADDR_LEN);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read MAC address\n");
+ return (error);
+ }
+
+ error = uath_get_status(sc, ST_SERIAL_NUMBER,
+ &sc->sc_serial[0], sizeof(sc->sc_serial));
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not read device serial number\n");
+ return (error);
+ }
+ return (0);
+}
+
+static int
+uath_get_status(struct uath_softc *sc, uint32_t which, void *odata, int olen)
+{
+ int error;
+
+ which = htobe32(which);
+ error = uath_cmd_read(sc, WDCMSG_TARGET_GET_STATUS,
+ &which, sizeof(which), odata, olen, UATH_CMD_FLAG_MAGIC);
+ if (error != 0)
+ device_printf(sc->sc_dev,
+ "could not read EEPROM offset 0x%02x\n", be32toh(which));
+ return (error);
+}
+
+static void
+uath_free_data_list(struct uath_softc *sc, struct uath_data data[], int ndata,
+ int fillmbuf)
+{
+ int i;
+
+ for (i = 0; i < ndata; i++) {
+ struct uath_data *dp = &data[i];
+
+ if (fillmbuf == 1) {
+ if (dp->m != NULL) {
+ m_freem(dp->m);
+ dp->m = NULL;
+ dp->buf = NULL;
+ }
+ } else {
+ dp->buf = NULL;
+ }
+ if (dp->ni != NULL) {
+ ieee80211_free_node(dp->ni);
+ dp->ni = NULL;
+ }
+ }
+}
+
+static int
+uath_alloc_data_list(struct uath_softc *sc, struct uath_data data[],
+ int ndata, int maxsz, void *dma_buf)
+{
+ int i, error;
+
+ for (i = 0; i < ndata; i++) {
+ struct uath_data *dp = &data[i];
+
+ dp->sc = sc;
+ if (dma_buf == NULL) {
+ /* XXX check maxsz */
+ dp->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (dp->m == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ dp->buf = mtod(dp->m, uint8_t *);
+ } else {
+ dp->m = NULL;
+ dp->buf = ((uint8_t *)dma_buf) + (i * maxsz);
+ }
+ dp->ni = NULL;
+ }
+
+ return (0);
+
+fail: uath_free_data_list(sc, data, ndata, 1 /* free mbufs */);
+ return (error);
+}
+
+static int
+uath_alloc_rx_data_list(struct uath_softc *sc)
+{
+ int error, i;
+
+ /* XXX is it enough to store the RX packet with MCLBYTES bytes? */
+ error = uath_alloc_data_list(sc,
+ sc->sc_rx, UATH_RX_DATA_LIST_COUNT, MCLBYTES,
+ NULL /* setup mbufs */);
+ if (error != 0)
+ return (error);
+
+ STAILQ_INIT(&sc->sc_rx_active);
+ STAILQ_INIT(&sc->sc_rx_inactive);
+
+ for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
+ STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i],
+ next);
+ UATH_STAT_INC(sc, st_rx_inactive);
+ }
+
+ return (0);
+}
+
+static int
+uath_alloc_tx_data_list(struct uath_softc *sc)
+{
+ int error, i;
+
+ error = uath_alloc_data_list(sc,
+ sc->sc_tx, UATH_TX_DATA_LIST_COUNT, UATH_MAX_TXBUFSZ,
+ sc->sc_tx_dma_buf);
+ if (error != 0)
+ return (error);
+
+ STAILQ_INIT(&sc->sc_tx_active);
+ STAILQ_INIT(&sc->sc_tx_inactive);
+ STAILQ_INIT(&sc->sc_tx_pending);
+
+ for (i = 0; i < UATH_TX_DATA_LIST_COUNT; i++) {
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i],
+ next);
+ UATH_STAT_INC(sc, st_tx_inactive);
+ }
+
+ return (0);
+}
+
+static void
+uath_free_rx_data_list(struct uath_softc *sc)
+{
+ uath_free_data_list(sc, sc->sc_rx, UATH_RX_DATA_LIST_COUNT,
+ 1 /* free mbufs */);
+}
+
+static void
+uath_free_tx_data_list(struct uath_softc *sc)
+{
+ uath_free_data_list(sc, sc->sc_tx, UATH_TX_DATA_LIST_COUNT,
+ 0 /* no mbufs */);
+}
+
+static struct ieee80211vap *
+uath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct uath_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return (NULL);
+ uvp = malloc(sizeof(struct uath_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) {
+ /* out of memory */
+ free(uvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = uath_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status, mac);
+ ic->ic_opmode = opmode;
+ return (vap);
+}
+
+static void
+uath_vap_delete(struct ieee80211vap *vap)
+{
+ struct uath_vap *uvp = UATH_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static int
+uath_init(struct uath_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t val;
+ int error;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ if (sc->sc_flags & UATH_FLAG_INITDONE)
+ uath_stop(sc);
+
+ /* reset variables */
+ sc->sc_intrx_nextnum = sc->sc_msgid = 0;
+
+ val = htobe32(0);
+ uath_cmd_write(sc, WDCMSG_BIND, &val, sizeof val, 0);
+
+ /* set MAC address */
+ uath_config_multi(sc, CFG_MAC_ADDR,
+ vap ? vap->iv_myaddr : ic->ic_macaddr, IEEE80211_ADDR_LEN);
+
+ /* XXX honor net80211 state */
+ uath_config(sc, CFG_RATE_CONTROL_ENABLE, 0x00000001);
+ uath_config(sc, CFG_DIVERSITY_CTL, 0x00000001);
+ uath_config(sc, CFG_ABOLT, 0x0000003f);
+ uath_config(sc, CFG_WME_ENABLED, 0x00000001);
+
+ uath_config(sc, CFG_SERVICE_TYPE, 1);
+ uath_config(sc, CFG_TP_SCALE, 0x00000000);
+ uath_config(sc, CFG_TPC_HALF_DBM5, 0x0000003c);
+ uath_config(sc, CFG_TPC_HALF_DBM2, 0x0000003c);
+ uath_config(sc, CFG_OVERRD_TX_POWER, 0x00000000);
+ uath_config(sc, CFG_GMODE_PROTECTION, 0x00000000);
+ uath_config(sc, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003);
+ uath_config(sc, CFG_PROTECTION_TYPE, 0x00000000);
+ uath_config(sc, CFG_MODE_CTS, 0x00000002);
+
+ error = uath_cmd_read(sc, WDCMSG_TARGET_START, NULL, 0,
+ &val, sizeof(val), UATH_CMD_FLAG_MAGIC);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not start target, error %d\n", error);
+ goto fail;
+ }
+ DPRINTF(sc, UATH_DEBUG_INIT, "%s returns handle: 0x%x\n",
+ uath_codename(WDCMSG_TARGET_START), be32toh(val));
+
+ /* set default channel */
+ error = uath_switch_channel(sc, ic->ic_curchan);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not switch channel, error %d\n", error);
+ goto fail;
+ }
+
+ val = htobe32(TARGET_DEVICE_AWAKE);
+ uath_cmd_write(sc, WDCMSG_SET_PWR_MODE, &val, sizeof val, 0);
+ /* XXX? check */
+ uath_cmd_write(sc, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0);
+
+ usbd_transfer_start(sc->sc_xfer[UATH_BULK_RX]);
+ /* enable Rx */
+ uath_set_rxfilter(sc, 0x0, UATH_FILTER_OP_INIT);
+ uath_set_rxfilter(sc,
+ UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
+ UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON,
+ UATH_FILTER_OP_SET);
+
+ sc->sc_flags |= UATH_FLAG_INITDONE;
+
+ callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc);
+
+ return (0);
+
+fail:
+ uath_stop(sc);
+ return (error);
+}
+
+static void
+uath_stop(struct uath_softc *sc)
+{
+
+ UATH_ASSERT_LOCKED(sc);
+
+ sc->sc_flags &= ~UATH_FLAG_INITDONE;
+
+ callout_stop(&sc->stat_ch);
+ callout_stop(&sc->watchdog_ch);
+ sc->sc_tx_timer = 0;
+ /* abort pending transmits */
+ uath_abort_xfers(sc);
+ /* flush data & control requests into the target */
+ (void)uath_flush(sc);
+ /* set a LED status to the disconnected. */
+ uath_set_ledstate(sc, 0);
+ /* stop the target */
+ uath_cmd_write(sc, WDCMSG_TARGET_STOP, NULL, 0, 0);
+}
+
+static int
+uath_config(struct uath_softc *sc, uint32_t reg, uint32_t val)
+{
+ struct uath_write_mac write;
+ int error;
+
+ write.reg = htobe32(reg);
+ write.len = htobe32(0); /* 0 = single write */
+ *(uint32_t *)write.data = htobe32(val);
+
+ error = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write,
+ 3 * sizeof (uint32_t), 0);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not write register 0x%02x\n",
+ reg);
+ }
+ return (error);
+}
+
+static int
+uath_config_multi(struct uath_softc *sc, uint32_t reg, const void *data,
+ int len)
+{
+ struct uath_write_mac write;
+ int error;
+
+ write.reg = htobe32(reg);
+ write.len = htobe32(len);
+ bcopy(data, write.data, len);
+
+ /* properly handle the case where len is zero (reset) */
+ error = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write,
+ (len == 0) ? sizeof (uint32_t) : 2 * sizeof (uint32_t) + len, 0);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not write %d bytes to register 0x%02x\n", len, reg);
+ }
+ return (error);
+}
+
+static int
+uath_switch_channel(struct uath_softc *sc, struct ieee80211_channel *c)
+{
+ int error;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ /* set radio frequency */
+ error = uath_set_chan(sc, c);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not set channel, error %d\n", error);
+ goto failed;
+ }
+ /* reset Tx rings */
+ error = uath_reset_tx_queues(sc);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not reset Tx queues, error %d\n", error);
+ goto failed;
+ }
+ /* set Tx rings WME properties */
+ error = uath_wme_init(sc);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not init Tx queues, error %d\n", error);
+ goto failed;
+ }
+ error = uath_set_ledstate(sc, 0);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not set led state, error %d\n", error);
+ goto failed;
+ }
+ error = uath_flush(sc);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not flush pipes, error %d\n", error);
+ goto failed;
+ }
+failed:
+ return (error);
+}
+
+static int
+uath_set_rxfilter(struct uath_softc *sc, uint32_t bits, uint32_t op)
+{
+ struct uath_cmd_rx_filter rxfilter;
+
+ rxfilter.bits = htobe32(bits);
+ rxfilter.op = htobe32(op);
+
+ DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL,
+ "setting Rx filter=0x%x flags=0x%x\n", bits, op);
+ return uath_cmd_write(sc, WDCMSG_RX_FILTER, &rxfilter,
+ sizeof rxfilter, 0);
+}
+
+static void
+uath_watchdog(void *arg)
+{
+ struct uath_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ if (sc->sc_tx_timer > 0) {
+ if (--sc->sc_tx_timer == 0) {
+ device_printf(sc->sc_dev, "device timeout\n");
+ /*uath_init(sc); XXX needs a process context! */
+ counter_u64_add(ic->ic_oerrors, 1);
+ return;
+ }
+ callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc);
+ }
+}
+
+static void
+uath_abort_xfers(struct uath_softc *sc)
+{
+ int i;
+
+ UATH_ASSERT_LOCKED(sc);
+ /* abort any pending transfers */
+ for (i = 0; i < UATH_N_XFERS; i++)
+ usbd_transfer_stop(sc->sc_xfer[i]);
+}
+
+static int
+uath_flush(struct uath_softc *sc)
+{
+ int error;
+
+ error = uath_dataflush(sc);
+ if (error != 0)
+ goto failed;
+
+ error = uath_cmdflush(sc);
+ if (error != 0)
+ goto failed;
+
+failed:
+ return (error);
+}
+
+static int
+uath_cmdflush(struct uath_softc *sc)
+{
+
+ return uath_cmd_write(sc, WDCMSG_FLUSH, NULL, 0, 0);
+}
+
+static int
+uath_dataflush(struct uath_softc *sc)
+{
+ struct uath_data *data;
+ struct uath_chunk *chunk;
+ struct uath_tx_desc *desc;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ data = uath_getbuf(sc);
+ if (data == NULL)
+ return (ENOBUFS);
+ data->buflen = sizeof(struct uath_chunk) + sizeof(struct uath_tx_desc);
+ data->m = NULL;
+ data->ni = NULL;
+ chunk = (struct uath_chunk *)data->buf;
+ desc = (struct uath_tx_desc *)(chunk + 1);
+
+ /* one chunk only */
+ chunk->seqnum = 0;
+ chunk->flags = UATH_CFLAGS_FINAL;
+ chunk->length = htobe16(sizeof (struct uath_tx_desc));
+
+ memset(desc, 0, sizeof(struct uath_tx_desc));
+ desc->msglen = htobe32(sizeof(struct uath_tx_desc));
+ desc->msgid = (sc->sc_msgid++) + 1; /* don't care about endianness */
+ desc->type = htobe32(WDCMSG_FLUSH);
+ desc->txqid = htobe32(0);
+ desc->connid = htobe32(0);
+ desc->flags = htobe32(0);
+
+#ifdef UATH_DEBUG
+ if (sc->sc_debug & UATH_DEBUG_CMDS) {
+ DPRINTF(sc, UATH_DEBUG_RESET, "send flush ix %d\n",
+ desc->msgid);
+ if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP)
+ uath_dump_cmd(data->buf, data->buflen, '+');
+ }
+#endif
+
+ STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next);
+ UATH_STAT_INC(sc, st_tx_pending);
+ sc->sc_tx_timer = 5;
+ usbd_transfer_start(sc->sc_xfer[UATH_BULK_TX]);
+
+ return (0);
+}
+
+static struct uath_data *
+_uath_getbuf(struct uath_softc *sc)
+{
+ struct uath_data *bf;
+
+ bf = STAILQ_FIRST(&sc->sc_tx_inactive);
+ if (bf != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
+ UATH_STAT_DEC(sc, st_tx_inactive);
+ } else
+ bf = NULL;
+ if (bf == NULL)
+ DPRINTF(sc, UATH_DEBUG_XMIT, "%s: %s\n", __func__,
+ "out of xmit buffers");
+ return (bf);
+}
+
+static struct uath_data *
+uath_getbuf(struct uath_softc *sc)
+{
+ struct uath_data *bf;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ bf = _uath_getbuf(sc);
+ if (bf == NULL)
+ DPRINTF(sc, UATH_DEBUG_XMIT, "%s: stop queue\n", __func__);
+ return (bf);
+}
+
+static int
+uath_set_ledstate(struct uath_softc *sc, int connected)
+{
+
+ DPRINTF(sc, UATH_DEBUG_LED,
+ "set led state %sconnected\n", connected ? "" : "!");
+ connected = htobe32(connected);
+ return uath_cmd_write(sc, WDCMSG_SET_LED_STATE,
+ &connected, sizeof connected, 0);
+}
+
+static int
+uath_set_chan(struct uath_softc *sc, struct ieee80211_channel *c)
+{
+#ifdef UATH_DEBUG
+ struct ieee80211com *ic = &sc->sc_ic;
+#endif
+ struct uath_cmd_reset reset;
+
+ memset(&reset, 0, sizeof(reset));
+ if (IEEE80211_IS_CHAN_2GHZ(c))
+ reset.flags |= htobe32(UATH_CHAN_2GHZ);
+ if (IEEE80211_IS_CHAN_5GHZ(c))
+ reset.flags |= htobe32(UATH_CHAN_5GHZ);
+ /* NB: 11g =>'s 11b so don't specify both OFDM and CCK */
+ if (IEEE80211_IS_CHAN_OFDM(c))
+ reset.flags |= htobe32(UATH_CHAN_OFDM);
+ else if (IEEE80211_IS_CHAN_CCK(c))
+ reset.flags |= htobe32(UATH_CHAN_CCK);
+ /* turbo can be used in either 2GHz or 5GHz */
+ if (c->ic_flags & IEEE80211_CHAN_TURBO)
+ reset.flags |= htobe32(UATH_CHAN_TURBO);
+ reset.freq = htobe32(c->ic_freq);
+ reset.maxrdpower = htobe32(50); /* XXX */
+ reset.channelchange = htobe32(1);
+ reset.keeprccontent = htobe32(0);
+
+ DPRINTF(sc, UATH_DEBUG_CHANNEL, "set channel %d, flags 0x%x freq %u\n",
+ ieee80211_chan2ieee(ic, c),
+ be32toh(reset.flags), be32toh(reset.freq));
+ return uath_cmd_write(sc, WDCMSG_RESET, &reset, sizeof reset, 0);
+}
+
+static int
+uath_reset_tx_queues(struct uath_softc *sc)
+{
+ int ac, error;
+
+ DPRINTF(sc, UATH_DEBUG_RESET, "%s: reset Tx queues\n", __func__);
+ for (ac = 0; ac < 4; ac++) {
+ const uint32_t qid = htobe32(ac);
+
+ error = uath_cmd_write(sc, WDCMSG_RELEASE_TX_QUEUE, &qid,
+ sizeof qid, 0);
+ if (error != 0)
+ break;
+ }
+ return (error);
+}
+
+static int
+uath_wme_init(struct uath_softc *sc)
+{
+ /* XXX get from net80211 */
+ static const struct uath_wme_settings uath_wme_11g[4] = {
+ { 7, 4, 10, 0, 0 }, /* Background */
+ { 3, 4, 10, 0, 0 }, /* Best-Effort */
+ { 3, 3, 4, 26, 0 }, /* Video */
+ { 2, 2, 3, 47, 0 } /* Voice */
+ };
+ struct uath_cmd_txq_setup qinfo;
+ int ac, error;
+
+ DPRINTF(sc, UATH_DEBUG_WME, "%s: setup Tx queues\n", __func__);
+ for (ac = 0; ac < 4; ac++) {
+ qinfo.qid = htobe32(ac);
+ qinfo.len = htobe32(sizeof(qinfo.attr));
+ qinfo.attr.priority = htobe32(ac); /* XXX */
+ qinfo.attr.aifs = htobe32(uath_wme_11g[ac].aifsn);
+ qinfo.attr.logcwmin = htobe32(uath_wme_11g[ac].logcwmin);
+ qinfo.attr.logcwmax = htobe32(uath_wme_11g[ac].logcwmax);
+ qinfo.attr.bursttime = htobe32(IEEE80211_TXOP_TO_US(
+ uath_wme_11g[ac].txop));
+ qinfo.attr.mode = htobe32(uath_wme_11g[ac].acm);/*XXX? */
+ qinfo.attr.qflags = htobe32(1); /* XXX? */
+
+ error = uath_cmd_write(sc, WDCMSG_SETUP_TX_QUEUE, &qinfo,
+ sizeof qinfo, 0);
+ if (error != 0)
+ break;
+ }
+ return (error);
+}
+
+static void
+uath_parent(struct ieee80211com *ic)
+{
+ struct uath_softc *sc = ic->ic_softc;
+ int startall = 0;
+
+ UATH_LOCK(sc);
+ if (sc->sc_flags & UATH_FLAG_INVALID) {
+ UATH_UNLOCK(sc);
+ return;
+ }
+
+ if (ic->ic_nrunning > 0) {
+ if (!(sc->sc_flags & UATH_FLAG_INITDONE)) {
+ uath_init(sc);
+ startall = 1;
+ }
+ } else if (sc->sc_flags & UATH_FLAG_INITDONE)
+ uath_stop(sc);
+ UATH_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+}
+
+static int
+uath_tx_start(struct uath_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
+ struct uath_data *data)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct uath_chunk *chunk;
+ struct uath_tx_desc *desc;
+ const struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
+ int framelen, msglen;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ data->ni = ni;
+ data->m = m0;
+ chunk = (struct uath_chunk *)data->buf;
+ desc = (struct uath_tx_desc *)(chunk + 1);
+
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct uath_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ if (m0->m_flags & M_FRAG)
+ tap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
+
+ ieee80211_radiotap_tx(vap, m0);
+ }
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(desc + 1));
+
+ framelen = m0->m_pkthdr.len + IEEE80211_CRC_LEN;
+ msglen = framelen + sizeof (struct uath_tx_desc);
+ data->buflen = msglen + sizeof (struct uath_chunk);
+
+ /* one chunk only for now */
+ chunk->seqnum = sc->sc_seqnum++;
+ chunk->flags = (m0->m_flags & M_FRAG) ? 0 : UATH_CFLAGS_FINAL;
+ if (m0->m_flags & M_LASTFRAG)
+ chunk->flags |= UATH_CFLAGS_FINAL;
+ chunk->flags = UATH_CFLAGS_FINAL;
+ chunk->length = htobe16(msglen);
+
+ /* fill Tx descriptor */
+ desc->msglen = htobe32(msglen);
+ /* NB: to get UATH_TX_NOTIFY reply, `msgid' must be larger than 0 */
+ desc->msgid = (sc->sc_msgid++) + 1; /* don't care about endianness */
+ desc->type = htobe32(WDCMSG_SEND);
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_CTL:
+ case IEEE80211_FC0_TYPE_MGT:
+ /* NB: force all management frames to highest queue */
+ if (ni->ni_flags & IEEE80211_NODE_QOS) {
+ /* NB: force all management frames to highest queue */
+ desc->txqid = htobe32(WME_AC_VO | UATH_TXQID_MINRATE);
+ } else
+ desc->txqid = htobe32(WME_AC_BE | UATH_TXQID_MINRATE);
+ break;
+ case IEEE80211_FC0_TYPE_DATA:
+ /* XXX multicast frames should honor mcastrate */
+ desc->txqid = htobe32(M_WME_GETAC(m0));
+ break;
+ default:
+ device_printf(sc->sc_dev, "bogus frame type 0x%x (%s)\n",
+ wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
+ m_freem(m0);
+ return (EIO);
+ }
+ if (vap->iv_state == IEEE80211_S_AUTH ||
+ vap->iv_state == IEEE80211_S_ASSOC ||
+ vap->iv_state == IEEE80211_S_RUN)
+ desc->connid = htobe32(UATH_ID_BSS);
+ else
+ desc->connid = htobe32(UATH_ID_INVALID);
+ desc->flags = htobe32(0 /* no UATH_TX_NOTIFY */);
+ desc->buflen = htobe32(m0->m_pkthdr.len);
+
+#ifdef UATH_DEBUG
+ DPRINTF(sc, UATH_DEBUG_XMIT,
+ "send frame ix %u framelen %d msglen %d connid 0x%x txqid 0x%x\n",
+ desc->msgid, framelen, msglen, be32toh(desc->connid),
+ be32toh(desc->txqid));
+ if (sc->sc_debug & UATH_DEBUG_XMIT_DUMP)
+ uath_dump_cmd(data->buf, data->buflen, '+');
+#endif
+
+ STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next);
+ UATH_STAT_INC(sc, st_tx_pending);
+ usbd_transfer_start(sc->sc_xfer[UATH_BULK_TX]);
+
+ return (0);
+}
+
+/*
+ * Cleanup driver resources when we run out of buffers while processing
+ * fragments; return the tx buffers allocated and drop node references.
+ */
+static void
+uath_txfrag_cleanup(struct uath_softc *sc,
+ uath_datahead *frags, struct ieee80211_node *ni)
+{
+ struct uath_data *bf, *next;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ STAILQ_FOREACH_SAFE(bf, frags, next, next) {
+ /* NB: bf assumed clean */
+ STAILQ_REMOVE_HEAD(frags, next);
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
+ UATH_STAT_INC(sc, st_tx_inactive);
+ ieee80211_node_decref(ni);
+ }
+}
+
+/*
+ * Setup xmit of a fragmented frame. Allocate a buffer for each frag and bump
+ * the node reference count to reflect the held reference to be setup by
+ * uath_tx_start.
+ */
+static int
+uath_txfrag_setup(struct uath_softc *sc, uath_datahead *frags,
+ struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct mbuf *m;
+ struct uath_data *bf;
+
+ UATH_ASSERT_LOCKED(sc);
+ for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) {
+ bf = uath_getbuf(sc);
+ if (bf == NULL) { /* out of buffers, cleanup */
+ uath_txfrag_cleanup(sc, frags, ni);
+ break;
+ }
+ ieee80211_node_incref(ni);
+ STAILQ_INSERT_TAIL(frags, bf, next);
+ }
+
+ return !STAILQ_EMPTY(frags);
+}
+
+static int
+uath_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct uath_softc *sc = ic->ic_softc;
+ int error;
+
+ UATH_LOCK(sc);
+ if ((sc->sc_flags & UATH_FLAG_INITDONE) == 0) {
+ UATH_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ UATH_UNLOCK(sc);
+ return (error);
+ }
+ uath_start(sc);
+ UATH_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+uath_start(struct uath_softc *sc)
+{
+ struct uath_data *bf;
+ struct ieee80211_node *ni;
+ struct mbuf *m, *next;
+ uath_datahead frags;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ if ((sc->sc_flags & UATH_FLAG_INITDONE) == 0 ||
+ (sc->sc_flags & UATH_FLAG_INVALID))
+ return;
+
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ bf = uath_getbuf(sc);
+ if (bf == NULL) {
+ mbufq_prepend(&sc->sc_snd, m);
+ break;
+ }
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+
+ /*
+ * Check for fragmentation. If this frame has been broken up
+ * verify we have enough buffers to send all the fragments
+ * so all go out or none...
+ */
+ STAILQ_INIT(&frags);
+ if ((m->m_flags & M_FRAG) &&
+ !uath_txfrag_setup(sc, &frags, m, ni)) {
+ DPRINTF(sc, UATH_DEBUG_XMIT,
+ "%s: out of txfrag buffers\n", __func__);
+ ieee80211_free_mbuf(m);
+ goto bad;
+ }
+ sc->sc_seqnum = 0;
+ nextfrag:
+ /*
+ * Pass the frame to the h/w for transmission.
+ * Fragmented frames have each frag chained together
+ * with m_nextpkt. We know there are sufficient uath_data's
+ * to send all the frags because of work done by
+ * uath_txfrag_setup.
+ */
+ next = m->m_nextpkt;
+ if (uath_tx_start(sc, m, ni, bf) != 0) {
+ bad:
+ if_inc_counter(ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ reclaim:
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
+ UATH_STAT_INC(sc, st_tx_inactive);
+ uath_txfrag_cleanup(sc, &frags, ni);
+ ieee80211_free_node(ni);
+ continue;
+ }
+
+ if (next != NULL) {
+ /*
+ * Beware of state changing between frags.
+ XXX check sta power-save state?
+ */
+ if (ni->ni_vap->iv_state != IEEE80211_S_RUN) {
+ DPRINTF(sc, UATH_DEBUG_XMIT,
+ "%s: flush fragmented packet, state %s\n",
+ __func__,
+ ieee80211_state_name[ni->ni_vap->iv_state]);
+ ieee80211_free_mbuf(next);
+ goto reclaim;
+ }
+ m = next;
+ bf = STAILQ_FIRST(&frags);
+ KASSERT(bf != NULL, ("no buf for txfrag"));
+ STAILQ_REMOVE_HEAD(&frags, next);
+ goto nextfrag;
+ }
+
+ sc->sc_tx_timer = 5;
+ }
+}
+
+static int
+uath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct uath_data *bf;
+ struct uath_softc *sc = ic->ic_softc;
+
+ UATH_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if ((sc->sc_flags & UATH_FLAG_INVALID) ||
+ !(sc->sc_flags & UATH_FLAG_INITDONE)) {
+ m_freem(m);
+ UATH_UNLOCK(sc);
+ return (ENETDOWN);
+ }
+
+ /* grab a TX buffer */
+ bf = uath_getbuf(sc);
+ if (bf == NULL) {
+ m_freem(m);
+ UATH_UNLOCK(sc);
+ return (ENOBUFS);
+ }
+
+ sc->sc_seqnum = 0;
+ if (uath_tx_start(sc, m, ni, bf) != 0) {
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
+ UATH_STAT_INC(sc, st_tx_inactive);
+ UATH_UNLOCK(sc);
+ return (EIO);
+ }
+ UATH_UNLOCK(sc);
+
+ sc->sc_tx_timer = 5;
+ return (0);
+}
+
+static void
+uath_scan_start(struct ieee80211com *ic)
+{
+ /* do nothing */
+}
+
+static void
+uath_scan_end(struct ieee80211com *ic)
+{
+ /* do nothing */
+}
+
+static void
+uath_set_channel(struct ieee80211com *ic)
+{
+ struct uath_softc *sc = ic->ic_softc;
+
+ UATH_LOCK(sc);
+ if ((sc->sc_flags & UATH_FLAG_INVALID) ||
+ (sc->sc_flags & UATH_FLAG_INITDONE) == 0) {
+ UATH_UNLOCK(sc);
+ return;
+ }
+ (void)uath_switch_channel(sc, ic->ic_curchan);
+ UATH_UNLOCK(sc);
+}
+
+static int
+uath_set_rxmulti_filter(struct uath_softc *sc)
+{
+ /* XXX broken */
+ return (0);
+}
+static void
+uath_update_mcast(struct ieee80211com *ic)
+{
+ struct uath_softc *sc = ic->ic_softc;
+
+ UATH_LOCK(sc);
+ if ((sc->sc_flags & UATH_FLAG_INVALID) ||
+ (sc->sc_flags & UATH_FLAG_INITDONE) == 0) {
+ UATH_UNLOCK(sc);
+ return;
+ }
+ /*
+ * this is for avoiding the race condition when we're try to
+ * connect to the AP with WPA.
+ */
+ if (sc->sc_flags & UATH_FLAG_INITDONE)
+ (void)uath_set_rxmulti_filter(sc);
+ UATH_UNLOCK(sc);
+}
+
+static void
+uath_update_promisc(struct ieee80211com *ic)
+{
+ struct uath_softc *sc = ic->ic_softc;
+
+ UATH_LOCK(sc);
+ if ((sc->sc_flags & UATH_FLAG_INVALID) ||
+ (sc->sc_flags & UATH_FLAG_INITDONE) == 0) {
+ UATH_UNLOCK(sc);
+ return;
+ }
+ if (sc->sc_flags & UATH_FLAG_INITDONE) {
+ uath_set_rxfilter(sc,
+ UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST |
+ UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON |
+ UATH_FILTER_RX_PROM, UATH_FILTER_OP_SET);
+ }
+ UATH_UNLOCK(sc);
+}
+
+static int
+uath_create_connection(struct uath_softc *sc, uint32_t connid)
+{
+ const struct ieee80211_rateset *rs;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni;
+ struct uath_cmd_create_connection create;
+
+ ni = ieee80211_ref_node(vap->iv_bss);
+ memset(&create, 0, sizeof(create));
+ create.connid = htobe32(connid);
+ create.bssid = htobe32(0);
+ /* XXX packed or not? */
+ create.size = htobe32(sizeof(struct uath_cmd_rateset));
+
+ rs = &ni->ni_rates;
+ create.connattr.rateset.length = rs->rs_nrates;
+ bcopy(rs->rs_rates, &create.connattr.rateset.set[0],
+ rs->rs_nrates);
+
+ /* XXX turbo */
+ if (IEEE80211_IS_CHAN_A(ni->ni_chan))
+ create.connattr.wlanmode = htobe32(WLAN_MODE_11a);
+ else if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan))
+ create.connattr.wlanmode = htobe32(WLAN_MODE_11g);
+ else
+ create.connattr.wlanmode = htobe32(WLAN_MODE_11b);
+ ieee80211_free_node(ni);
+
+ return uath_cmd_write(sc, WDCMSG_CREATE_CONNECTION, &create,
+ sizeof create, 0);
+}
+
+static int
+uath_set_rates(struct uath_softc *sc, const struct ieee80211_rateset *rs)
+{
+ struct uath_cmd_rates rates;
+
+ memset(&rates, 0, sizeof(rates));
+ rates.connid = htobe32(UATH_ID_BSS); /* XXX */
+ rates.size = htobe32(sizeof(struct uath_cmd_rateset));
+ /* XXX bounds check rs->rs_nrates */
+ rates.rateset.length = rs->rs_nrates;
+ bcopy(rs->rs_rates, &rates.rateset.set[0], rs->rs_nrates);
+
+ DPRINTF(sc, UATH_DEBUG_RATES,
+ "setting supported rates nrates=%d\n", rs->rs_nrates);
+ return uath_cmd_write(sc, WDCMSG_SET_BASIC_RATE,
+ &rates, sizeof rates, 0);
+}
+
+static int
+uath_write_associd(struct uath_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni;
+ struct uath_cmd_set_associd associd;
+
+ ni = ieee80211_ref_node(vap->iv_bss);
+ memset(&associd, 0, sizeof(associd));
+ associd.defaultrateix = htobe32(1); /* XXX */
+ associd.associd = htobe32(ni->ni_associd);
+ associd.timoffset = htobe32(0x3b); /* XXX */
+ IEEE80211_ADDR_COPY(associd.bssid, ni->ni_bssid);
+ ieee80211_free_node(ni);
+ return uath_cmd_write(sc, WDCMSG_WRITE_ASSOCID, &associd,
+ sizeof associd, 0);
+}
+
+static int
+uath_set_ledsteady(struct uath_softc *sc, int lednum, int ledmode)
+{
+ struct uath_cmd_ledsteady led;
+
+ led.lednum = htobe32(lednum);
+ led.ledmode = htobe32(ledmode);
+
+ DPRINTF(sc, UATH_DEBUG_LED, "set %s led %s (steady)\n",
+ (lednum == UATH_LED_LINK) ? "link" : "activity",
+ ledmode ? "on" : "off");
+ return uath_cmd_write(sc, WDCMSG_SET_LED_STEADY, &led, sizeof led, 0);
+}
+
+static int
+uath_set_ledblink(struct uath_softc *sc, int lednum, int ledmode,
+ int blinkrate, int slowmode)
+{
+ struct uath_cmd_ledblink led;
+
+ led.lednum = htobe32(lednum);
+ led.ledmode = htobe32(ledmode);
+ led.blinkrate = htobe32(blinkrate);
+ led.slowmode = htobe32(slowmode);
+
+ DPRINTF(sc, UATH_DEBUG_LED, "set %s led %s (blink)\n",
+ (lednum == UATH_LED_LINK) ? "link" : "activity",
+ ledmode ? "on" : "off");
+ return uath_cmd_write(sc, WDCMSG_SET_LED_BLINK, &led, sizeof led, 0);
+}
+
+static int
+uath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ enum ieee80211_state ostate = vap->iv_state;
+ int error;
+ struct ieee80211_node *ni;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct uath_softc *sc = ic->ic_softc;
+ struct uath_vap *uvp = UATH_VAP(vap);
+
+ DPRINTF(sc, UATH_DEBUG_STATE,
+ "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ IEEE80211_UNLOCK(ic);
+ UATH_LOCK(sc);
+ callout_stop(&sc->stat_ch);
+ callout_stop(&sc->watchdog_ch);
+ ni = ieee80211_ref_node(vap->iv_bss);
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_RUN) {
+ /* turn link and activity LEDs off */
+ uath_set_ledstate(sc, 0);
+ }
+ break;
+
+ case IEEE80211_S_SCAN:
+ break;
+
+ case IEEE80211_S_AUTH:
+ /* XXX good place? set RTS threshold */
+ uath_config(sc, CFG_USER_RTS_THRESHOLD, vap->iv_rtsthreshold);
+ /* XXX bad place */
+ error = uath_set_keys(sc, vap);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not set crypto keys, error %d\n", error);
+ break;
+ }
+ if (uath_switch_channel(sc, ni->ni_chan) != 0) {
+ device_printf(sc->sc_dev, "could not switch channel\n");
+ break;
+ }
+ if (uath_create_connection(sc, UATH_ID_BSS) != 0) {
+ device_printf(sc->sc_dev,
+ "could not create connection\n");
+ break;
+ }
+ break;
+
+ case IEEE80211_S_ASSOC:
+ if (uath_set_rates(sc, &ni->ni_rates) != 0) {
+ device_printf(sc->sc_dev,
+ "could not set negotiated rate set\n");
+ break;
+ }
+ break;
+
+ case IEEE80211_S_RUN:
+ /* XXX monitor mode doesn't be tested */
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ uath_set_ledstate(sc, 1);
+ break;
+ }
+
+ /*
+ * Tx rate is controlled by firmware, report the maximum
+ * negotiated rate in ifconfig output.
+ */
+ ni->ni_txrate = ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates-1];
+
+ if (uath_write_associd(sc) != 0) {
+ device_printf(sc->sc_dev,
+ "could not write association id\n");
+ break;
+ }
+ /* turn link LED on */
+ uath_set_ledsteady(sc, UATH_LED_LINK, UATH_LED_ON);
+ /* make activity LED blink */
+ uath_set_ledblink(sc, UATH_LED_ACTIVITY, UATH_LED_ON, 1, 2);
+ /* set state to associated */
+ uath_set_ledstate(sc, 1);
+
+ /* start statistics timer */
+ callout_reset(&sc->stat_ch, hz, uath_stat, sc);
+ break;
+ default:
+ break;
+ }
+ ieee80211_free_node(ni);
+ UATH_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (uvp->newstate(vap, nstate, arg));
+}
+
+static int
+uath_set_key(struct uath_softc *sc, const struct ieee80211_key *wk,
+ int index)
+{
+#if 0
+ struct uath_cmd_crypto crypto;
+ int i;
+
+ memset(&crypto, 0, sizeof(crypto));
+ crypto.keyidx = htobe32(index);
+ crypto.magic1 = htobe32(1);
+ crypto.size = htobe32(368);
+ crypto.mask = htobe32(0xffff);
+ crypto.flags = htobe32(0x80000068);
+ if (index != UATH_DEFAULT_KEY)
+ crypto.flags |= htobe32(index << 16);
+ memset(crypto.magic2, 0xff, sizeof(crypto.magic2));
+
+ /*
+ * Each byte of the key must be XOR'ed with 10101010 before being
+ * transmitted to the firmware.
+ */
+ for (i = 0; i < wk->wk_keylen; i++)
+ crypto.key[i] = wk->wk_key[i] ^ 0xaa;
+
+ DPRINTF(sc, UATH_DEBUG_CRYPTO,
+ "setting crypto key index=%d len=%d\n", index, wk->wk_keylen);
+ return uath_cmd_write(sc, WDCMSG_SET_KEY_CACHE_ENTRY, &crypto,
+ sizeof crypto, 0);
+#else
+ /* XXX support H/W cryto */
+ return (0);
+#endif
+}
+
+static int
+uath_set_keys(struct uath_softc *sc, struct ieee80211vap *vap)
+{
+ int i, error;
+
+ error = 0;
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ const struct ieee80211_key *wk = &vap->iv_nw_keys[i];
+
+ if (wk->wk_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV)) {
+ error = uath_set_key(sc, wk, i);
+ if (error)
+ return (error);
+ }
+ }
+ if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) {
+ error = uath_set_key(sc, &vap->iv_nw_keys[vap->iv_def_txkey],
+ UATH_DEFAULT_KEY);
+ }
+ return (error);
+}
+
+#define UATH_SYSCTL_STAT_ADD32(c, h, n, p, d) \
+ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d)
+
+static void
+uath_sysctl_node(struct uath_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *child;
+ struct sysctl_oid *tree;
+ struct uath_stat *stats;
+
+ stats = &sc->sc_stat;
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
+
+ tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
+ NULL, "UATH statistics");
+ child = SYSCTL_CHILDREN(tree);
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "badchunkseqnum",
+ &stats->st_badchunkseqnum, "Bad chunk sequence numbers");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "invalidlen", &stats->st_invalidlen,
+ "Invalid length");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "multichunk", &stats->st_multichunk,
+ "Multi chunks");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "toobigrxpkt",
+ &stats->st_toobigrxpkt, "Too big rx packets");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "stopinprogress",
+ &stats->st_stopinprogress, "Stop in progress");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "crcerrs", &stats->st_crcerr,
+ "CRC errors");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "phyerr", &stats->st_phyerr,
+ "PHY errors");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "decrypt_crcerr",
+ &stats->st_decrypt_crcerr, "Decryption CRC errors");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "decrypt_micerr",
+ &stats->st_decrypt_micerr, "Decryption Misc errors");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "decomperr", &stats->st_decomperr,
+ "Decomp errors");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "keyerr", &stats->st_keyerr,
+ "Key errors");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "err", &stats->st_err,
+ "Unknown errors");
+
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_active",
+ &stats->st_cmd_active, "Active numbers in Command queue");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_inactive",
+ &stats->st_cmd_inactive, "Inactive numbers in Command queue");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_pending",
+ &stats->st_cmd_pending, "Pending numbers in Command queue");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_waiting",
+ &stats->st_cmd_waiting, "Waiting numbers in Command queue");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "rx_active",
+ &stats->st_rx_active, "Active numbers in RX queue");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "rx_inactive",
+ &stats->st_rx_inactive, "Inactive numbers in RX queue");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_active",
+ &stats->st_tx_active, "Active numbers in TX queue");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_inactive",
+ &stats->st_tx_inactive, "Inactive numbers in TX queue");
+ UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_pending",
+ &stats->st_tx_pending, "Pending numbers in TX queue");
+}
+
+#undef UATH_SYSCTL_STAT_ADD32
+
+static void
+uath_cmdeof(struct uath_softc *sc, struct uath_cmd *cmd)
+{
+ struct uath_cmd_hdr *hdr;
+ int dlen;
+
+ hdr = (struct uath_cmd_hdr *)cmd->buf;
+ /* NB: msgid is passed thru w/o byte swapping */
+#ifdef UATH_DEBUG
+ if (sc->sc_debug & UATH_DEBUG_CMDS) {
+ int len = be32toh(hdr->len);
+ printf("%s: %s [ix %u] len %u status %u\n",
+ __func__, uath_codename(be32toh(hdr->code)),
+ hdr->msgid, len, be32toh(hdr->magic));
+ if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP)
+ uath_dump_cmd(cmd->buf,
+ len > UATH_MAX_CMDSZ ? sizeof(*hdr) : len, '-');
+ }
+#endif
+ hdr->code = be32toh(hdr->code);
+ hdr->len = be32toh(hdr->len);
+ hdr->magic = be32toh(hdr->magic); /* target status on return */
+
+ switch (hdr->code & 0xff) {
+ /* reply to a read command */
+ default:
+ dlen = hdr->len - sizeof(*hdr);
+ if (dlen < 0) {
+ device_printf(sc->sc_dev,
+ "Invalid header length %d\n", dlen);
+ return;
+ }
+ DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL,
+ "%s: code %d data len %u\n",
+ __func__, hdr->code & 0xff, dlen);
+ /*
+ * The first response from the target after the
+ * HOST_AVAILABLE has an invalid msgid so we must
+ * treat it specially.
+ */
+ if (hdr->msgid < UATH_CMD_LIST_COUNT) {
+ uint32_t *rp = (uint32_t *)(hdr+1);
+ u_int olen;
+
+ if (!(sizeof(*hdr) <= hdr->len &&
+ hdr->len < UATH_MAX_CMDSZ)) {
+ device_printf(sc->sc_dev,
+ "%s: invalid WDC msg length %u; "
+ "msg ignored\n", __func__, hdr->len);
+ return;
+ }
+ /*
+ * Calculate return/receive payload size; the
+ * first word, if present, always gives the
+ * number of bytes--unless it's 0 in which
+ * case a single 32-bit word should be present.
+ */
+ if (dlen >= (int)sizeof(uint32_t)) {
+ olen = be32toh(rp[0]);
+ dlen -= sizeof(uint32_t);
+ if (olen == 0) {
+ /* convention is 0 =>'s one word */
+ olen = sizeof(uint32_t);
+ /* XXX KASSERT(olen == dlen ) */
+ }
+ } else
+ olen = 0;
+ if (cmd->odata != NULL) {
+ /* NB: cmd->olen validated in uath_cmd */
+ if (olen > (u_int)cmd->olen) {
+ /* XXX complain? */
+ device_printf(sc->sc_dev,
+ "%s: cmd 0x%x olen %u cmd olen %u\n",
+ __func__, hdr->code, olen,
+ cmd->olen);
+ olen = cmd->olen;
+ }
+ if (olen > (u_int)dlen) {
+ /* XXX complain, shouldn't happen */
+ device_printf(sc->sc_dev,
+ "%s: cmd 0x%x olen %u dlen %u\n",
+ __func__, hdr->code, olen, dlen);
+ olen = dlen;
+ }
+ /* XXX have submitter do this */
+ /* copy answer into caller's supplied buffer */
+ bcopy(&rp[1], cmd->odata, olen);
+ cmd->olen = olen;
+ }
+ }
+ wakeup_one(cmd); /* wake up caller */
+ break;
+
+ case WDCMSG_TARGET_START:
+ if (hdr->msgid >= UATH_CMD_LIST_COUNT) {
+ /* XXX */
+ return;
+ }
+ dlen = hdr->len - sizeof(*hdr);
+ if (dlen != (int)sizeof(uint32_t)) {
+ /* XXX something wrong */
+ return;
+ }
+ /* XXX have submitter do this */
+ /* copy answer into caller's supplied buffer */
+ bcopy(hdr+1, cmd->odata, sizeof(uint32_t));
+ cmd->olen = sizeof(uint32_t);
+ wakeup_one(cmd); /* wake up caller */
+ break;
+
+ case WDCMSG_SEND_COMPLETE:
+ /* this notification is sent when UATH_TX_NOTIFY is set */
+ DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL,
+ "%s: received Tx notification\n", __func__);
+ break;
+
+ case WDCMSG_TARGET_GET_STATS:
+ DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL,
+ "%s: received device statistics\n", __func__);
+ callout_reset(&sc->stat_ch, hz, uath_stat, sc);
+ break;
+ }
+}
+
+static void
+uath_intr_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uath_softc *sc = usbd_xfer_softc(xfer);
+ struct uath_cmd *cmd;
+ struct usb_page_cache *pc;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ UATH_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ cmd = STAILQ_FIRST(&sc->sc_cmd_waiting);
+ if (cmd == NULL)
+ goto setup;
+ STAILQ_REMOVE_HEAD(&sc->sc_cmd_waiting, next);
+ UATH_STAT_DEC(sc, st_cmd_waiting);
+ STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next);
+ UATH_STAT_INC(sc, st_cmd_inactive);
+
+ KASSERT(actlen >= (int)sizeof(struct uath_cmd_hdr),
+ ("short xfer error"));
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, cmd->buf, actlen);
+ uath_cmdeof(sc, cmd);
+ case USB_ST_SETUP:
+setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ break;
+ default:
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto setup;
+ }
+ break;
+ }
+}
+
+static void
+uath_intr_tx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uath_softc *sc = usbd_xfer_softc(xfer);
+ struct uath_cmd *cmd;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ cmd = STAILQ_FIRST(&sc->sc_cmd_active);
+ if (cmd != NULL && USB_GET_STATE(xfer) != USB_ST_SETUP) {
+ STAILQ_REMOVE_HEAD(&sc->sc_cmd_active, next);
+ UATH_STAT_DEC(sc, st_cmd_active);
+ STAILQ_INSERT_TAIL((cmd->flags & UATH_CMD_FLAG_READ) ?
+ &sc->sc_cmd_waiting : &sc->sc_cmd_inactive, cmd, next);
+ if (cmd->flags & UATH_CMD_FLAG_READ)
+ UATH_STAT_INC(sc, st_cmd_waiting);
+ else
+ UATH_STAT_INC(sc, st_cmd_inactive);
+ }
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+setup:
+ cmd = STAILQ_FIRST(&sc->sc_cmd_pending);
+ if (cmd == NULL) {
+ DPRINTF(sc, UATH_DEBUG_XMIT, "%s: empty pending queue\n",
+ __func__);
+ return;
+ }
+ STAILQ_REMOVE_HEAD(&sc->sc_cmd_pending, next);
+ UATH_STAT_DEC(sc, st_cmd_pending);
+ STAILQ_INSERT_TAIL((cmd->flags & UATH_CMD_FLAG_ASYNC) ?
+ &sc->sc_cmd_inactive : &sc->sc_cmd_active, cmd, next);
+ if (cmd->flags & UATH_CMD_FLAG_ASYNC)
+ UATH_STAT_INC(sc, st_cmd_inactive);
+ else
+ UATH_STAT_INC(sc, st_cmd_active);
+
+ usbd_xfer_set_frame_data(xfer, 0, cmd->buf, cmd->buflen);
+ usbd_transfer_submit(xfer);
+ break;
+ default:
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto setup;
+ }
+ break;
+ }
+}
+
+static void
+uath_update_rxstat(struct uath_softc *sc, uint32_t status)
+{
+
+ switch (status) {
+ case UATH_STATUS_STOP_IN_PROGRESS:
+ UATH_STAT_INC(sc, st_stopinprogress);
+ break;
+ case UATH_STATUS_CRC_ERR:
+ UATH_STAT_INC(sc, st_crcerr);
+ break;
+ case UATH_STATUS_PHY_ERR:
+ UATH_STAT_INC(sc, st_phyerr);
+ break;
+ case UATH_STATUS_DECRYPT_CRC_ERR:
+ UATH_STAT_INC(sc, st_decrypt_crcerr);
+ break;
+ case UATH_STATUS_DECRYPT_MIC_ERR:
+ UATH_STAT_INC(sc, st_decrypt_micerr);
+ break;
+ case UATH_STATUS_DECOMP_ERR:
+ UATH_STAT_INC(sc, st_decomperr);
+ break;
+ case UATH_STATUS_KEY_ERR:
+ UATH_STAT_INC(sc, st_keyerr);
+ break;
+ case UATH_STATUS_ERR:
+ UATH_STAT_INC(sc, st_err);
+ break;
+ default:
+ break;
+ }
+}
+
+static struct mbuf *
+uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data,
+ struct uath_rx_desc **pdesc)
+{
+ struct uath_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct uath_chunk *chunk;
+ struct uath_rx_desc *desc;
+ struct mbuf *m = data->m, *mnew, *mp;
+ uint16_t chunklen;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ if (actlen < (int)UATH_MIN_RXBUFSZ) {
+ DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL,
+ "%s: wrong xfer size (len=%d)\n", __func__, actlen);
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+ }
+
+ chunk = (struct uath_chunk *)data->buf;
+ if (chunk->seqnum == 0 && chunk->flags == 0 && chunk->length == 0) {
+ device_printf(sc->sc_dev, "%s: strange response\n", __func__);
+ counter_u64_add(ic->ic_ierrors, 1);
+ UATH_RESET_INTRX(sc);
+ return (NULL);
+ }
+
+ if (chunk->seqnum != sc->sc_intrx_nextnum) {
+ DPRINTF(sc, UATH_DEBUG_XMIT, "invalid seqnum %d, expected %d\n",
+ chunk->seqnum, sc->sc_intrx_nextnum);
+ UATH_STAT_INC(sc, st_badchunkseqnum);
+ if (sc->sc_intrx_head != NULL)
+ m_freem(sc->sc_intrx_head);
+ UATH_RESET_INTRX(sc);
+ return (NULL);
+ }
+
+ /* check multi-chunk frames */
+ if ((chunk->seqnum == 0 && !(chunk->flags & UATH_CFLAGS_FINAL)) ||
+ (chunk->seqnum != 0 && (chunk->flags & UATH_CFLAGS_FINAL)) ||
+ chunk->flags & UATH_CFLAGS_RXMSG)
+ UATH_STAT_INC(sc, st_multichunk);
+
+ chunklen = be16toh(chunk->length);
+ if (chunk->flags & UATH_CFLAGS_FINAL)
+ chunklen -= sizeof(struct uath_rx_desc);
+
+ if (chunklen > 0 &&
+ (!(chunk->flags & UATH_CFLAGS_FINAL) || !(chunk->seqnum == 0))) {
+ /* we should use intermediate RX buffer */
+ if (chunk->seqnum == 0)
+ UATH_RESET_INTRX(sc);
+ if ((sc->sc_intrx_len + sizeof(struct uath_rx_desc) +
+ chunklen) > UATH_MAX_INTRX_SIZE) {
+ UATH_STAT_INC(sc, st_invalidlen);
+ counter_u64_add(ic->ic_ierrors, 1);
+ if (sc->sc_intrx_head != NULL)
+ m_freem(sc->sc_intrx_head);
+ UATH_RESET_INTRX(sc);
+ return (NULL);
+ }
+
+ m->m_len = chunklen;
+ m->m_data += sizeof(struct uath_chunk);
+
+ if (sc->sc_intrx_head == NULL) {
+ sc->sc_intrx_head = m;
+ sc->sc_intrx_tail = m;
+ } else {
+ m->m_flags &= ~M_PKTHDR;
+ sc->sc_intrx_tail->m_next = m;
+ sc->sc_intrx_tail = m;
+ }
+ }
+ sc->sc_intrx_len += chunklen;
+
+ mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (mnew == NULL) {
+ DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL,
+ "%s: can't get new mbuf, drop frame\n", __func__);
+ counter_u64_add(ic->ic_ierrors, 1);
+ if (sc->sc_intrx_head != NULL)
+ m_freem(sc->sc_intrx_head);
+ UATH_RESET_INTRX(sc);
+ return (NULL);
+ }
+
+ data->m = mnew;
+ data->buf = mtod(mnew, uint8_t *);
+
+ /* if the frame is not final continue the transfer */
+ if (!(chunk->flags & UATH_CFLAGS_FINAL)) {
+ sc->sc_intrx_nextnum++;
+ UATH_RESET_INTRX(sc);
+ return (NULL);
+ }
+
+ /*
+ * if the frame is not set UATH_CFLAGS_RXMSG, then rx descriptor is
+ * located at the end, 32-bit aligned
+ */
+ desc = (chunk->flags & UATH_CFLAGS_RXMSG) ?
+ (struct uath_rx_desc *)(chunk + 1) :
+ (struct uath_rx_desc *)(((uint8_t *)chunk) +
+ sizeof(struct uath_chunk) + be16toh(chunk->length) -
+ sizeof(struct uath_rx_desc));
+ *pdesc = desc;
+
+ DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL,
+ "%s: frame len %u code %u status %u rate %u antenna %u "
+ "rssi %d channel %u phyerror %u connix %u decrypterror %u "
+ "keycachemiss %u\n", __func__, be32toh(desc->framelen)
+ , be32toh(desc->code), be32toh(desc->status), be32toh(desc->rate)
+ , be32toh(desc->antenna), be32toh(desc->rssi), be32toh(desc->channel)
+ , be32toh(desc->phyerror), be32toh(desc->connix)
+ , be32toh(desc->decrypterror), be32toh(desc->keycachemiss));
+
+ if (be32toh(desc->len) > MCLBYTES) {
+ DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL,
+ "%s: bad descriptor (len=%d)\n", __func__,
+ be32toh(desc->len));
+ counter_u64_add(ic->ic_ierrors, 1);
+ UATH_STAT_INC(sc, st_toobigrxpkt);
+ if (sc->sc_intrx_head != NULL)
+ m_freem(sc->sc_intrx_head);
+ UATH_RESET_INTRX(sc);
+ return (NULL);
+ }
+
+ uath_update_rxstat(sc, be32toh(desc->status));
+
+ /* finalize mbuf */
+ if (sc->sc_intrx_head == NULL) {
+ m->m_pkthdr.len = m->m_len =
+ be32toh(desc->framelen) - UATH_RX_DUMMYSIZE;
+ m->m_data += sizeof(struct uath_chunk);
+ } else {
+ mp = sc->sc_intrx_head;
+ mp->m_flags |= M_PKTHDR;
+ mp->m_pkthdr.len = sc->sc_intrx_len;
+ m = mp;
+ }
+
+ /* there are a lot more fields in the RX descriptor */
+ if ((sc->sc_flags & UATH_FLAG_INVALID) == 0 &&
+ ieee80211_radiotap_active(ic)) {
+ struct uath_rx_radiotap_header *tap = &sc->sc_rxtap;
+ uint32_t tsf_hi = be32toh(desc->tstamp_high);
+ uint32_t tsf_lo = be32toh(desc->tstamp_low);
+
+ /* XXX only get low order 24bits of tsf from h/w */
+ tap->wr_tsf = htole64(((uint64_t)tsf_hi << 32) | tsf_lo);
+ tap->wr_flags = 0;
+ if (be32toh(desc->status) == UATH_STATUS_CRC_ERR)
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+ /* XXX map other status to BADFCS? */
+ /* XXX ath h/w rate code, need to map */
+ tap->wr_rate = be32toh(desc->rate);
+ tap->wr_antenna = be32toh(desc->antenna);
+ tap->wr_antsignal = -95 + be32toh(desc->rssi);
+ tap->wr_antnoise = -95;
+ }
+
+ UATH_RESET_INTRX(sc);
+
+ return (m);
+}
+
+static void
+uath_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uath_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL;
+ struct uath_data *data;
+ struct uath_rx_desc *desc = NULL;
+ int8_t nf;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&sc->sc_rx_active);
+ if (data == NULL)
+ goto setup;
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
+ UATH_STAT_DEC(sc, st_rx_active);
+ m = uath_data_rxeof(xfer, data, &desc);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ UATH_STAT_INC(sc, st_rx_inactive);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+setup:
+ data = STAILQ_FIRST(&sc->sc_rx_inactive);
+ if (data == NULL)
+ return;
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
+ UATH_STAT_DEC(sc, st_rx_inactive);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
+ UATH_STAT_INC(sc, st_rx_active);
+ usbd_xfer_set_frame_data(xfer, 0, data->buf, MCLBYTES);
+ usbd_transfer_submit(xfer);
+
+ /*
+ * To avoid LOR we should unlock our private mutex here to call
+ * ieee80211_input() because here is at the end of a USB
+ * callback and safe to unlock.
+ */
+ if (sc->sc_flags & UATH_FLAG_INVALID) {
+ if (m != NULL)
+ m_freem(m);
+ return;
+ }
+ UATH_UNLOCK(sc);
+ if (m != NULL && desc != NULL) {
+ wh = mtod(m, struct ieee80211_frame *);
+ ni = ieee80211_find_rxnode(ic,
+ (struct ieee80211_frame_min *)wh);
+ nf = -95; /* XXX */
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m,
+ (int)be32toh(desc->rssi), nf);
+ /* node is no longer needed */
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m,
+ (int)be32toh(desc->rssi), nf);
+ m = NULL;
+ desc = NULL;
+ }
+ UATH_LOCK(sc);
+ uath_start(sc);
+ break;
+ default:
+ /* needs it to the inactive queue due to a error. */
+ data = STAILQ_FIRST(&sc->sc_rx_active);
+ if (data != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
+ UATH_STAT_DEC(sc, st_rx_active);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ UATH_STAT_INC(sc, st_rx_inactive);
+ }
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto setup;
+ }
+ break;
+ }
+}
+
+static void
+uath_data_txeof(struct usb_xfer *xfer, struct uath_data *data)
+{
+ struct uath_softc *sc = usbd_xfer_softc(xfer);
+
+ UATH_ASSERT_LOCKED(sc);
+
+ if (data->m) {
+ /* XXX status? */
+ ieee80211_tx_complete(data->ni, data->m, 0);
+ data->m = NULL;
+ data->ni = NULL;
+ }
+ sc->sc_tx_timer = 0;
+}
+
+static void
+uath_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uath_softc *sc = usbd_xfer_softc(xfer);
+ struct uath_data *data;
+
+ UATH_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&sc->sc_tx_active);
+ if (data == NULL)
+ goto setup;
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next);
+ UATH_STAT_DEC(sc, st_tx_active);
+ uath_data_txeof(xfer, data);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
+ UATH_STAT_INC(sc, st_tx_inactive);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+setup:
+ data = STAILQ_FIRST(&sc->sc_tx_pending);
+ if (data == NULL) {
+ DPRINTF(sc, UATH_DEBUG_XMIT, "%s: empty pending queue\n",
+ __func__);
+ return;
+ }
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next);
+ UATH_STAT_DEC(sc, st_tx_pending);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next);
+ UATH_STAT_INC(sc, st_tx_active);
+
+ usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
+ usbd_transfer_submit(xfer);
+
+ uath_start(sc);
+ break;
+ default:
+ data = STAILQ_FIRST(&sc->sc_tx_active);
+ if (data == NULL)
+ goto setup;
+ if (data->ni != NULL) {
+ if_inc_counter(data->ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ if ((sc->sc_flags & UATH_FLAG_INVALID) == 0)
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto setup;
+ }
+ break;
+ }
+}
+
+static device_method_t uath_methods[] = {
+ DEVMETHOD(device_probe, uath_match),
+ DEVMETHOD(device_attach, uath_attach),
+ DEVMETHOD(device_detach, uath_detach),
+ DEVMETHOD_END
+};
+static driver_t uath_driver = {
+ .name = "uath",
+ .methods = uath_methods,
+ .size = sizeof(struct uath_softc)
+};
+static devclass_t uath_devclass;
+
+DRIVER_MODULE(uath, uhub, uath_driver, uath_devclass, NULL, 0);
+MODULE_DEPEND(uath, wlan, 1, 1, 1);
+MODULE_DEPEND(uath, usb, 1, 1, 1);
+MODULE_VERSION(uath, 1);
+USB_PNP_HOST_INFO(uath_devs);
diff --git a/freebsd/sys/dev/usb/wlan/if_uathreg.h b/freebsd/sys/dev/usb/wlan/if_uathreg.h
new file mode 100644
index 00000000..1e7929bc
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_uathreg.h
@@ -0,0 +1,601 @@
+/* $OpenBSD: if_uathreg.h,v 1.2 2006/09/18 16:34:23 damien Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Sam Leffler, Errno Consulting
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define UATH_CONFIG_INDEX 0
+#define UATH_IFACE_INDEX 0
+
+/* all fields are big endian */
+struct uath_fwblock {
+ uint32_t flags;
+#define UATH_WRITE_BLOCK (1 << 4)
+
+ uint32_t len;
+#define UATH_MAX_FWBLOCK_SIZE 2048
+
+ uint32_t total;
+ uint32_t remain;
+ uint32_t rxtotal;
+ uint32_t pad[123];
+} __packed;
+
+#define UATH_MAX_CMDSZ 512
+
+/*
+ * Messages are passed in Target Endianness. All fixed-size
+ * fields of a WDS Control Message are treated as 32-bit
+ * values and Control Msgs are guaranteed to be 32-bit aligned.
+ *
+ * The format of a WDS Control Message is as follows:
+ * Message Length 32 bits
+ * Message Opcode 32 bits
+ * Message ID 32 bits
+ * parameter 1
+ * parameter 2
+ * ...
+ *
+ * A variable-length parameter, or a parmeter that is larger than
+ * 32 bits is passed as <length, data> pair, where length is a
+ * 32-bit quantity and data is padded to 32 bits.
+ */
+struct uath_cmd_hdr {
+ uint32_t len; /* msg length including header */
+ uint32_t code; /* operation code */
+/* NB: these are defined for rev 1.5 firmware; rev 1.6 is different */
+/* messages from Host -> Target */
+#define WDCMSG_HOST_AVAILABLE 0x01
+#define WDCMSG_BIND 0x02
+#define WDCMSG_TARGET_RESET 0x03
+#define WDCMSG_TARGET_GET_CAPABILITY 0x04
+#define WDCMSG_TARGET_SET_CONFIG 0x05
+#define WDCMSG_TARGET_GET_STATUS 0x06
+#define WDCMSG_TARGET_GET_STATS 0x07
+#define WDCMSG_TARGET_START 0x08
+#define WDCMSG_TARGET_STOP 0x09
+#define WDCMSG_TARGET_ENABLE 0x0a
+#define WDCMSG_TARGET_DISABLE 0x0b
+#define WDCMSG_CREATE_CONNECTION 0x0c
+#define WDCMSG_UPDATE_CONNECT_ATTR 0x0d
+#define WDCMSG_DELETE_CONNECT 0x0e
+#define WDCMSG_SEND 0x0f
+#define WDCMSG_FLUSH 0x10
+/* messages from Target -> Host */
+#define WDCMSG_STATS_UPDATE 0x11
+#define WDCMSG_BMISS 0x12
+#define WDCMSG_DEVICE_AVAIL 0x13
+#define WDCMSG_SEND_COMPLETE 0x14
+#define WDCMSG_DATA_AVAIL 0x15
+#define WDCMSG_SET_PWR_MODE 0x16
+#define WDCMSG_BMISS_ACK 0x17
+#define WDCMSG_SET_LED_STEADY 0x18
+#define WDCMSG_SET_LED_BLINK 0x19
+/* more messages */
+#define WDCMSG_SETUP_BEACON_DESC 0x1a
+#define WDCMSG_BEACON_INIT 0x1b
+#define WDCMSG_RESET_KEY_CACHE 0x1c
+#define WDCMSG_RESET_KEY_CACHE_ENTRY 0x1d
+#define WDCMSG_SET_KEY_CACHE_ENTRY 0x1e
+#define WDCMSG_SET_DECOMP_MASK 0x1f
+#define WDCMSG_SET_REGULATORY_DOMAIN 0x20
+#define WDCMSG_SET_LED_STATE 0x21
+#define WDCMSG_WRITE_ASSOCID 0x22
+#define WDCMSG_SET_STA_BEACON_TIMERS 0x23
+#define WDCMSG_GET_TSF 0x24
+#define WDCMSG_RESET_TSF 0x25
+#define WDCMSG_SET_ADHOC_MODE 0x26
+#define WDCMSG_SET_BASIC_RATE 0x27
+#define WDCMSG_MIB_CONTROL 0x28
+#define WDCMSG_GET_CHANNEL_DATA 0x29
+#define WDCMSG_GET_CUR_RSSI 0x2a
+#define WDCMSG_SET_ANTENNA_SWITCH 0x2b
+#define WDCMSG_USE_SHORT_SLOT_TIME 0x2f
+#define WDCMSG_SET_POWER_MODE 0x30
+#define WDCMSG_SETUP_PSPOLL_DESC 0x31
+#define WDCMSG_SET_RX_MULTICAST_FILTER 0x32
+#define WDCMSG_RX_FILTER 0x33
+#define WDCMSG_PER_CALIBRATION 0x34
+#define WDCMSG_RESET 0x35
+#define WDCMSG_DISABLE 0x36
+#define WDCMSG_PHY_DISABLE 0x37
+#define WDCMSG_SET_TX_POWER_LIMIT 0x38
+#define WDCMSG_SET_TX_QUEUE_PARAMS 0x39
+#define WDCMSG_SETUP_TX_QUEUE 0x3a
+#define WDCMSG_RELEASE_TX_QUEUE 0x3b
+#define WDCMSG_SET_DEFAULT_KEY 0x43
+ uint32_t msgid; /* msg id (supplied by host) */
+ uint32_t magic; /* response desired/target status */
+ uint32_t debug[4]; /* debug data area */
+ /* msg data follows */
+} __packed;
+
+struct uath_chunk {
+ uint8_t seqnum; /* sequence number for ordering */
+ uint8_t flags;
+#define UATH_CFLAGS_FINAL 0x01 /* final chunk of a msg */
+#define UATH_CFLAGS_RXMSG 0x02 /* chunk contains rx completion */
+#define UATH_CFLAGS_DEBUG 0x04 /* for debugging */
+ uint16_t length; /* chunk size in bytes */
+ /* chunk data follows */
+} __packed;
+
+#define UATH_RX_DUMMYSIZE 4
+
+/*
+ * Message format for a WDCMSG_DATA_AVAIL message from Target to Host.
+ */
+struct uath_rx_desc {
+ uint32_t len; /* msg length including header */
+ uint32_t code; /* WDCMSG_DATA_AVAIL */
+ uint32_t gennum; /* generation number */
+ uint32_t status; /* start of RECEIVE_INFO */
+#define UATH_STATUS_OK 0
+#define UATH_STATUS_STOP_IN_PROGRESS 1
+#define UATH_STATUS_CRC_ERR 2
+#define UATH_STATUS_PHY_ERR 3
+#define UATH_STATUS_DECRYPT_CRC_ERR 4
+#define UATH_STATUS_DECRYPT_MIC_ERR 5
+#define UATH_STATUS_DECOMP_ERR 6
+#define UATH_STATUS_KEY_ERR 7
+#define UATH_STATUS_ERR 8
+ uint32_t tstamp_low; /* low-order 32-bits of rx timestamp */
+ uint32_t tstamp_high; /* high-order 32-bits of rx timestamp */
+ uint32_t framelen; /* frame length */
+ uint32_t rate; /* rx rate code */
+ uint32_t antenna;
+ int32_t rssi;
+ uint32_t channel;
+ uint32_t phyerror;
+ uint32_t connix; /* key table ix for bss traffic */
+ uint32_t decrypterror;
+ uint32_t keycachemiss;
+ uint32_t pad; /* XXX? */
+} __packed;
+
+struct uath_tx_desc {
+ uint32_t msglen;
+ uint32_t msgid; /* msg id (supplied by host) */
+ uint32_t type; /* opcode: WDMSG_SEND or WDCMSG_FLUSH */
+ uint32_t txqid; /* tx queue id and flags */
+#define UATH_TXQID_MASK 0x0f
+#define UATH_TXQID_MINRATE 0x10 /* use min tx rate */
+#define UATH_TXQID_FF 0x20 /* content is fast frame */
+ uint32_t connid; /* tx connection id */
+#define UATH_ID_INVALID 0xffffffff /* for sending prior to connection */
+ uint32_t flags; /* non-zero if response desired */
+#define UATH_TX_NOTIFY (1 << 24) /* f/w will send a UATH_NOTIF_TX */
+ uint32_t buflen; /* payload length */
+} __packed;
+
+struct uath_cmd_host_available {
+ uint32_t sw_ver_major;
+ uint32_t sw_ver_minor;
+ uint32_t sw_ver_patch;
+ uint32_t sw_ver_build;
+} __packed;
+#define ATH_SW_VER_MAJOR 1
+#define ATH_SW_VER_MINOR 5
+#define ATH_SW_VER_PATCH 0
+#define ATH_SW_VER_BUILD 9999
+
+struct uath_cmd_bind {
+ uint32_t targethandle;
+ uint32_t hostapiversion;
+} __packed;
+
+/* structure for command WDCMSG_RESET */
+struct uath_cmd_reset {
+ uint32_t flags; /* channel flags */
+#define UATH_CHAN_TURBO 0x0100
+#define UATH_CHAN_CCK 0x0200
+#define UATH_CHAN_OFDM 0x0400
+#define UATH_CHAN_2GHZ 0x1000
+#define UATH_CHAN_5GHZ 0x2000
+ uint32_t freq; /* channel frequency */
+ uint32_t maxrdpower;
+ uint32_t cfgctl;
+ uint32_t twiceantennareduction;
+ uint32_t channelchange;
+ uint32_t keeprccontent;
+} __packed;
+
+/* structure for commands UATH_CMD_READ_MAC and UATH_CMD_READ_EEPROM */
+struct uath_read_mac {
+ uint32_t len;
+ uint8_t data[32];
+} __packed;
+
+/* structure for command UATH_CMD_WRITE_MAC */
+struct uath_write_mac {
+ uint32_t reg;
+ uint32_t len;
+ uint8_t data[32];
+} __packed;
+
+/* structure for command UATH_CMD_STA_JOIN */
+struct uath_cmd_join_bss {
+ uint32_t bssid; /* NB: use zero */
+ uint32_t bssmac[2]; /* bssid mac address */
+ uint32_t bsstype;
+ uint32_t wlanmode;
+ uint32_t beaconinterval;
+ uint32_t dtiminterval;
+ uint32_t cfpinterval;
+ uint32_t atimwindow;
+ uint32_t defaultrateix;
+ uint32_t shortslottime11g;
+ uint32_t sleepduration;
+ uint32_t bmissthreshold;
+ uint32_t tcppowerlimit;
+ uint32_t quietduration;
+ uint32_t quietoffset;
+ uint32_t quietackctsallow;
+ uint32_t bssdefaultkey; /* XXX? */
+} __packed;
+
+struct uath_cmd_assoc_bss {
+ uint32_t bssid;
+ uint32_t associd;
+} __packed;
+
+struct uath_cmd_start_bss {
+ uint32_t bssid;
+} __packed;
+
+/* structure for command UATH_CMD_0C */
+struct uath_cmd_0c {
+ uint32_t magic1;
+ uint32_t magic2;
+ uint32_t magic3;
+} __packed;
+
+struct uath_cmd_ledsteady { /* WDCMSG_SET_LED_STEADY */
+ uint32_t lednum;
+#define UATH_LED_LINK 0
+#define UATH_LED_ACTIVITY 1
+ uint32_t ledmode;
+#define UATH_LED_OFF 0
+#define UATH_LED_ON 1
+} __packed;
+
+struct uath_cmd_ledblink { /* WDCMSG_SET_LED_BLINK */
+ uint32_t lednum;
+ uint32_t ledmode;
+ uint32_t blinkrate;
+ uint32_t slowmode;
+} __packed;
+
+struct uath_cmd_ledstate { /* WDCMSG_SET_LED_STATE */
+ uint32_t connected;
+} __packed;
+
+struct uath_connkey_rec {
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+ uint32_t keyiv;
+ uint32_t extkeyiv;
+ uint16_t keyflags;
+ uint16_t keylen;
+ uint16_t keytype; /* WEP, TKIP or AES */
+ /* As far as I know, MIPS 4Kp is 32-bit processor */
+ uint32_t priv;
+ uint8_t keyval[32];
+ uint16_t aes_keylen;
+ uint8_t aes_keyval[16];
+ uint8_t mic_txkeyval[8];
+ uint8_t mic_rxkeyval[8];
+ int64_t keyrsc[17];
+ int32_t keytsc[17];
+ int32_t keyexttsc[17];
+} __packed;
+
+/* structure for command UATH_CMD_CRYPTO */
+struct uath_cmd_crypto {
+ uint32_t keyidx;
+#define UATH_DEFAULT_KEY 6
+ uint32_t xorkey;
+ uint32_t size;
+ struct uath_connkey_rec rec;
+} __packed;
+
+struct uath_cmd_rateset {
+ uint8_t length;
+#define UATH_MAX_NRATES 32
+ uint8_t set[UATH_MAX_NRATES];
+};
+
+/* structure for command WDCMSG_SET_BASIC_RATE */
+struct uath_cmd_rates {
+ uint32_t connid;
+ uint32_t keeprccontent;
+ uint32_t size;
+ struct uath_cmd_rateset rateset;
+} __packed;
+
+enum {
+ WLAN_MODE_NONE = 0,
+ WLAN_MODE_11b,
+ WLAN_MODE_11a,
+ WLAN_MODE_11g,
+ WLAN_MODE_11a_TURBO,
+ WLAN_MODE_11g_TURBO,
+ WLAN_MODE_11a_TURBO_PRIME,
+ WLAN_MODE_11g_TURBO_PRIME,
+ WLAN_MODE_11a_XR,
+ WLAN_MODE_11g_XR,
+};
+
+struct uath_cmd_connection_attr {
+ uint32_t longpreambleonly;
+ struct uath_cmd_rateset rateset;
+ uint32_t wlanmode;
+} __packed;
+
+/* structure for command WDCMSG_CREATE_CONNECTION */
+struct uath_cmd_create_connection {
+ uint32_t connid;
+ uint32_t bssid;
+ uint32_t size;
+ struct uath_cmd_connection_attr connattr;
+} __packed;
+
+struct uath_cmd_txq_setparams { /* WDCMSG_SET_TX_QUEUE_PARAMS */
+ uint32_t qnum;
+ uint32_t aifs;
+ uint32_t logcwmin;
+ uint32_t logcwmax;
+ uint32_t bursttime;
+ uint32_t qflags;
+} __packed;
+
+struct uath_cmd_txq_attr {
+ uint32_t priority;
+ uint32_t aifs;
+ uint32_t logcwmin;
+ uint32_t logcwmax;
+ uint32_t bursttime;
+ uint32_t mode;
+ uint32_t qflags;
+} __packed;
+
+struct uath_cmd_txq_setup { /* WDCMSG_SETUP_TX_QUEUE */
+ uint32_t qid;
+ uint32_t len;
+ struct uath_cmd_txq_attr attr;
+} __packed;
+
+struct uath_cmd_stoptxdma { /* WDCMSG_STOP_TX_DMA */
+ uint32_t qnum;
+ uint32_t msec;
+} __packed;
+
+/* structure for command UATH_CMD_31 */
+struct uath_cmd_31 {
+ uint32_t magic1;
+ uint32_t magic2;
+} __packed;
+
+struct uath_cmd_rx_filter { /* WDCMSG_RX_FILTER */
+ uint32_t bits;
+#define UATH_FILTER_RX_UCAST 0x00000001
+#define UATH_FILTER_RX_MCAST 0x00000002
+#define UATH_FILTER_RX_BCAST 0x00000004
+#define UATH_FILTER_RX_CONTROL 0x00000008
+#define UATH_FILTER_RX_BEACON 0x00000010 /* beacon frames */
+#define UATH_FILTER_RX_PROM 0x00000020 /* promiscuous mode */
+#define UATH_FILTER_RX_PHY_ERR 0x00000040 /* phy errors */
+#define UATH_FILTER_RX_PHY_RADAR 0x00000080 /* radar phy errors */
+#define UATH_FILTER_RX_XR_POOL 0x00000400 /* XR group polls */
+#define UATH_FILTER_RX_PROBE_REQ 0x00000800
+ uint32_t op;
+#define UATH_FILTER_OP_INIT 0x0
+#define UATH_FILTER_OP_SET 0x1
+#define UATH_FILTER_OP_CLEAR 0x2
+#define UATH_FILTER_OP_TEMP 0x3
+#define UATH_FILTER_OP_RESTORE 0x4
+} __packed;
+
+struct uath_cmd_rx_mcast_filter { /* WDCMSG_SET_RX_MCAST_FILTER */
+ uint32_t filter0;
+ uint32_t filter1;
+} __packed;
+
+struct uath_cmd_set_associd { /* WDCMSG_WRITE_ASSOCID */
+ uint32_t defaultrateix;
+ uint32_t associd;
+ uint32_t timoffset;
+ uint32_t turboprime;
+ uint32_t bssid[2];
+} __packed;
+
+struct uath_cmd_set_stabeacon_timers { /* WDCMSG_SET_STA_BEACON_TIMERS */
+ uint32_t nexttbtt;
+ uint32_t nextdtim;
+ uint32_t nextcfp;
+ uint32_t beaconperiod;
+ uint32_t dtimperiod;
+ uint32_t cfpperiod;
+ uint32_t cfpduration;
+ uint32_t sleepduration;
+ uint32_t bsmissthreshold;
+} __packed;
+
+enum {
+ CFG_NONE, /* Sentinal to indicate "no config" */
+ CFG_REG_DOMAIN, /* Regulatory Domain */
+ CFG_RATE_CONTROL_ENABLE,
+ CFG_DEF_XMIT_DATA_RATE, /* NB: if rate control is not enabled */
+ CFG_HW_TX_RETRIES,
+ CFG_SW_TX_RETRIES,
+ CFG_SLOW_CLOCK_ENABLE,
+ CFG_COMP_PROC,
+ CFG_USER_RTS_THRESHOLD,
+ CFG_XR2NORM_RATE_THRESHOLD,
+ CFG_XRMODE_SWITCH_COUNT,
+ CFG_PROTECTION_TYPE,
+ CFG_BURST_SEQ_THRESHOLD,
+ CFG_ABOLT,
+ CFG_IQ_LOG_COUNT_MAX,
+ CFG_MODE_CTS,
+ CFG_WME_ENABLED,
+ CFG_GPRS_CBR_PERIOD,
+ CFG_SERVICE_TYPE,
+ /* MAC Address to use. Overrides EEPROM */
+ CFG_MAC_ADDR,
+ CFG_DEBUG_EAR,
+ CFG_INIT_REGS,
+ /* An ID for use in error & debug messages */
+ CFG_DEBUG_ID,
+ CFG_COMP_WIN_SZ,
+ CFG_DIVERSITY_CTL,
+ CFG_TP_SCALE,
+ CFG_TPC_HALF_DBM5,
+ CFG_TPC_HALF_DBM2,
+ CFG_OVERRD_TX_POWER,
+ CFG_USE_32KHZ_CLOCK,
+ CFG_GMODE_PROTECTION,
+ CFG_GMODE_PROTECT_RATE_INDEX,
+ CFG_GMODE_NON_ERP_PREAMBLE,
+ CFG_WDC_TRANSPORT_CHUNK_SIZE,
+};
+
+enum {
+ /* Sentinal to indicate "no capability" */
+ CAP_NONE,
+ CAP_ALL, /* ALL capabilities */
+ CAP_TARGET_VERSION,
+ CAP_TARGET_REVISION,
+ CAP_MAC_VERSION,
+ CAP_MAC_REVISION,
+ CAP_PHY_REVISION,
+ CAP_ANALOG_5GHz_REVISION,
+ CAP_ANALOG_2GHz_REVISION,
+ /* Target supports WDC message debug features */
+ CAP_DEBUG_WDCMSG_SUPPORT,
+
+ CAP_REG_DOMAIN,
+ CAP_COUNTRY_CODE,
+ CAP_REG_CAP_BITS,
+
+ CAP_WIRELESS_MODES,
+ CAP_CHAN_SPREAD_SUPPORT,
+ CAP_SLEEP_AFTER_BEACON_BROKEN,
+ CAP_COMPRESS_SUPPORT,
+ CAP_BURST_SUPPORT,
+ CAP_FAST_FRAMES_SUPPORT,
+ CAP_CHAP_TUNING_SUPPORT,
+ CAP_TURBOG_SUPPORT,
+ CAP_TURBO_PRIME_SUPPORT,
+ CAP_DEVICE_TYPE,
+ CAP_XR_SUPPORT,
+ CAP_WME_SUPPORT,
+ CAP_TOTAL_QUEUES,
+ CAP_CONNECTION_ID_MAX, /* Should absorb CAP_KEY_CACHE_SIZE */
+
+ CAP_LOW_5GHZ_CHAN,
+ CAP_HIGH_5GHZ_CHAN,
+ CAP_LOW_2GHZ_CHAN,
+ CAP_HIGH_2GHZ_CHAN,
+
+ CAP_MIC_AES_CCM,
+ CAP_MIC_CKIP,
+ CAP_MIC_TKIP,
+ CAP_MIC_TKIP_WME,
+ CAP_CIPHER_AES_CCM,
+ CAP_CIPHER_CKIP,
+ CAP_CIPHER_TKIP,
+
+ CAP_TWICE_ANTENNAGAIN_5G,
+ CAP_TWICE_ANTENNAGAIN_2G,
+};
+
+enum {
+ ST_NONE, /* Sentinal to indicate "no status" */
+ ST_ALL,
+ ST_SERVICE_TYPE,
+ ST_WLAN_MODE,
+ ST_FREQ,
+ ST_BAND,
+ ST_LAST_RSSI,
+ ST_PS_FRAMES_DROPPED,
+ ST_CACHED_DEF_ANT,
+ ST_COUNT_OTHER_RX_ANT,
+ ST_USE_FAST_DIVERSITY,
+ ST_MAC_ADDR,
+ ST_RX_GENERATION_NUM,
+ ST_TX_QUEUE_DEPTH,
+ ST_SERIAL_NUMBER,
+ ST_WDC_TRANSPORT_CHUNK_SIZE,
+};
+
+enum {
+ BSS_ATTR_BEACON_INTERVAL,
+ BSS_ATTR_DTIM_INTERVAL,
+ BSS_ATTR_CFP_INTERVAL,
+ BSS_ATTR_CFP_MAX_DURATION,
+ BSS_ATTR_ATIM_WINDOW,
+ BSS_ATTR_DEFAULT_RATE_INDEX,
+ BSS_ATTR_SHORT_SLOT_TIME_11g,
+ BSS_ATTR_SLEEP_DURATION,
+ BSS_ATTR_BMISS_THRESHOLD,
+ BSS_ATTR_TPC_POWER_LIMIT,
+ BSS_ATTR_BSS_KEY_UPDATE,
+};
+
+struct uath_cmd_update_bss_attribute {
+ uint32_t bssid;
+ uint32_t attribute; /* BSS_ATTR_BEACON_INTERVAL, et al. */
+ uint32_t cfgsize; /* should be zero 0 */
+ uint32_t cfgdata;
+};
+
+struct uath_cmd_update_bss_attribute_key {
+ uint32_t bssid;
+ uint32_t attribute; /* BSS_ATTR_BSS_KEY_UPDATE */
+ uint32_t cfgsize; /* size of remaining data */
+ uint32_t bsskeyix;
+ uint32_t isdefaultkey;
+ uint32_t keyiv; /* IV generation control */
+ uint32_t extkeyiv; /* extended IV for TKIP & CCM */
+ uint32_t keyflags;
+ uint32_t keytype;
+ uint32_t initvalue; /* XXX */
+ uint32_t keyval[4];
+ uint32_t mictxkeyval[2];
+ uint32_t micrxkeyval[2];
+ uint32_t keyrsc[2];
+};
+
+enum {
+ TARGET_DEVICE_AWAKE,
+ TARGET_DEVICE_SLEEP,
+ TARGET_DEVICE_PWRDN,
+ TARGET_DEVICE_PWRSAVE,
+ TARGET_DEVICE_SUSPEND,
+ TARGET_DEVICE_RESUME,
+};
+
+#define UATH_MAX_TXBUFSZ \
+ (sizeof(struct uath_chunk) + sizeof(struct uath_tx_desc) + \
+ IEEE80211_MAX_LEN)
+
+/*
+ * it's not easy to measure how the chunk is passed into the host if the target
+ * passed the multi-chunks so just we check a minimal size we can imagine.
+ */
+#define UATH_MIN_RXBUFSZ (sizeof(struct uath_chunk))
diff --git a/freebsd/sys/dev/usb/wlan/if_uathvar.h b/freebsd/sys/dev/usb/wlan/if_uathvar.h
new file mode 100644
index 00000000..a38f54fc
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_uathvar.h
@@ -0,0 +1,245 @@
+/* $OpenBSD: if_uathvar.h,v 1.3 2006/09/20 19:47:17 damien Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Sam Leffler, Errno Consulting
+ * Copyright (c) 2008-2009 Weongyo Jeong <weongyo@freebsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+enum {
+ UATH_INTR_RX,
+ UATH_INTR_TX,
+ UATH_BULK_RX,
+ UATH_BULK_TX,
+ UATH_N_XFERS = 4,
+};
+
+#define UATH_ID_BSS 2 /* Connection ID */
+
+#define UATH_RX_DATA_LIST_COUNT 128
+#define UATH_TX_DATA_LIST_COUNT 16
+#define UATH_CMD_LIST_COUNT 60
+
+#define UATH_DATA_TIMEOUT 10000
+#define UATH_CMD_TIMEOUT 1000
+
+/* flags for sending firmware commands */
+#define UATH_CMD_FLAG_ASYNC (1 << 0)
+#define UATH_CMD_FLAG_READ (1 << 1)
+#define UATH_CMD_FLAG_MAGIC (1 << 2)
+
+struct uath_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ u_int64_t wr_tsf;
+ u_int8_t wr_flags;
+ u_int8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_antsignal;
+ int8_t wr_antnoise;
+ u_int8_t wr_antenna;
+} __packed __aligned(8);
+
+#define UATH_RX_RADIOTAP_PRESENT ( \
+ (1 << IEEE80211_RADIOTAP_TSFT) | \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ 0)
+
+struct uath_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed __aligned(8);
+
+#define UATH_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct uath_data {
+ struct uath_softc *sc;
+ uint8_t *buf;
+ uint16_t buflen;
+ struct mbuf *m;
+ struct ieee80211_node *ni; /* NB: tx only */
+ STAILQ_ENTRY(uath_data) next;
+};
+typedef STAILQ_HEAD(, uath_data) uath_datahead;
+
+struct uath_cmd {
+ struct uath_softc *sc;
+ uint32_t flags;
+ uint32_t msgid;
+ uint8_t *buf;
+ uint16_t buflen;
+ void *odata; /* NB: tx only */
+ int olen; /* space in odata */
+ STAILQ_ENTRY(uath_cmd) next;
+};
+typedef STAILQ_HEAD(, uath_cmd) uath_cmdhead;
+
+struct uath_wme_settings {
+ uint8_t aifsn;
+ uint8_t logcwmin;
+ uint8_t logcwmax;
+ uint16_t txop;
+ uint8_t acm;
+};
+
+struct uath_devcap {
+ uint32_t targetVersion;
+ uint32_t targetRevision;
+ uint32_t macVersion;
+ uint32_t macRevision;
+ uint32_t phyRevision;
+ uint32_t analog5GhzRevision;
+ uint32_t analog2GhzRevision;
+ uint32_t regDomain;
+ uint32_t regCapBits;
+ uint32_t countryCode;
+ uint32_t keyCacheSize;
+ uint32_t numTxQueues;
+ uint32_t connectionIdMax;
+ uint32_t wirelessModes;
+#define UATH_WIRELESS_MODE_11A 0x01
+#define UATH_WIRELESS_MODE_TURBO 0x02
+#define UATH_WIRELESS_MODE_11B 0x04
+#define UATH_WIRELESS_MODE_11G 0x08
+#define UATH_WIRELESS_MODE_108G 0x10
+ uint32_t chanSpreadSupport;
+ uint32_t compressSupport;
+ uint32_t burstSupport;
+ uint32_t fastFramesSupport;
+ uint32_t chapTuningSupport;
+ uint32_t turboGSupport;
+ uint32_t turboPrimeSupport;
+ uint32_t deviceType;
+ uint32_t wmeSupport;
+ uint32_t low2GhzChan;
+ uint32_t high2GhzChan;
+ uint32_t low5GhzChan;
+ uint32_t high5GhzChan;
+ uint32_t supportCipherWEP;
+ uint32_t supportCipherAES_CCM;
+ uint32_t supportCipherTKIP;
+ uint32_t supportCipherMicAES_CCM;
+ uint32_t supportMicTKIP;
+ uint32_t twiceAntennaGain5G;
+ uint32_t twiceAntennaGain2G;
+};
+
+struct uath_stat {
+ uint32_t st_badchunkseqnum;
+ uint32_t st_invalidlen;
+ uint32_t st_multichunk;
+ uint32_t st_toobigrxpkt;
+ uint32_t st_stopinprogress;
+ uint32_t st_crcerr;
+ uint32_t st_phyerr;
+ uint32_t st_decrypt_crcerr;
+ uint32_t st_decrypt_micerr;
+ uint32_t st_decomperr;
+ uint32_t st_keyerr;
+ uint32_t st_err;
+ /* CMD/RX/TX queues */
+ uint32_t st_cmd_active;
+ uint32_t st_cmd_inactive;
+ uint32_t st_cmd_pending;
+ uint32_t st_cmd_waiting;
+ uint32_t st_rx_active;
+ uint32_t st_rx_inactive;
+ uint32_t st_tx_active;
+ uint32_t st_tx_inactive;
+ uint32_t st_tx_pending;
+};
+#define UATH_STAT_INC(sc, var) (sc)->sc_stat.var++
+#define UATH_STAT_DEC(sc, var) (sc)->sc_stat.var--
+
+struct uath_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define UATH_VAP(vap) ((struct uath_vap *)(vap))
+
+struct uath_softc {
+ struct ieee80211com sc_ic;
+ struct mbufq sc_snd;
+ device_t sc_dev;
+ struct usb_device *sc_udev;
+ void *sc_cmd_dma_buf;
+ void *sc_tx_dma_buf;
+ struct mtx sc_mtx;
+ uint32_t sc_debug;
+
+ struct uath_stat sc_stat;
+ int (*sc_newstate)(struct ieee80211com *,
+ enum ieee80211_state, int);
+
+ struct usb_xfer *sc_xfer[UATH_N_XFERS];
+ struct uath_cmd sc_cmd[UATH_CMD_LIST_COUNT];
+ uath_cmdhead sc_cmd_active;
+ uath_cmdhead sc_cmd_inactive;
+ uath_cmdhead sc_cmd_pending;
+ uath_cmdhead sc_cmd_waiting;
+ struct uath_data sc_rx[UATH_RX_DATA_LIST_COUNT];
+ uath_datahead sc_rx_active;
+ uath_datahead sc_rx_inactive;
+ struct uath_data sc_tx[UATH_TX_DATA_LIST_COUNT];
+ uath_datahead sc_tx_active;
+ uath_datahead sc_tx_inactive;
+ uath_datahead sc_tx_pending;
+
+ uint32_t sc_msgid;
+ uint32_t sc_seqnum;
+ int sc_tx_timer;
+ struct callout watchdog_ch;
+ struct callout stat_ch;
+ /* multi-chunked support */
+ struct mbuf *sc_intrx_head;
+ struct mbuf *sc_intrx_tail;
+ uint8_t sc_intrx_nextnum;
+ uint32_t sc_intrx_len;
+#define UATH_MAX_INTRX_SIZE 3616
+
+ struct uath_devcap sc_devcap;
+ uint8_t sc_serial[16];
+
+ /* unsorted */
+ uint32_t sc_flags;
+#define UATH_FLAG_INVALID (1 << 1)
+#define UATH_FLAG_INITDONE (1 << 2)
+
+ struct uath_rx_radiotap_header sc_rxtap;
+ struct uath_tx_radiotap_header sc_txtap;
+};
+
+#define UATH_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define UATH_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define UATH_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
+
+#define UATH_RESET_INTRX(sc) do { \
+ (sc)->sc_intrx_head = NULL; \
+ (sc)->sc_intrx_tail = NULL; \
+ (sc)->sc_intrx_nextnum = 0; \
+ (sc)->sc_intrx_len = 0; \
+} while (0)
diff --git a/freebsd/sys/dev/usb/wlan/if_upgt.c b/freebsd/sys/dev/usb/wlan/if_upgt.c
new file mode 100644
index 00000000..50acf468
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_upgt.c
@@ -0,0 +1,2353 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $OpenBSD: if_upgt.c,v 1.35 2008/04/16 18:32:15 damien Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/firmware.h>
+#include <sys/linker.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_regdomain.h>
+
+#include <net/bpf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#include <dev/usb/wlan/if_upgtvar.h>
+
+/*
+ * Driver for the USB PrismGT devices.
+ *
+ * For now just USB 2.0 devices with the GW3887 chipset are supported.
+ * The driver has been written based on the firmware version 2.13.1.0_LM87.
+ *
+ * TODO's:
+ * - MONITOR mode test.
+ * - Add HOSTAP mode.
+ * - Add IBSS mode.
+ * - Support the USB 1.0 devices (NET2280, ISL3880, ISL3886 chipsets).
+ *
+ * Parts of this driver has been influenced by reading the p54u driver
+ * written by Jean-Baptiste Note <jean-baptiste.note@m4x.org> and
+ * Sebastien Bourdeauducq <lekernel@prism54.org>.
+ */
+
+static SYSCTL_NODE(_hw, OID_AUTO, upgt, CTLFLAG_RD, 0,
+ "USB PrismGT GW3887 driver parameters");
+
+#ifdef UPGT_DEBUG
+int upgt_debug = 0;
+SYSCTL_INT(_hw_upgt, OID_AUTO, debug, CTLFLAG_RWTUN, &upgt_debug,
+ 0, "control debugging printfs");
+enum {
+ UPGT_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ UPGT_DEBUG_RECV = 0x00000002, /* basic recv operation */
+ UPGT_DEBUG_RESET = 0x00000004, /* reset processing */
+ UPGT_DEBUG_INTR = 0x00000008, /* INTR */
+ UPGT_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */
+ UPGT_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */
+ UPGT_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */
+ UPGT_DEBUG_STAT = 0x00000080, /* statistic */
+ UPGT_DEBUG_FW = 0x00000100, /* firmware */
+ UPGT_DEBUG_ANY = 0xffffffff
+};
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (sc->sc_debug & (m)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#endif
+
+/*
+ * Prototypes.
+ */
+static device_probe_t upgt_match;
+static device_attach_t upgt_attach;
+static device_detach_t upgt_detach;
+static int upgt_alloc_tx(struct upgt_softc *);
+static int upgt_alloc_rx(struct upgt_softc *);
+static int upgt_device_reset(struct upgt_softc *);
+static void upgt_bulk_tx(struct upgt_softc *, struct upgt_data *);
+static int upgt_fw_verify(struct upgt_softc *);
+static int upgt_mem_init(struct upgt_softc *);
+static int upgt_fw_load(struct upgt_softc *);
+static int upgt_fw_copy(const uint8_t *, char *, int);
+static uint32_t upgt_crc32_le(const void *, size_t);
+static struct mbuf *
+ upgt_rxeof(struct usb_xfer *, struct upgt_data *, int *);
+static struct mbuf *
+ upgt_rx(struct upgt_softc *, uint8_t *, int, int *);
+static void upgt_txeof(struct usb_xfer *, struct upgt_data *);
+static int upgt_eeprom_read(struct upgt_softc *);
+static int upgt_eeprom_parse(struct upgt_softc *);
+static void upgt_eeprom_parse_hwrx(struct upgt_softc *, uint8_t *);
+static void upgt_eeprom_parse_freq3(struct upgt_softc *, uint8_t *, int);
+static void upgt_eeprom_parse_freq4(struct upgt_softc *, uint8_t *, int);
+static void upgt_eeprom_parse_freq6(struct upgt_softc *, uint8_t *, int);
+static uint32_t upgt_chksum_le(const uint32_t *, size_t);
+static void upgt_tx_done(struct upgt_softc *, uint8_t *);
+static void upgt_init(struct upgt_softc *);
+static void upgt_parent(struct ieee80211com *);
+static int upgt_transmit(struct ieee80211com *, struct mbuf *);
+static void upgt_start(struct upgt_softc *);
+static int upgt_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void upgt_scan_start(struct ieee80211com *);
+static void upgt_scan_end(struct ieee80211com *);
+static void upgt_set_channel(struct ieee80211com *);
+static struct ieee80211vap *upgt_vap_create(struct ieee80211com *,
+ const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void upgt_vap_delete(struct ieee80211vap *);
+static void upgt_update_mcast(struct ieee80211com *);
+static uint8_t upgt_rx_rate(struct upgt_softc *, const int);
+static void upgt_set_multi(void *);
+static void upgt_stop(struct upgt_softc *);
+static void upgt_setup_rates(struct ieee80211vap *, struct ieee80211com *);
+static int upgt_set_macfilter(struct upgt_softc *, uint8_t);
+static int upgt_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static void upgt_set_chan(struct upgt_softc *, struct ieee80211_channel *);
+static void upgt_set_led(struct upgt_softc *, int);
+static void upgt_set_led_blink(void *);
+static void upgt_get_stats(struct upgt_softc *);
+static void upgt_mem_free(struct upgt_softc *, uint32_t);
+static uint32_t upgt_mem_alloc(struct upgt_softc *);
+static void upgt_free_tx(struct upgt_softc *);
+static void upgt_free_rx(struct upgt_softc *);
+static void upgt_watchdog(void *);
+static void upgt_abort_xfers(struct upgt_softc *);
+static void upgt_abort_xfers_locked(struct upgt_softc *);
+static void upgt_sysctl_node(struct upgt_softc *);
+static struct upgt_data *
+ upgt_getbuf(struct upgt_softc *);
+static struct upgt_data *
+ upgt_gettxbuf(struct upgt_softc *);
+static int upgt_tx_start(struct upgt_softc *, struct mbuf *,
+ struct ieee80211_node *, struct upgt_data *);
+
+static const char *upgt_fwname = "upgt-gw3887";
+
+static const STRUCT_USB_HOST_ID upgt_devs[] = {
+#define UPGT_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+ /* version 2 devices */
+ UPGT_DEV(ACCTON, PRISM_GT),
+ UPGT_DEV(BELKIN, F5D7050),
+ UPGT_DEV(CISCOLINKSYS, WUSB54AG),
+ UPGT_DEV(CONCEPTRONIC, PRISM_GT),
+ UPGT_DEV(DELL, PRISM_GT_1),
+ UPGT_DEV(DELL, PRISM_GT_2),
+ UPGT_DEV(FSC, E5400),
+ UPGT_DEV(GLOBESPAN, PRISM_GT_1),
+ UPGT_DEV(GLOBESPAN, PRISM_GT_2),
+ UPGT_DEV(NETGEAR, WG111V1_2),
+ UPGT_DEV(INTERSIL, PRISM_GT),
+ UPGT_DEV(SMC, 2862WG),
+ UPGT_DEV(USR, USR5422),
+ UPGT_DEV(WISTRONNEWEB, UR045G),
+ UPGT_DEV(XYRATEX, PRISM_GT_1),
+ UPGT_DEV(XYRATEX, PRISM_GT_2),
+ UPGT_DEV(ZCOM, XG703A),
+ UPGT_DEV(ZCOM, XM142)
+};
+
+static usb_callback_t upgt_bulk_rx_callback;
+static usb_callback_t upgt_bulk_tx_callback;
+
+static const struct usb_config upgt_config[UPGT_N_XFERS] = {
+ [UPGT_BULK_TX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MCLBYTES * UPGT_TX_MAXCOUNT,
+ .flags = {
+ .force_short_xfer = 1,
+ .pipe_bof = 1
+ },
+ .callback = upgt_bulk_tx_callback,
+ .timeout = UPGT_USB_TIMEOUT, /* ms */
+ },
+ [UPGT_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = MCLBYTES * UPGT_RX_MAXCOUNT,
+ .flags = {
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = upgt_bulk_rx_callback,
+ },
+};
+
+static int
+upgt_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != UPGT_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != UPGT_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(upgt_devs, sizeof(upgt_devs), uaa));
+}
+
+static int
+upgt_attach(device_t dev)
+{
+ struct upgt_softc *sc = device_get_softc(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ uint8_t bands[IEEE80211_MODE_BYTES];
+ uint8_t iface_index = UPGT_IFACE_INDEX;
+ int error;
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+#ifdef UPGT_DEBUG
+ sc->sc_debug = upgt_debug;
+#endif
+ device_set_usb_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
+ MTX_DEF);
+ callout_init(&sc->sc_led_ch, 0);
+ callout_init(&sc->sc_watchdog_ch, 0);
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ upgt_config, UPGT_N_XFERS, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "could not allocate USB transfers, "
+ "err=%s\n", usbd_errstr(error));
+ goto fail1;
+ }
+
+ sc->sc_rx_dma_buf = usbd_xfer_get_frame_buffer(
+ sc->sc_xfer[UPGT_BULK_RX], 0);
+ sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer(
+ sc->sc_xfer[UPGT_BULK_TX], 0);
+
+ /* Setup TX and RX buffers */
+ error = upgt_alloc_tx(sc);
+ if (error)
+ goto fail2;
+ error = upgt_alloc_rx(sc);
+ if (error)
+ goto fail3;
+
+ /* Initialize the device. */
+ error = upgt_device_reset(sc);
+ if (error)
+ goto fail4;
+ /* Verify the firmware. */
+ error = upgt_fw_verify(sc);
+ if (error)
+ goto fail4;
+ /* Calculate device memory space. */
+ if (sc->sc_memaddr_frame_start == 0 || sc->sc_memaddr_frame_end == 0) {
+ device_printf(dev,
+ "could not find memory space addresses on FW\n");
+ error = EIO;
+ goto fail4;
+ }
+ sc->sc_memaddr_frame_end -= UPGT_MEMSIZE_RX + 1;
+ sc->sc_memaddr_rx_start = sc->sc_memaddr_frame_end + 1;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame start=0x%08x\n",
+ sc->sc_memaddr_frame_start);
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame end=0x%08x\n",
+ sc->sc_memaddr_frame_end);
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory address rx start=0x%08x\n",
+ sc->sc_memaddr_rx_start);
+
+ upgt_mem_init(sc);
+
+ /* Load the firmware. */
+ error = upgt_fw_load(sc);
+ if (error)
+ goto fail4;
+
+ /* Read the whole EEPROM content and parse it. */
+ error = upgt_eeprom_read(sc);
+ if (error)
+ goto fail4;
+ error = upgt_eeprom_parse(sc);
+ if (error)
+ goto fail4;
+
+ /* all works related with the device have done here. */
+ upgt_abort_xfers(sc);
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(dev);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA;
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ ieee80211_init_channels(ic, NULL, bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_raw_xmit = upgt_raw_xmit;
+ ic->ic_scan_start = upgt_scan_start;
+ ic->ic_scan_end = upgt_scan_end;
+ ic->ic_set_channel = upgt_set_channel;
+ ic->ic_vap_create = upgt_vap_create;
+ ic->ic_vap_delete = upgt_vap_delete;
+ ic->ic_update_mcast = upgt_update_mcast;
+ ic->ic_transmit = upgt_transmit;
+ ic->ic_parent = upgt_parent;
+
+ ieee80211_radiotap_attach(ic,
+ &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
+ UPGT_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ UPGT_RX_RADIOTAP_PRESENT);
+
+ upgt_sysctl_node(sc);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return (0);
+
+fail4: upgt_free_rx(sc);
+fail3: upgt_free_tx(sc);
+fail2: usbd_transfer_unsetup(sc->sc_xfer, UPGT_N_XFERS);
+fail1: mtx_destroy(&sc->sc_mtx);
+
+ return (error);
+}
+
+static void
+upgt_txeof(struct usb_xfer *xfer, struct upgt_data *data)
+{
+
+ if (data->m) {
+ /* XXX status? */
+ ieee80211_tx_complete(data->ni, data->m, 0);
+ data->m = NULL;
+ data->ni = NULL;
+ }
+}
+
+static void
+upgt_get_stats(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_stats *stats;
+
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__);
+ return;
+ }
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ memset(data_cmd->buf, 0, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ stats = (struct upgt_lmac_stats *)(mem + 1);
+
+ stats->header1.flags = 0;
+ stats->header1.type = UPGT_H1_TYPE_CTRL;
+ stats->header1.len = htole16(
+ sizeof(struct upgt_lmac_stats) - sizeof(struct upgt_lmac_header));
+
+ stats->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ stats->header2.type = htole16(UPGT_H2_TYPE_STATS);
+ stats->header2.flags = 0;
+
+ data_cmd->buflen = sizeof(*mem) + sizeof(*stats);
+
+ mem->chksum = upgt_chksum_le((uint32_t *)stats,
+ data_cmd->buflen - sizeof(*mem));
+
+ upgt_bulk_tx(sc, data_cmd);
+}
+
+static void
+upgt_parent(struct ieee80211com *ic)
+{
+ struct upgt_softc *sc = ic->ic_softc;
+ int startall = 0;
+
+ UPGT_LOCK(sc);
+ if (sc->sc_flags & UPGT_FLAG_DETACHED) {
+ UPGT_UNLOCK(sc);
+ return;
+ }
+ if (ic->ic_nrunning > 0) {
+ if (sc->sc_flags & UPGT_FLAG_INITDONE) {
+ if (ic->ic_allmulti > 0 || ic->ic_promisc > 0)
+ upgt_set_multi(sc);
+ } else {
+ upgt_init(sc);
+ startall = 1;
+ }
+ } else if (sc->sc_flags & UPGT_FLAG_INITDONE)
+ upgt_stop(sc);
+ UPGT_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+}
+
+static void
+upgt_stop(struct upgt_softc *sc)
+{
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ if (sc->sc_flags & UPGT_FLAG_INITDONE)
+ upgt_set_macfilter(sc, IEEE80211_S_INIT);
+ upgt_abort_xfers_locked(sc);
+ /* device down */
+ sc->sc_tx_timer = 0;
+ sc->sc_flags &= ~UPGT_FLAG_INITDONE;
+}
+
+static void
+upgt_set_led(struct upgt_softc *sc, int action)
+{
+ struct upgt_data *data_cmd;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_led *led;
+
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__);
+ return;
+ }
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ memset(data_cmd->buf, 0, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ led = (struct upgt_lmac_led *)(mem + 1);
+
+ led->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
+ led->header1.type = UPGT_H1_TYPE_CTRL;
+ led->header1.len = htole16(
+ sizeof(struct upgt_lmac_led) -
+ sizeof(struct upgt_lmac_header));
+
+ led->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ led->header2.type = htole16(UPGT_H2_TYPE_LED);
+ led->header2.flags = 0;
+
+ switch (action) {
+ case UPGT_LED_OFF:
+ led->mode = htole16(UPGT_LED_MODE_SET);
+ led->action_fix = 0;
+ led->action_tmp = htole16(UPGT_LED_ACTION_OFF);
+ led->action_tmp_dur = 0;
+ break;
+ case UPGT_LED_ON:
+ led->mode = htole16(UPGT_LED_MODE_SET);
+ led->action_fix = 0;
+ led->action_tmp = htole16(UPGT_LED_ACTION_ON);
+ led->action_tmp_dur = 0;
+ break;
+ case UPGT_LED_BLINK:
+ if (sc->sc_state != IEEE80211_S_RUN) {
+ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next);
+ return;
+ }
+ if (sc->sc_led_blink) {
+ /* previous blink was not finished */
+ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next);
+ return;
+ }
+ led->mode = htole16(UPGT_LED_MODE_SET);
+ led->action_fix = htole16(UPGT_LED_ACTION_OFF);
+ led->action_tmp = htole16(UPGT_LED_ACTION_ON);
+ led->action_tmp_dur = htole16(UPGT_LED_ACTION_TMP_DUR);
+ /* lock blink */
+ sc->sc_led_blink = 1;
+ callout_reset(&sc->sc_led_ch, hz, upgt_set_led_blink, sc);
+ break;
+ default:
+ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next);
+ return;
+ }
+
+ data_cmd->buflen = sizeof(*mem) + sizeof(*led);
+
+ mem->chksum = upgt_chksum_le((uint32_t *)led,
+ data_cmd->buflen - sizeof(*mem));
+
+ upgt_bulk_tx(sc, data_cmd);
+}
+
+static void
+upgt_set_led_blink(void *arg)
+{
+ struct upgt_softc *sc = arg;
+
+ /* blink finished, we are ready for a next one */
+ sc->sc_led_blink = 0;
+}
+
+static void
+upgt_init(struct upgt_softc *sc)
+{
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ if (sc->sc_flags & UPGT_FLAG_INITDONE)
+ upgt_stop(sc);
+
+ usbd_transfer_start(sc->sc_xfer[UPGT_BULK_RX]);
+
+ (void)upgt_set_macfilter(sc, IEEE80211_S_SCAN);
+
+ sc->sc_flags |= UPGT_FLAG_INITDONE;
+
+ callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc);
+}
+
+static int
+upgt_set_macfilter(struct upgt_softc *sc, uint8_t state)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni;
+ struct upgt_data *data_cmd;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_filter *filter;
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ device_printf(sc->sc_dev, "out of TX buffers.\n");
+ return (ENOBUFS);
+ }
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ memset(data_cmd->buf, 0, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ filter = (struct upgt_lmac_filter *)(mem + 1);
+
+ filter->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
+ filter->header1.type = UPGT_H1_TYPE_CTRL;
+ filter->header1.len = htole16(
+ sizeof(struct upgt_lmac_filter) -
+ sizeof(struct upgt_lmac_header));
+
+ filter->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ filter->header2.type = htole16(UPGT_H2_TYPE_MACFILTER);
+ filter->header2.flags = 0;
+
+ switch (state) {
+ case IEEE80211_S_INIT:
+ DPRINTF(sc, UPGT_DEBUG_STATE, "%s: set MAC filter to INIT\n",
+ __func__);
+ filter->type = htole16(UPGT_FILTER_TYPE_RESET);
+ break;
+ case IEEE80211_S_SCAN:
+ DPRINTF(sc, UPGT_DEBUG_STATE,
+ "set MAC filter to SCAN (bssid %s)\n",
+ ether_sprintf(ieee80211broadcastaddr));
+ filter->type = htole16(UPGT_FILTER_TYPE_NONE);
+ IEEE80211_ADDR_COPY(filter->dst,
+ vap ? vap->iv_myaddr : ic->ic_macaddr);
+ IEEE80211_ADDR_COPY(filter->src, ieee80211broadcastaddr);
+ filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1);
+ filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
+ filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2);
+ filter->rxhw = htole32(sc->sc_eeprom_hwrx);
+ filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3);
+ break;
+ case IEEE80211_S_RUN:
+ ni = ieee80211_ref_node(vap->iv_bss);
+ /* XXX monitor mode isn't tested yet. */
+ if (vap->iv_opmode == IEEE80211_M_MONITOR) {
+ filter->type = htole16(UPGT_FILTER_TYPE_MONITOR);
+ IEEE80211_ADDR_COPY(filter->dst,
+ vap ? vap->iv_myaddr : ic->ic_macaddr);
+ IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid);
+ filter->unknown1 = htole16(UPGT_FILTER_MONITOR_UNKNOWN1);
+ filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
+ filter->unknown2 = htole16(UPGT_FILTER_MONITOR_UNKNOWN2);
+ filter->rxhw = htole32(sc->sc_eeprom_hwrx);
+ filter->unknown3 = htole16(UPGT_FILTER_MONITOR_UNKNOWN3);
+ } else {
+ DPRINTF(sc, UPGT_DEBUG_STATE,
+ "set MAC filter to RUN (bssid %s)\n",
+ ether_sprintf(ni->ni_bssid));
+ filter->type = htole16(UPGT_FILTER_TYPE_STA);
+ IEEE80211_ADDR_COPY(filter->dst,
+ vap ? vap->iv_myaddr : ic->ic_macaddr);
+ IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid);
+ filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1);
+ filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
+ filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2);
+ filter->rxhw = htole32(sc->sc_eeprom_hwrx);
+ filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3);
+ }
+ ieee80211_free_node(ni);
+ break;
+ default:
+ device_printf(sc->sc_dev,
+ "MAC filter does not know that state\n");
+ break;
+ }
+
+ data_cmd->buflen = sizeof(*mem) + sizeof(*filter);
+
+ mem->chksum = upgt_chksum_le((uint32_t *)filter,
+ data_cmd->buflen - sizeof(*mem));
+
+ upgt_bulk_tx(sc, data_cmd);
+
+ return (0);
+}
+
+static void
+upgt_setup_rates(struct ieee80211vap *vap, struct ieee80211com *ic)
+{
+ struct upgt_softc *sc = ic->ic_softc;
+ const struct ieee80211_txparam *tp;
+
+ /*
+ * 0x01 = OFMD6 0x10 = DS1
+ * 0x04 = OFDM9 0x11 = DS2
+ * 0x06 = OFDM12 0x12 = DS5
+ * 0x07 = OFDM18 0x13 = DS11
+ * 0x08 = OFDM24
+ * 0x09 = OFDM36
+ * 0x0a = OFDM48
+ * 0x0b = OFDM54
+ */
+ const uint8_t rateset_auto_11b[] =
+ { 0x13, 0x13, 0x12, 0x11, 0x11, 0x10, 0x10, 0x10 };
+ const uint8_t rateset_auto_11g[] =
+ { 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x04, 0x01 };
+ const uint8_t rateset_fix_11bg[] =
+ { 0x10, 0x11, 0x12, 0x13, 0x01, 0x04, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b };
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+
+ /* XXX */
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
+ /*
+ * Automatic rate control is done by the device.
+ * We just pass the rateset from which the device
+ * will pickup a rate.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ memcpy(sc->sc_cur_rateset, rateset_auto_11b,
+ sizeof(sc->sc_cur_rateset));
+ if (ic->ic_curmode == IEEE80211_MODE_11G ||
+ ic->ic_curmode == IEEE80211_MODE_AUTO)
+ memcpy(sc->sc_cur_rateset, rateset_auto_11g,
+ sizeof(sc->sc_cur_rateset));
+ } else {
+ /* set a fixed rate */
+ memset(sc->sc_cur_rateset, rateset_fix_11bg[tp->ucastrate],
+ sizeof(sc->sc_cur_rateset));
+ }
+}
+
+static void
+upgt_set_multi(void *arg)
+{
+
+ /* XXX don't know how to set a device. Lack of docs. */
+}
+
+static int
+upgt_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct upgt_softc *sc = ic->ic_softc;
+ int error;
+
+ UPGT_LOCK(sc);
+ if ((sc->sc_flags & UPGT_FLAG_INITDONE) == 0) {
+ UPGT_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ UPGT_UNLOCK(sc);
+ return (error);
+ }
+ upgt_start(sc);
+ UPGT_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+upgt_start(struct upgt_softc *sc)
+{
+ struct upgt_data *data_tx;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ if ((sc->sc_flags & UPGT_FLAG_INITDONE) == 0)
+ return;
+
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ data_tx = upgt_gettxbuf(sc);
+ if (data_tx == NULL) {
+ mbufq_prepend(&sc->sc_snd, m);
+ break;
+ }
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+
+ if (upgt_tx_start(sc, m, ni, data_tx) != 0) {
+ if_inc_counter(ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, data_tx, next);
+ UPGT_STAT_INC(sc, st_tx_inactive);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ sc->sc_tx_timer = 5;
+ }
+}
+
+static int
+upgt_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct upgt_softc *sc = ic->ic_softc;
+ struct upgt_data *data_tx = NULL;
+
+ UPGT_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if (!(sc->sc_flags & UPGT_FLAG_INITDONE)) {
+ m_freem(m);
+ UPGT_UNLOCK(sc);
+ return ENETDOWN;
+ }
+
+ data_tx = upgt_gettxbuf(sc);
+ if (data_tx == NULL) {
+ m_freem(m);
+ UPGT_UNLOCK(sc);
+ return (ENOBUFS);
+ }
+
+ if (upgt_tx_start(sc, m, ni, data_tx) != 0) {
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, data_tx, next);
+ UPGT_STAT_INC(sc, st_tx_inactive);
+ UPGT_UNLOCK(sc);
+ return (EIO);
+ }
+ UPGT_UNLOCK(sc);
+
+ sc->sc_tx_timer = 5;
+ return (0);
+}
+
+static void
+upgt_watchdog(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ if (sc->sc_tx_timer > 0) {
+ if (--sc->sc_tx_timer == 0) {
+ device_printf(sc->sc_dev, "watchdog timeout\n");
+ /* upgt_init(sc); XXX needs a process context ? */
+ counter_u64_add(ic->ic_oerrors, 1);
+ return;
+ }
+ callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc);
+ }
+}
+
+static uint32_t
+upgt_mem_alloc(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_memory.pages; i++) {
+ if (sc->sc_memory.page[i].used == 0) {
+ sc->sc_memory.page[i].used = 1;
+ return (sc->sc_memory.page[i].addr);
+ }
+ }
+
+ return (0);
+}
+
+static void
+upgt_scan_start(struct ieee80211com *ic)
+{
+ /* do nothing. */
+}
+
+static void
+upgt_scan_end(struct ieee80211com *ic)
+{
+ /* do nothing. */
+}
+
+static void
+upgt_set_channel(struct ieee80211com *ic)
+{
+ struct upgt_softc *sc = ic->ic_softc;
+
+ UPGT_LOCK(sc);
+ upgt_set_chan(sc, ic->ic_curchan);
+ UPGT_UNLOCK(sc);
+}
+
+static void
+upgt_set_chan(struct upgt_softc *sc, struct ieee80211_channel *c)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct upgt_data *data_cmd;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_channel *chan;
+ int channel;
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ channel = ieee80211_chan2ieee(ic, c);
+ if (channel == 0 || channel == IEEE80211_CHAN_ANY) {
+ /* XXX should NEVER happen */
+ device_printf(sc->sc_dev,
+ "%s: invalid channel %x\n", __func__, channel);
+ return;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_STATE, "%s: channel %d\n", __func__, channel);
+
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__);
+ return;
+ }
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ memset(data_cmd->buf, 0, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ chan = (struct upgt_lmac_channel *)(mem + 1);
+
+ chan->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
+ chan->header1.type = UPGT_H1_TYPE_CTRL;
+ chan->header1.len = htole16(
+ sizeof(struct upgt_lmac_channel) - sizeof(struct upgt_lmac_header));
+
+ chan->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ chan->header2.type = htole16(UPGT_H2_TYPE_CHANNEL);
+ chan->header2.flags = 0;
+
+ chan->unknown1 = htole16(UPGT_CHANNEL_UNKNOWN1);
+ chan->unknown2 = htole16(UPGT_CHANNEL_UNKNOWN2);
+ chan->freq6 = sc->sc_eeprom_freq6[channel];
+ chan->settings = sc->sc_eeprom_freq6_settings;
+ chan->unknown3 = UPGT_CHANNEL_UNKNOWN3;
+
+ memcpy(chan->freq3_1, &sc->sc_eeprom_freq3[channel].data,
+ sizeof(chan->freq3_1));
+ memcpy(chan->freq4, &sc->sc_eeprom_freq4[channel],
+ sizeof(sc->sc_eeprom_freq4[channel]));
+ memcpy(chan->freq3_2, &sc->sc_eeprom_freq3[channel].data,
+ sizeof(chan->freq3_2));
+
+ data_cmd->buflen = sizeof(*mem) + sizeof(*chan);
+
+ mem->chksum = upgt_chksum_le((uint32_t *)chan,
+ data_cmd->buflen - sizeof(*mem));
+
+ upgt_bulk_tx(sc, data_cmd);
+}
+
+static struct ieee80211vap *
+upgt_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct upgt_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ uvp = malloc(sizeof(struct upgt_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) {
+ /* out of memory */
+ free(uvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = upgt_newstate;
+
+ /* setup device rates */
+ upgt_setup_rates(vap, ic);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status, mac);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static int
+upgt_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct upgt_vap *uvp = UPGT_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct upgt_softc *sc = ic->ic_softc;
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+
+ IEEE80211_UNLOCK(ic);
+ UPGT_LOCK(sc);
+ callout_stop(&sc->sc_led_ch);
+ callout_stop(&sc->sc_watchdog_ch);
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ /* do not accept any frames if the device is down */
+ (void)upgt_set_macfilter(sc, sc->sc_state);
+ upgt_set_led(sc, UPGT_LED_OFF);
+ break;
+ case IEEE80211_S_SCAN:
+ upgt_set_chan(sc, ic->ic_curchan);
+ break;
+ case IEEE80211_S_AUTH:
+ upgt_set_chan(sc, ic->ic_curchan);
+ break;
+ case IEEE80211_S_ASSOC:
+ break;
+ case IEEE80211_S_RUN:
+ upgt_set_macfilter(sc, sc->sc_state);
+ upgt_set_led(sc, UPGT_LED_ON);
+ break;
+ default:
+ break;
+ }
+ UPGT_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (uvp->newstate(vap, nstate, arg));
+}
+
+static void
+upgt_vap_delete(struct ieee80211vap *vap)
+{
+ struct upgt_vap *uvp = UPGT_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static void
+upgt_update_mcast(struct ieee80211com *ic)
+{
+ struct upgt_softc *sc = ic->ic_softc;
+
+ upgt_set_multi(sc);
+}
+
+static int
+upgt_eeprom_parse(struct upgt_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct upgt_eeprom_header *eeprom_header;
+ struct upgt_eeprom_option *eeprom_option;
+ uint16_t option_len;
+ uint16_t option_type;
+ uint16_t preamble_len;
+ int option_end = 0;
+
+ /* calculate eeprom options start offset */
+ eeprom_header = (struct upgt_eeprom_header *)sc->sc_eeprom;
+ preamble_len = le16toh(eeprom_header->preamble_len);
+ eeprom_option = (struct upgt_eeprom_option *)(sc->sc_eeprom +
+ (sizeof(struct upgt_eeprom_header) + preamble_len));
+
+ while (!option_end) {
+
+ /* sanity check */
+ if (eeprom_option >= (struct upgt_eeprom_option *)
+ (sc->sc_eeprom + UPGT_EEPROM_SIZE)) {
+ return (EINVAL);
+ }
+
+ /* the eeprom option length is stored in words */
+ option_len =
+ (le16toh(eeprom_option->len) - 1) * sizeof(uint16_t);
+ option_type =
+ le16toh(eeprom_option->type);
+
+ /* sanity check */
+ if (option_len == 0 || option_len >= UPGT_EEPROM_SIZE)
+ return (EINVAL);
+
+ switch (option_type) {
+ case UPGT_EEPROM_TYPE_NAME:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM name len=%d\n", option_len);
+ break;
+ case UPGT_EEPROM_TYPE_SERIAL:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM serial len=%d\n", option_len);
+ break;
+ case UPGT_EEPROM_TYPE_MAC:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM mac len=%d\n", option_len);
+
+ IEEE80211_ADDR_COPY(ic->ic_macaddr,
+ eeprom_option->data);
+ break;
+ case UPGT_EEPROM_TYPE_HWRX:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM hwrx len=%d\n", option_len);
+
+ upgt_eeprom_parse_hwrx(sc, eeprom_option->data);
+ break;
+ case UPGT_EEPROM_TYPE_CHIP:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM chip len=%d\n", option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ3:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM freq3 len=%d\n", option_len);
+
+ upgt_eeprom_parse_freq3(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ4:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM freq4 len=%d\n", option_len);
+
+ upgt_eeprom_parse_freq4(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ5:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM freq5 len=%d\n", option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ6:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM freq6 len=%d\n", option_len);
+
+ upgt_eeprom_parse_freq6(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_END:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM end len=%d\n", option_len);
+ option_end = 1;
+ break;
+ case UPGT_EEPROM_TYPE_OFF:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "%s: EEPROM off without end option\n", __func__);
+ return (EIO);
+ default:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "EEPROM unknown type 0x%04x len=%d\n",
+ option_type, option_len);
+ break;
+ }
+
+ /* jump to next EEPROM option */
+ eeprom_option = (struct upgt_eeprom_option *)
+ (eeprom_option->data + option_len);
+ }
+ return (0);
+}
+
+static void
+upgt_eeprom_parse_freq3(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_eeprom_freq3_header *freq3_header;
+ struct upgt_lmac_freq3 *freq3;
+ int i;
+ int elements;
+ int flags;
+ unsigned channel;
+
+ freq3_header = (struct upgt_eeprom_freq3_header *)data;
+ freq3 = (struct upgt_lmac_freq3 *)(freq3_header + 1);
+
+ flags = freq3_header->flags;
+ elements = freq3_header->elements;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d\n",
+ flags, elements);
+
+ if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq3[0])))
+ return;
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(le16toh(freq3[i].freq), 0);
+ if (channel >= IEEE80211_CHAN_MAX)
+ continue;
+
+ sc->sc_eeprom_freq3[channel] = freq3[i];
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n",
+ le16toh(sc->sc_eeprom_freq3[channel].freq), channel);
+ }
+}
+
+void
+upgt_eeprom_parse_freq4(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_eeprom_freq4_header *freq4_header;
+ struct upgt_eeprom_freq4_1 *freq4_1;
+ struct upgt_eeprom_freq4_2 *freq4_2;
+ int i;
+ int j;
+ int elements;
+ int settings;
+ int flags;
+ unsigned channel;
+
+ freq4_header = (struct upgt_eeprom_freq4_header *)data;
+ freq4_1 = (struct upgt_eeprom_freq4_1 *)(freq4_header + 1);
+ flags = freq4_header->flags;
+ elements = freq4_header->elements;
+ settings = freq4_header->settings;
+
+ /* we need this value later */
+ sc->sc_eeprom_freq6_settings = freq4_header->settings;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d settings=%d\n",
+ flags, elements, settings);
+
+ if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq4_1[0])))
+ return;
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(le16toh(freq4_1[i].freq), 0);
+ if (channel >= IEEE80211_CHAN_MAX)
+ continue;
+
+ freq4_2 = (struct upgt_eeprom_freq4_2 *)freq4_1[i].data;
+ for (j = 0; j < settings; j++) {
+ sc->sc_eeprom_freq4[channel][j].cmd = freq4_2[j];
+ sc->sc_eeprom_freq4[channel][j].pad = 0;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n",
+ le16toh(freq4_1[i].freq), channel);
+ }
+}
+
+void
+upgt_eeprom_parse_freq6(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_lmac_freq6 *freq6;
+ int i;
+ int elements;
+ unsigned channel;
+
+ freq6 = (struct upgt_lmac_freq6 *)data;
+ elements = len / sizeof(struct upgt_lmac_freq6);
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "elements=%d\n", elements);
+
+ if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq6[0])))
+ return;
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(le16toh(freq6[i].freq), 0);
+ if (channel >= IEEE80211_CHAN_MAX)
+ continue;
+
+ sc->sc_eeprom_freq6[channel] = freq6[i];
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n",
+ le16toh(sc->sc_eeprom_freq6[channel].freq), channel);
+ }
+}
+
+static void
+upgt_eeprom_parse_hwrx(struct upgt_softc *sc, uint8_t *data)
+{
+ struct upgt_eeprom_option_hwrx *option_hwrx;
+
+ option_hwrx = (struct upgt_eeprom_option_hwrx *)data;
+
+ sc->sc_eeprom_hwrx = option_hwrx->rxfilter - UPGT_EEPROM_RX_CONST;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "hwrx option value=0x%04x\n",
+ sc->sc_eeprom_hwrx);
+}
+
+static int
+upgt_eeprom_read(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_eeprom *eeprom;
+ int block, error, offset;
+
+ UPGT_LOCK(sc);
+ usb_pause_mtx(&sc->sc_mtx, 100);
+
+ offset = 0;
+ block = UPGT_EEPROM_BLOCK_SIZE;
+ while (offset < UPGT_EEPROM_SIZE) {
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "request EEPROM block (offset=%d, len=%d)\n", offset, block);
+
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ UPGT_UNLOCK(sc);
+ return (ENOBUFS);
+ }
+
+ /*
+ * Transmit the URB containing the CMD data.
+ */
+ memset(data_cmd->buf, 0, MCLBYTES);
+
+ mem = (struct upgt_lmac_mem *)data_cmd->buf;
+ mem->addr = htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ eeprom = (struct upgt_lmac_eeprom *)(mem + 1);
+ eeprom->header1.flags = 0;
+ eeprom->header1.type = UPGT_H1_TYPE_CTRL;
+ eeprom->header1.len = htole16((
+ sizeof(struct upgt_lmac_eeprom) -
+ sizeof(struct upgt_lmac_header)) + block);
+
+ eeprom->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ eeprom->header2.type = htole16(UPGT_H2_TYPE_EEPROM);
+ eeprom->header2.flags = 0;
+
+ eeprom->offset = htole16(offset);
+ eeprom->len = htole16(block);
+
+ data_cmd->buflen = sizeof(*mem) + sizeof(*eeprom) + block;
+
+ mem->chksum = upgt_chksum_le((uint32_t *)eeprom,
+ data_cmd->buflen - sizeof(*mem));
+ upgt_bulk_tx(sc, data_cmd);
+
+ error = mtx_sleep(sc, &sc->sc_mtx, 0, "eeprom_request", hz);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "timeout while waiting for EEPROM data\n");
+ UPGT_UNLOCK(sc);
+ return (EIO);
+ }
+
+ offset += block;
+ if (UPGT_EEPROM_SIZE - offset < block)
+ block = UPGT_EEPROM_SIZE - offset;
+ }
+
+ UPGT_UNLOCK(sc);
+ return (0);
+}
+
+/*
+ * When a rx data came in the function returns a mbuf and a rssi values.
+ */
+static struct mbuf *
+upgt_rxeof(struct usb_xfer *xfer, struct upgt_data *data, int *rssi)
+{
+ struct mbuf *m = NULL;
+ struct upgt_softc *sc = usbd_xfer_softc(xfer);
+ struct upgt_lmac_header *header;
+ struct upgt_lmac_eeprom *eeprom;
+ uint8_t h1_type;
+ uint16_t h2_type;
+ int actlen, sumlen;
+
+ usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ if (actlen < 1)
+ return (NULL);
+
+ /* Check only at the very beginning. */
+ if (!(sc->sc_flags & UPGT_FLAG_FWLOADED) &&
+ (memcmp(data->buf, "OK", 2) == 0)) {
+ sc->sc_flags |= UPGT_FLAG_FWLOADED;
+ wakeup_one(sc);
+ return (NULL);
+ }
+
+ if (actlen < (int)UPGT_RX_MINSZ)
+ return (NULL);
+
+ /*
+ * Check what type of frame came in.
+ */
+ header = (struct upgt_lmac_header *)(data->buf + 4);
+
+ h1_type = header->header1.type;
+ h2_type = le16toh(header->header2.type);
+
+ if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_EEPROM) {
+ eeprom = (struct upgt_lmac_eeprom *)(data->buf + 4);
+ uint16_t eeprom_offset = le16toh(eeprom->offset);
+ uint16_t eeprom_len = le16toh(eeprom->len);
+
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "received EEPROM block (offset=%d, len=%d)\n",
+ eeprom_offset, eeprom_len);
+
+ memcpy(sc->sc_eeprom + eeprom_offset,
+ data->buf + sizeof(struct upgt_lmac_eeprom) + 4,
+ eeprom_len);
+
+ /* EEPROM data has arrived in time, wakeup. */
+ wakeup(sc);
+ } else if (h1_type == UPGT_H1_TYPE_CTRL &&
+ h2_type == UPGT_H2_TYPE_TX_DONE) {
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: received 802.11 TX done\n",
+ __func__);
+ upgt_tx_done(sc, data->buf + 4);
+ } else if (h1_type == UPGT_H1_TYPE_RX_DATA ||
+ h1_type == UPGT_H1_TYPE_RX_DATA_MGMT) {
+ DPRINTF(sc, UPGT_DEBUG_RECV, "%s: received 802.11 RX data\n",
+ __func__);
+ m = upgt_rx(sc, data->buf + 4, le16toh(header->header1.len),
+ rssi);
+ } else if (h1_type == UPGT_H1_TYPE_CTRL &&
+ h2_type == UPGT_H2_TYPE_STATS) {
+ DPRINTF(sc, UPGT_DEBUG_STAT, "%s: received statistic data\n",
+ __func__);
+ /* TODO: what could we do with the statistic data? */
+ } else {
+ /* ignore unknown frame types */
+ DPRINTF(sc, UPGT_DEBUG_INTR,
+ "received unknown frame type 0x%02x\n",
+ header->header1.type);
+ }
+ return (m);
+}
+
+/*
+ * The firmware awaits a checksum for each frame we send to it.
+ * The algorithm used therefor is uncommon but somehow similar to CRC32.
+ */
+static uint32_t
+upgt_chksum_le(const uint32_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t crc = 0;
+
+ for (i = 0; i < size; i += sizeof(uint32_t)) {
+ crc = htole32(crc ^ *buf++);
+ crc = htole32((crc >> 5) ^ (crc << 3));
+ }
+
+ return (crc);
+}
+
+static struct mbuf *
+upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen, int *rssi)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct upgt_lmac_rx_desc *rxdesc;
+ struct mbuf *m;
+
+ /*
+ * don't pass packets to the ieee80211 framework if the driver isn't
+ * RUNNING.
+ */
+ if (!(sc->sc_flags & UPGT_FLAG_INITDONE))
+ return (NULL);
+
+ /* access RX packet descriptor */
+ rxdesc = (struct upgt_lmac_rx_desc *)data;
+
+ /* create mbuf which is suitable for strict alignment archs */
+ KASSERT((pkglen + ETHER_ALIGN) < MCLBYTES,
+ ("A current mbuf storage is small (%d)", pkglen + ETHER_ALIGN));
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ device_printf(sc->sc_dev, "could not create RX mbuf\n");
+ return (NULL);
+ }
+ m_adj(m, ETHER_ALIGN);
+ memcpy(mtod(m, char *), rxdesc->data, pkglen);
+ /* trim FCS */
+ m->m_len = m->m_pkthdr.len = pkglen - IEEE80211_CRC_LEN;
+
+ if (ieee80211_radiotap_active(ic)) {
+ struct upgt_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = 0;
+ tap->wr_rate = upgt_rx_rate(sc, rxdesc->rate);
+ tap->wr_antsignal = rxdesc->rssi;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_RX_PROC, "%s: RX done\n", __func__);
+ *rssi = rxdesc->rssi;
+ return (m);
+}
+
+static uint8_t
+upgt_rx_rate(struct upgt_softc *sc, const int rate)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ static const uint8_t cck_upgt2rate[4] = { 2, 4, 11, 22 };
+ static const uint8_t ofdm_upgt2rate[12] =
+ { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 };
+
+ if (ic->ic_curmode == IEEE80211_MODE_11B &&
+ !(rate < 0 || rate > 3))
+ return cck_upgt2rate[rate & 0xf];
+
+ if (ic->ic_curmode == IEEE80211_MODE_11G &&
+ !(rate < 0 || rate > 11))
+ return ofdm_upgt2rate[rate & 0xf];
+
+ return (0);
+}
+
+static void
+upgt_tx_done(struct upgt_softc *sc, uint8_t *data)
+{
+ struct upgt_lmac_tx_done_desc *desc;
+ int i, freed = 0;
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ desc = (struct upgt_lmac_tx_done_desc *)data;
+
+ for (i = 0; i < UPGT_TX_MAXCOUNT; i++) {
+ struct upgt_data *data_tx = &sc->sc_tx_data[i];
+
+ if (data_tx->addr == le32toh(desc->header2.reqid)) {
+ upgt_mem_free(sc, data_tx->addr);
+ data_tx->ni = NULL;
+ data_tx->addr = 0;
+ data_tx->m = NULL;
+
+ DPRINTF(sc, UPGT_DEBUG_TX_PROC,
+ "TX done: memaddr=0x%08x, status=0x%04x, rssi=%d, ",
+ le32toh(desc->header2.reqid),
+ le16toh(desc->status), le16toh(desc->rssi));
+ DPRINTF(sc, UPGT_DEBUG_TX_PROC, "seq=%d\n",
+ le16toh(desc->seq));
+
+ freed++;
+ }
+ }
+
+ if (freed != 0) {
+ UPGT_UNLOCK(sc);
+ sc->sc_tx_timer = 0;
+ upgt_start(sc);
+ UPGT_LOCK(sc);
+ }
+}
+
+static void
+upgt_mem_free(struct upgt_softc *sc, uint32_t addr)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_memory.pages; i++) {
+ if (sc->sc_memory.page[i].addr == addr) {
+ sc->sc_memory.page[i].used = 0;
+ return;
+ }
+ }
+
+ device_printf(sc->sc_dev,
+ "could not free memory address 0x%08x\n", addr);
+}
+
+static int
+upgt_fw_load(struct upgt_softc *sc)
+{
+ const struct firmware *fw;
+ struct upgt_data *data_cmd;
+ struct upgt_fw_x2_header *x2;
+ char start_fwload_cmd[] = { 0x3c, 0x0d };
+ int error = 0;
+ size_t offset;
+ int bsize;
+ int n;
+ uint32_t crc32;
+
+ fw = firmware_get(upgt_fwname);
+ if (fw == NULL) {
+ device_printf(sc->sc_dev, "could not read microcode %s\n",
+ upgt_fwname);
+ return (EIO);
+ }
+
+ UPGT_LOCK(sc);
+
+ /* send firmware start load command */
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ data_cmd->buflen = sizeof(start_fwload_cmd);
+ memcpy(data_cmd->buf, start_fwload_cmd, data_cmd->buflen);
+ upgt_bulk_tx(sc, data_cmd);
+
+ /* send X2 header */
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ data_cmd->buflen = sizeof(struct upgt_fw_x2_header);
+ x2 = (struct upgt_fw_x2_header *)data_cmd->buf;
+ memcpy(x2->signature, UPGT_X2_SIGNATURE, UPGT_X2_SIGNATURE_SIZE);
+ x2->startaddr = htole32(UPGT_MEMADDR_FIRMWARE_START);
+ x2->len = htole32(fw->datasize);
+ x2->crc = upgt_crc32_le((uint8_t *)data_cmd->buf +
+ UPGT_X2_SIGNATURE_SIZE,
+ sizeof(struct upgt_fw_x2_header) - UPGT_X2_SIGNATURE_SIZE -
+ sizeof(uint32_t));
+ upgt_bulk_tx(sc, data_cmd);
+
+ /* download firmware */
+ for (offset = 0; offset < fw->datasize; offset += bsize) {
+ if (fw->datasize - offset > UPGT_FW_BLOCK_SIZE)
+ bsize = UPGT_FW_BLOCK_SIZE;
+ else
+ bsize = fw->datasize - offset;
+
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ n = upgt_fw_copy((const uint8_t *)fw->data + offset,
+ data_cmd->buf, bsize);
+ data_cmd->buflen = bsize;
+ upgt_bulk_tx(sc, data_cmd);
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "FW offset=%d, read=%d, sent=%d\n",
+ offset, n, bsize);
+ bsize = n;
+ }
+ DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware downloaded\n", __func__);
+
+ /* load firmware */
+ data_cmd = upgt_getbuf(sc);
+ if (data_cmd == NULL) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ crc32 = upgt_crc32_le(fw->data, fw->datasize);
+ *((uint32_t *)(data_cmd->buf) ) = crc32;
+ *((uint8_t *)(data_cmd->buf) + 4) = 'g';
+ *((uint8_t *)(data_cmd->buf) + 5) = '\r';
+ data_cmd->buflen = 6;
+ upgt_bulk_tx(sc, data_cmd);
+
+ /* waiting 'OK' response. */
+ usbd_transfer_start(sc->sc_xfer[UPGT_BULK_RX]);
+ error = mtx_sleep(sc, &sc->sc_mtx, 0, "upgtfw", 2 * hz);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "firmware load failed\n");
+ error = EIO;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware loaded\n", __func__);
+fail:
+ UPGT_UNLOCK(sc);
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ return (error);
+}
+
+static uint32_t
+upgt_crc32_le(const void *buf, size_t size)
+{
+ uint32_t crc;
+
+ crc = ether_crc32_le(buf, size);
+
+ /* apply final XOR value as common for CRC-32 */
+ crc = htole32(crc ^ 0xffffffffU);
+
+ return (crc);
+}
+
+/*
+ * While copying the version 2 firmware, we need to replace two characters:
+ *
+ * 0x7e -> 0x7d 0x5e
+ * 0x7d -> 0x7d 0x5d
+ */
+static int
+upgt_fw_copy(const uint8_t *src, char *dst, int size)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < size && j < size; i++) {
+ switch (src[i]) {
+ case 0x7e:
+ dst[j] = 0x7d;
+ j++;
+ dst[j] = 0x5e;
+ j++;
+ break;
+ case 0x7d:
+ dst[j] = 0x7d;
+ j++;
+ dst[j] = 0x5d;
+ j++;
+ break;
+ default:
+ dst[j] = src[i];
+ j++;
+ break;
+ }
+ }
+
+ return (i);
+}
+
+static int
+upgt_mem_init(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < UPGT_MEMORY_MAX_PAGES; i++) {
+ sc->sc_memory.page[i].used = 0;
+
+ if (i == 0) {
+ /*
+ * The first memory page is always reserved for
+ * command data.
+ */
+ sc->sc_memory.page[i].addr =
+ sc->sc_memaddr_frame_start + MCLBYTES;
+ } else {
+ sc->sc_memory.page[i].addr =
+ sc->sc_memory.page[i - 1].addr + MCLBYTES;
+ }
+
+ if (sc->sc_memory.page[i].addr + MCLBYTES >=
+ sc->sc_memaddr_frame_end)
+ break;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory address page %d=0x%08x\n",
+ i, sc->sc_memory.page[i].addr);
+ }
+
+ sc->sc_memory.pages = i;
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "memory pages=%d\n", sc->sc_memory.pages);
+ return (0);
+}
+
+static int
+upgt_fw_verify(struct upgt_softc *sc)
+{
+ const struct firmware *fw;
+ const struct upgt_fw_bra_option *bra_opt;
+ const struct upgt_fw_bra_descr *descr;
+ const uint8_t *p;
+ const uint32_t *uc;
+ uint32_t bra_option_type, bra_option_len;
+ size_t offset;
+ int bra_end = 0;
+ int error = 0;
+
+ fw = firmware_get(upgt_fwname);
+ if (fw == NULL) {
+ device_printf(sc->sc_dev, "could not read microcode %s\n",
+ upgt_fwname);
+ return EIO;
+ }
+
+ /*
+ * Seek to beginning of Boot Record Area (BRA).
+ */
+ for (offset = 0; offset < fw->datasize; offset += sizeof(*uc)) {
+ uc = (const uint32_t *)((const uint8_t *)fw->data + offset);
+ if (*uc == 0)
+ break;
+ }
+ for (; offset < fw->datasize; offset += sizeof(*uc)) {
+ uc = (const uint32_t *)((const uint8_t *)fw->data + offset);
+ if (*uc != 0)
+ break;
+ }
+ if (offset == fw->datasize) {
+ device_printf(sc->sc_dev,
+ "firmware Boot Record Area not found\n");
+ error = EIO;
+ goto fail;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "firmware Boot Record Area found at offset %d\n", offset);
+
+ /*
+ * Parse Boot Record Area (BRA) options.
+ */
+ while (offset < fw->datasize && bra_end == 0) {
+ /* get current BRA option */
+ p = (const uint8_t *)fw->data + offset;
+ bra_opt = (const struct upgt_fw_bra_option *)p;
+ bra_option_type = le32toh(bra_opt->type);
+ bra_option_len = le32toh(bra_opt->len) * sizeof(*uc);
+
+ switch (bra_option_type) {
+ case UPGT_BRA_TYPE_FW:
+ DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_FW len=%d\n",
+ bra_option_len);
+
+ if (bra_option_len != UPGT_BRA_FWTYPE_SIZE) {
+ device_printf(sc->sc_dev,
+ "wrong UPGT_BRA_TYPE_FW len\n");
+ error = EIO;
+ goto fail;
+ }
+ if (memcmp(UPGT_BRA_FWTYPE_LM86, bra_opt->data,
+ bra_option_len) == 0) {
+ sc->sc_fw_type = UPGT_FWTYPE_LM86;
+ break;
+ }
+ if (memcmp(UPGT_BRA_FWTYPE_LM87, bra_opt->data,
+ bra_option_len) == 0) {
+ sc->sc_fw_type = UPGT_FWTYPE_LM87;
+ break;
+ }
+ device_printf(sc->sc_dev,
+ "unsupported firmware type\n");
+ error = EIO;
+ goto fail;
+ case UPGT_BRA_TYPE_VERSION:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "UPGT_BRA_TYPE_VERSION len=%d\n", bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_DEPIF:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "UPGT_BRA_TYPE_DEPIF len=%d\n", bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_EXPIF:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "UPGT_BRA_TYPE_EXPIF len=%d\n", bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_DESCR:
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "UPGT_BRA_TYPE_DESCR len=%d\n", bra_option_len);
+
+ descr = (const struct upgt_fw_bra_descr *)bra_opt->data;
+
+ sc->sc_memaddr_frame_start =
+ le32toh(descr->memaddr_space_start);
+ sc->sc_memaddr_frame_end =
+ le32toh(descr->memaddr_space_end);
+
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "memory address space start=0x%08x\n",
+ sc->sc_memaddr_frame_start);
+ DPRINTF(sc, UPGT_DEBUG_FW,
+ "memory address space end=0x%08x\n",
+ sc->sc_memaddr_frame_end);
+ break;
+ case UPGT_BRA_TYPE_END:
+ DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_END len=%d\n",
+ bra_option_len);
+ bra_end = 1;
+ break;
+ default:
+ DPRINTF(sc, UPGT_DEBUG_FW, "unknown BRA option len=%d\n",
+ bra_option_len);
+ error = EIO;
+ goto fail;
+ }
+
+ /* jump to next BRA option */
+ offset += sizeof(struct upgt_fw_bra_option) + bra_option_len;
+ }
+
+ DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware verified", __func__);
+fail:
+ firmware_put(fw, FIRMWARE_UNLOAD);
+ return (error);
+}
+
+static void
+upgt_bulk_tx(struct upgt_softc *sc, struct upgt_data *data)
+{
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next);
+ UPGT_STAT_INC(sc, st_tx_pending);
+ usbd_transfer_start(sc->sc_xfer[UPGT_BULK_TX]);
+}
+
+static int
+upgt_device_reset(struct upgt_softc *sc)
+{
+ struct upgt_data *data;
+ char init_cmd[] = { 0x7e, 0x7e, 0x7e, 0x7e };
+
+ UPGT_LOCK(sc);
+
+ data = upgt_getbuf(sc);
+ if (data == NULL) {
+ UPGT_UNLOCK(sc);
+ return (ENOBUFS);
+ }
+ memcpy(data->buf, init_cmd, sizeof(init_cmd));
+ data->buflen = sizeof(init_cmd);
+ upgt_bulk_tx(sc, data);
+ usb_pause_mtx(&sc->sc_mtx, 100);
+
+ UPGT_UNLOCK(sc);
+ DPRINTF(sc, UPGT_DEBUG_FW, "%s: device initialized\n", __func__);
+ return (0);
+}
+
+static int
+upgt_alloc_tx(struct upgt_softc *sc)
+{
+ int i;
+
+ STAILQ_INIT(&sc->sc_tx_active);
+ STAILQ_INIT(&sc->sc_tx_inactive);
+ STAILQ_INIT(&sc->sc_tx_pending);
+
+ for (i = 0; i < UPGT_TX_MAXCOUNT; i++) {
+ struct upgt_data *data = &sc->sc_tx_data[i];
+ data->buf = ((uint8_t *)sc->sc_tx_dma_buf) + (i * MCLBYTES);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
+ UPGT_STAT_INC(sc, st_tx_inactive);
+ }
+
+ return (0);
+}
+
+static int
+upgt_alloc_rx(struct upgt_softc *sc)
+{
+ int i;
+
+ STAILQ_INIT(&sc->sc_rx_active);
+ STAILQ_INIT(&sc->sc_rx_inactive);
+
+ for (i = 0; i < UPGT_RX_MAXCOUNT; i++) {
+ struct upgt_data *data = &sc->sc_rx_data[i];
+ data->buf = ((uint8_t *)sc->sc_rx_dma_buf) + (i * MCLBYTES);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ }
+ return (0);
+}
+
+static int
+upgt_detach(device_t dev)
+{
+ struct upgt_softc *sc = device_get_softc(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ unsigned int x;
+
+ /*
+ * Prevent further allocations from RX/TX/CMD
+ * data lists and ioctls
+ */
+ UPGT_LOCK(sc);
+ sc->sc_flags |= UPGT_FLAG_DETACHED;
+
+ STAILQ_INIT(&sc->sc_tx_active);
+ STAILQ_INIT(&sc->sc_tx_inactive);
+ STAILQ_INIT(&sc->sc_tx_pending);
+
+ STAILQ_INIT(&sc->sc_rx_active);
+ STAILQ_INIT(&sc->sc_rx_inactive);
+
+ upgt_stop(sc);
+ UPGT_UNLOCK(sc);
+
+ callout_drain(&sc->sc_led_ch);
+ callout_drain(&sc->sc_watchdog_ch);
+
+ /* drain USB transfers */
+ for (x = 0; x != UPGT_N_XFERS; x++)
+ usbd_transfer_drain(sc->sc_xfer[x]);
+
+ /* free data buffers */
+ UPGT_LOCK(sc);
+ upgt_free_rx(sc);
+ upgt_free_tx(sc);
+ UPGT_UNLOCK(sc);
+
+ /* free USB transfers and some data buffers */
+ usbd_transfer_unsetup(sc->sc_xfer, UPGT_N_XFERS);
+
+ ieee80211_ifdetach(ic);
+ mbufq_drain(&sc->sc_snd);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+upgt_free_rx(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < UPGT_RX_MAXCOUNT; i++) {
+ struct upgt_data *data = &sc->sc_rx_data[i];
+
+ data->buf = NULL;
+ data->ni = NULL;
+ }
+}
+
+static void
+upgt_free_tx(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < UPGT_TX_MAXCOUNT; i++) {
+ struct upgt_data *data = &sc->sc_tx_data[i];
+
+ if (data->ni != NULL)
+ ieee80211_free_node(data->ni);
+
+ data->buf = NULL;
+ data->ni = NULL;
+ }
+}
+
+static void
+upgt_abort_xfers_locked(struct upgt_softc *sc)
+{
+ int i;
+
+ UPGT_ASSERT_LOCKED(sc);
+ /* abort any pending transfers */
+ for (i = 0; i < UPGT_N_XFERS; i++)
+ usbd_transfer_stop(sc->sc_xfer[i]);
+}
+
+static void
+upgt_abort_xfers(struct upgt_softc *sc)
+{
+
+ UPGT_LOCK(sc);
+ upgt_abort_xfers_locked(sc);
+ UPGT_UNLOCK(sc);
+}
+
+#define UPGT_SYSCTL_STAT_ADD32(c, h, n, p, d) \
+ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d)
+
+static void
+upgt_sysctl_node(struct upgt_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *child;
+ struct sysctl_oid *tree;
+ struct upgt_stat *stats;
+
+ stats = &sc->sc_stat;
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
+
+ tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
+ NULL, "UPGT statistics");
+ child = SYSCTL_CHILDREN(tree);
+ UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_active",
+ &stats->st_tx_active, "Active numbers in TX queue");
+ UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_inactive",
+ &stats->st_tx_inactive, "Inactive numbers in TX queue");
+ UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_pending",
+ &stats->st_tx_pending, "Pending numbers in TX queue");
+}
+
+#undef UPGT_SYSCTL_STAT_ADD32
+
+static struct upgt_data *
+_upgt_getbuf(struct upgt_softc *sc)
+{
+ struct upgt_data *bf;
+
+ bf = STAILQ_FIRST(&sc->sc_tx_inactive);
+ if (bf != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
+ UPGT_STAT_DEC(sc, st_tx_inactive);
+ } else
+ bf = NULL;
+ if (bf == NULL)
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: %s\n", __func__,
+ "out of xmit buffers");
+ return (bf);
+}
+
+static struct upgt_data *
+upgt_getbuf(struct upgt_softc *sc)
+{
+ struct upgt_data *bf;
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ bf = _upgt_getbuf(sc);
+ if (bf == NULL)
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: stop queue\n", __func__);
+
+ return (bf);
+}
+
+static struct upgt_data *
+upgt_gettxbuf(struct upgt_softc *sc)
+{
+ struct upgt_data *bf;
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ bf = upgt_getbuf(sc);
+ if (bf == NULL)
+ return (NULL);
+
+ bf->addr = upgt_mem_alloc(sc);
+ if (bf->addr == 0) {
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: no free prism memory!\n",
+ __func__);
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
+ UPGT_STAT_INC(sc, st_tx_inactive);
+ return (NULL);
+ }
+ return (bf);
+}
+
+static int
+upgt_tx_start(struct upgt_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
+ struct upgt_data *data)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ int error = 0, len;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
+ struct upgt_lmac_mem *mem;
+ struct upgt_lmac_tx_desc *txdesc;
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ upgt_set_led(sc, UPGT_LED_BLINK);
+
+ /*
+ * Software crypto.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_encap(ni, m);
+ if (k == NULL) {
+ device_printf(sc->sc_dev,
+ "ieee80211_crypto_encap returns NULL.\n");
+ error = EIO;
+ goto done;
+ }
+
+ /* in case packet header moved, reset pointer */
+ wh = mtod(m, struct ieee80211_frame *);
+ }
+
+ /* Transmit the URB containing the TX data. */
+ memset(data->buf, 0, MCLBYTES);
+ mem = (struct upgt_lmac_mem *)data->buf;
+ mem->addr = htole32(data->addr);
+ txdesc = (struct upgt_lmac_tx_desc *)(mem + 1);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_MGT) {
+ /* mgmt frames */
+ txdesc->header1.flags = UPGT_H1_FLAGS_TX_MGMT;
+ /* always send mgmt frames at lowest rate (DS1) */
+ memset(txdesc->rates, 0x10, sizeof(txdesc->rates));
+ } else {
+ /* data frames */
+ txdesc->header1.flags = UPGT_H1_FLAGS_TX_DATA;
+ memcpy(txdesc->rates, sc->sc_cur_rateset, sizeof(txdesc->rates));
+ }
+ txdesc->header1.type = UPGT_H1_TYPE_TX_DATA;
+ txdesc->header1.len = htole16(m->m_pkthdr.len);
+ txdesc->header2.reqid = htole32(data->addr);
+ txdesc->header2.type = htole16(UPGT_H2_TYPE_TX_ACK_YES);
+ txdesc->header2.flags = htole16(UPGT_H2_FLAGS_TX_ACK_YES);
+ txdesc->type = htole32(UPGT_TX_DESC_TYPE_DATA);
+ txdesc->pad3[0] = UPGT_TX_DESC_PAD3_SIZE;
+
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct upgt_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = 0; /* XXX where to get from? */
+
+ ieee80211_radiotap_tx(vap, m);
+ }
+
+ /* copy frame below our TX descriptor header */
+ m_copydata(m, 0, m->m_pkthdr.len,
+ data->buf + (sizeof(*mem) + sizeof(*txdesc)));
+ /* calculate frame size */
+ len = sizeof(*mem) + sizeof(*txdesc) + m->m_pkthdr.len;
+ /* we need to align the frame to a 4 byte boundary */
+ len = (len + 3) & ~3;
+ /* calculate frame checksum */
+ mem->chksum = upgt_chksum_le((uint32_t *)txdesc, len - sizeof(*mem));
+ data->ni = ni;
+ data->m = m;
+ data->buflen = len;
+
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: TX start data sending (%d bytes)\n",
+ __func__, len);
+ KASSERT(len <= MCLBYTES, ("mbuf is small for saving data"));
+
+ upgt_bulk_tx(sc, data);
+done:
+ /*
+ * If we don't regulary read the device statistics, the RX queue
+ * will stall. It's strange, but it works, so we keep reading
+ * the statistics here. *shrug*
+ */
+ if (!(vap->iv_ifp->if_get_counter(vap->iv_ifp, IFCOUNTER_OPACKETS) %
+ UPGT_TX_STAT_INTERVAL))
+ upgt_get_stats(sc);
+
+ return (error);
+}
+
+static void
+upgt_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct upgt_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL;
+ struct upgt_data *data;
+ int8_t nf;
+ int rssi = -1;
+
+ UPGT_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&sc->sc_rx_active);
+ if (data == NULL)
+ goto setup;
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
+ m = upgt_rxeof(xfer, data, &rssi);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+setup:
+ data = STAILQ_FIRST(&sc->sc_rx_inactive);
+ if (data == NULL)
+ return;
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
+ usbd_xfer_set_frame_data(xfer, 0, data->buf, MCLBYTES);
+ usbd_transfer_submit(xfer);
+
+ /*
+ * To avoid LOR we should unlock our private mutex here to call
+ * ieee80211_input() because here is at the end of a USB
+ * callback and safe to unlock.
+ */
+ UPGT_UNLOCK(sc);
+ if (m != NULL) {
+ wh = mtod(m, struct ieee80211_frame *);
+ ni = ieee80211_find_rxnode(ic,
+ (struct ieee80211_frame_min *)wh);
+ nf = -95; /* XXX */
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, nf);
+ /* node is no longer needed */
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, nf);
+ m = NULL;
+ }
+ UPGT_LOCK(sc);
+ upgt_start(sc);
+ break;
+ default:
+ /* needs it to the inactive queue due to a error. */
+ data = STAILQ_FIRST(&sc->sc_rx_active);
+ if (data != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ }
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto setup;
+ }
+ break;
+ }
+}
+
+static void
+upgt_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct upgt_softc *sc = usbd_xfer_softc(xfer);
+ struct upgt_data *data;
+
+ UPGT_ASSERT_LOCKED(sc);
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&sc->sc_tx_active);
+ if (data == NULL)
+ goto setup;
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next);
+ UPGT_STAT_DEC(sc, st_tx_active);
+ upgt_txeof(xfer, data);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
+ UPGT_STAT_INC(sc, st_tx_inactive);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+setup:
+ data = STAILQ_FIRST(&sc->sc_tx_pending);
+ if (data == NULL) {
+ DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: empty pending queue\n",
+ __func__);
+ return;
+ }
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next);
+ UPGT_STAT_DEC(sc, st_tx_pending);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next);
+ UPGT_STAT_INC(sc, st_tx_active);
+
+ usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
+ usbd_transfer_submit(xfer);
+ upgt_start(sc);
+ break;
+ default:
+ data = STAILQ_FIRST(&sc->sc_tx_active);
+ if (data == NULL)
+ goto setup;
+ if (data->ni != NULL) {
+ if_inc_counter(data->ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto setup;
+ }
+ break;
+ }
+}
+
+static device_method_t upgt_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, upgt_match),
+ DEVMETHOD(device_attach, upgt_attach),
+ DEVMETHOD(device_detach, upgt_detach),
+ DEVMETHOD_END
+};
+
+static driver_t upgt_driver = {
+ .name = "upgt",
+ .methods = upgt_methods,
+ .size = sizeof(struct upgt_softc)
+};
+
+static devclass_t upgt_devclass;
+
+DRIVER_MODULE(if_upgt, uhub, upgt_driver, upgt_devclass, NULL, 0);
+MODULE_VERSION(if_upgt, 1);
+MODULE_DEPEND(if_upgt, usb, 1, 1, 1);
+MODULE_DEPEND(if_upgt, wlan, 1, 1, 1);
+MODULE_DEPEND(if_upgt, upgtfw_fw, 1, 1, 1);
+USB_PNP_HOST_INFO(upgt_devs);
diff --git a/freebsd/sys/dev/usb/wlan/if_upgtvar.h b/freebsd/sys/dev/usb/wlan/if_upgtvar.h
new file mode 100644
index 00000000..ce996f6a
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_upgtvar.h
@@ -0,0 +1,480 @@
+/* $OpenBSD: if_upgtvar.h,v 1.14 2008/02/02 13:48:44 mglocker Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct upgt_softc;
+
+/*
+ * General values.
+ */
+enum {
+ UPGT_BULK_RX,
+ UPGT_BULK_TX,
+ UPGT_N_XFERS = 2,
+};
+
+#define UPGT_CONFIG_INDEX 0
+#define UPGT_IFACE_INDEX 0
+#define UPGT_USB_TIMEOUT 1000
+#define UPGT_FIRMWARE_TIMEOUT 10
+
+#define UPGT_MEMADDR_FIRMWARE_START 0x00020000 /* 512 bytes large */
+#define UPGT_MEMSIZE_FRAME_HEAD 0x0070
+#define UPGT_MEMSIZE_RX 0x3500
+
+#define UPGT_RX_MAXCOUNT 6
+#define UPGT_TX_MAXCOUNT 128
+#define UPGT_TX_STAT_INTERVAL 5
+#define UPGT_RX_MINSZ (sizeof(struct upgt_lmac_header) + 4)
+
+/* device flags */
+#define UPGT_DEVICE_ATTACHED (1 << 0)
+
+/* leds */
+#define UPGT_LED_OFF 0
+#define UPGT_LED_ON 1
+#define UPGT_LED_BLINK 2
+
+/*
+ * Firmware.
+ */
+#define UPGT_FW_BLOCK_SIZE 256
+
+#define UPGT_BRA_FWTYPE_SIZE 4
+#define UPGT_BRA_FWTYPE_LM86 "LM86"
+#define UPGT_BRA_FWTYPE_LM87 "LM87"
+enum upgt_fw_type {
+ UPGT_FWTYPE_LM86,
+ UPGT_FWTYPE_LM87
+};
+
+#define UPGT_BRA_TYPE_FW 0x80000001
+#define UPGT_BRA_TYPE_VERSION 0x80000002
+#define UPGT_BRA_TYPE_DEPIF 0x80000003
+#define UPGT_BRA_TYPE_EXPIF 0x80000004
+#define UPGT_BRA_TYPE_DESCR 0x80000101
+#define UPGT_BRA_TYPE_END 0xff0000ff
+struct upgt_fw_bra_option {
+ uint32_t type;
+ uint32_t len;
+ uint8_t data[];
+} __packed;
+
+struct upgt_fw_bra_descr {
+ uint32_t unknown1;
+ uint32_t memaddr_space_start;
+ uint32_t memaddr_space_end;
+ uint32_t unknown2;
+ uint32_t unknown3;
+ uint8_t rates[20];
+} __packed;
+
+#define UPGT_X2_SIGNATURE_SIZE 4
+#define UPGT_X2_SIGNATURE "x2 "
+struct upgt_fw_x2_header {
+ uint8_t signature[4];
+ uint32_t startaddr;
+ uint32_t len;
+ uint32_t crc;
+} __packed;
+
+/*
+ * EEPROM.
+ */
+#define UPGT_EEPROM_SIZE 8192
+#define UPGT_EEPROM_BLOCK_SIZE 1020
+
+struct upgt_eeprom_header {
+ /* 14 bytes */
+ uint32_t magic;
+ uint16_t pad1;
+ uint16_t preamble_len;
+ uint32_t pad2;
+ /* data */
+} __packed;
+
+#define UPGT_EEPROM_TYPE_END 0x0000
+#define UPGT_EEPROM_TYPE_NAME 0x0001
+#define UPGT_EEPROM_TYPE_SERIAL 0x0003
+#define UPGT_EEPROM_TYPE_MAC 0x0101
+#define UPGT_EEPROM_TYPE_HWRX 0x1001
+#define UPGT_EEPROM_TYPE_CHIP 0x1002
+#define UPGT_EEPROM_TYPE_FREQ3 0x1903
+#define UPGT_EEPROM_TYPE_FREQ4 0x1904
+#define UPGT_EEPROM_TYPE_FREQ5 0x1905
+#define UPGT_EEPROM_TYPE_FREQ6 0x1906
+#define UPGT_EEPROM_TYPE_OFF 0xffff
+struct upgt_eeprom_option {
+ uint16_t len;
+ uint16_t type;
+ uint8_t data[];
+ /* data */
+} __packed;
+
+#define UPGT_EEPROM_RX_CONST 0x88
+struct upgt_eeprom_option_hwrx {
+ uint32_t pad1;
+ uint8_t rxfilter;
+ uint8_t pad2[15];
+} __packed;
+
+struct upgt_eeprom_freq3_header {
+ uint8_t flags;
+ uint8_t elements;
+} __packed;
+
+struct upgt_eeprom_freq4_header {
+ uint8_t flags;
+ uint8_t elements;
+ uint8_t settings;
+ uint8_t type;
+} __packed;
+
+struct upgt_eeprom_freq4_1 {
+ uint16_t freq;
+ uint8_t data[50];
+} __packed;
+
+struct upgt_eeprom_freq4_2 {
+ uint16_t head;
+ uint8_t subtails[4];
+ uint8_t tail;
+} __packed;
+
+/*
+ * LMAC protocol.
+ */
+struct upgt_lmac_mem {
+ uint32_t addr;
+ uint32_t chksum;
+} __packed;
+
+#define UPGT_H1_FLAGS_TX_MGMT 0x00 /* for TX: mgmt frame */
+#define UPGT_H1_FLAGS_TX_NO_CALLBACK 0x01 /* for TX: no USB callback */
+#define UPGT_H1_FLAGS_TX_DATA 0x10 /* for TX: data frame */
+#define UPGT_H1_TYPE_RX_DATA 0x00 /* 802.11 RX data frame */
+#define UPGT_H1_TYPE_RX_DATA_MGMT 0x04 /* 802.11 RX mgmt frame */
+#define UPGT_H1_TYPE_TX_DATA 0x40 /* 802.11 TX data frame */
+#define UPGT_H1_TYPE_CTRL 0x80 /* control frame */
+struct upgt_lmac_h1 {
+ /* 4 bytes */
+ uint8_t flags;
+ uint8_t type;
+ uint16_t len;
+} __packed;
+
+#define UPGT_H2_TYPE_TX_ACK_NO 0x0000
+#define UPGT_H2_TYPE_TX_ACK_YES 0x0001
+#define UPGT_H2_TYPE_MACFILTER 0x0000
+#define UPGT_H2_TYPE_CHANNEL 0x0001
+#define UPGT_H2_TYPE_TX_DONE 0x0008
+#define UPGT_H2_TYPE_STATS 0x000a
+#define UPGT_H2_TYPE_EEPROM 0x000c
+#define UPGT_H2_TYPE_LED 0x000d
+#define UPGT_H2_FLAGS_TX_ACK_NO 0x0101
+#define UPGT_H2_FLAGS_TX_ACK_YES 0x0707
+struct upgt_lmac_h2 {
+ /* 8 bytes */
+ uint32_t reqid;
+ uint16_t type;
+ uint16_t flags;
+} __packed;
+
+struct upgt_lmac_header {
+ /* 12 bytes */
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+} __packed;
+
+struct upgt_lmac_eeprom {
+ /* 16 bytes */
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint16_t offset;
+ uint16_t len;
+ /* data */
+} __packed;
+
+#define UPGT_FILTER_TYPE_NONE 0x0000
+#define UPGT_FILTER_TYPE_STA 0x0001
+#define UPGT_FILTER_TYPE_IBSS 0x0002
+#define UPGT_FILTER_TYPE_HOSTAP 0x0004
+#define UPGT_FILTER_TYPE_MONITOR 0x0010
+#define UPGT_FILTER_TYPE_RESET 0x0020
+#define UPGT_FILTER_UNKNOWN1 0x0002
+#define UPGT_FILTER_UNKNOWN2 0x0ca8
+#define UPGT_FILTER_UNKNOWN3 0xffff
+#define UPGT_FILTER_MONITOR_UNKNOWN1 0x0000
+#define UPGT_FILTER_MONITOR_UNKNOWN2 0x0000
+#define UPGT_FILTER_MONITOR_UNKNOWN3 0x0000
+struct upgt_lmac_filter {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ /* 32 bytes */
+ uint16_t type;
+ uint8_t dst[IEEE80211_ADDR_LEN];
+ uint8_t src[IEEE80211_ADDR_LEN];
+ uint16_t unknown1;
+ uint32_t rxaddr;
+ uint16_t unknown2;
+ uint32_t rxhw;
+ uint16_t unknown3;
+ uint32_t unknown4;
+} __packed;
+
+/* frequence 3 data */
+struct upgt_lmac_freq3 {
+ uint16_t freq;
+ uint8_t data[6];
+} __packed;
+
+/* frequence 4 data */
+struct upgt_lmac_freq4 {
+ struct upgt_eeprom_freq4_2 cmd;
+ uint8_t pad;
+};
+
+/* frequence 6 data */
+struct upgt_lmac_freq6 {
+ uint16_t freq;
+ uint8_t data[8];
+} __packed;
+
+#define UPGT_CHANNEL_UNKNOWN1 0x0001
+#define UPGT_CHANNEL_UNKNOWN2 0x0000
+#define UPGT_CHANNEL_UNKNOWN3 0x48
+struct upgt_lmac_channel {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ /* 112 bytes */
+ uint16_t unknown1;
+ uint16_t unknown2;
+ uint8_t pad1[20];
+ struct upgt_lmac_freq6 freq6;
+ uint8_t settings;
+ uint8_t unknown3;
+ uint8_t freq3_1[4];
+ struct upgt_lmac_freq4 freq4[8];
+ uint8_t freq3_2[4];
+ uint32_t pad2;
+} __packed;
+
+#define UPGT_LED_MODE_SET 0x0003
+#define UPGT_LED_ACTION_OFF 0x0002
+#define UPGT_LED_ACTION_ON 0x0003
+#define UPGT_LED_ACTION_TMP_DUR 100 /* ms */
+struct upgt_lmac_led {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint16_t mode;
+ uint16_t action_fix;
+ uint16_t action_tmp;
+ uint16_t action_tmp_dur;
+} __packed;
+
+struct upgt_lmac_stats {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint8_t data[76];
+} __packed;
+
+struct upgt_lmac_rx_desc {
+ struct upgt_lmac_h1 header1;
+ /* 16 bytes */
+ uint16_t freq;
+ uint8_t unknown1;
+ uint8_t rate;
+ uint8_t rssi;
+ uint8_t pad;
+ uint16_t unknown2;
+ uint32_t timestamp;
+ uint32_t unknown3;
+ uint8_t data[];
+} __packed;
+
+#define UPGT_TX_DESC_KEY_EXISTS 0x01
+struct upgt_lmac_tx_desc_wep {
+ uint8_t key_exists;
+ uint8_t key_len;
+ uint8_t key_val[16];
+} __packed;
+
+#define UPGT_TX_DESC_TYPE_BEACON 0x00000000
+#define UPGT_TX_DESC_TYPE_PROBE 0x00000001
+#define UPGT_TX_DESC_TYPE_MGMT 0x00000002
+#define UPGT_TX_DESC_TYPE_DATA 0x00000004
+#define UPGT_TX_DESC_PAD3_SIZE 2
+struct upgt_lmac_tx_desc {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint8_t rates[8];
+ uint16_t pad1;
+ struct upgt_lmac_tx_desc_wep wep_key;
+ uint32_t type;
+ uint32_t pad2;
+ uint32_t unknown1;
+ uint32_t unknown2;
+ uint8_t pad3[2];
+ /* 802.11 frame data */
+} __packed;
+
+#define UPGT_TX_DONE_DESC_STATUS_OK 0x0001
+struct upgt_lmac_tx_done_desc {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint16_t status;
+ uint16_t rssi;
+ uint16_t seq;
+ uint16_t unknown;
+} __packed;
+
+/*
+ * USB xfers.
+ */
+struct upgt_data {
+ uint8_t *buf;
+ uint32_t buflen;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ uint32_t addr;
+ STAILQ_ENTRY(upgt_data) next;
+};
+typedef STAILQ_HEAD(, upgt_data) upgt_datahead;
+
+/*
+ * Prism memory.
+ */
+struct upgt_memory_page {
+ uint8_t used;
+ uint32_t addr;
+} __packed;
+
+#define UPGT_MEMORY_MAX_PAGES 8
+struct upgt_memory {
+ uint8_t pages;
+ struct upgt_memory_page page[UPGT_MEMORY_MAX_PAGES];
+} __packed;
+
+/*
+ * BPF
+ */
+struct upgt_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_antsignal;
+} __packed __aligned(8);
+
+#define UPGT_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))
+
+struct upgt_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed __aligned(8);
+
+#define UPGT_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct upgt_stat {
+ uint32_t st_tx_active;
+ uint32_t st_tx_inactive;
+ uint32_t st_tx_pending;
+};
+
+#define UPGT_STAT_INC(sc, var) (sc)->sc_stat.var++
+#define UPGT_STAT_DEC(sc, var) (sc)->sc_stat.var--
+
+struct upgt_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define UPGT_VAP(vap) ((struct upgt_vap *)(vap))
+
+struct upgt_softc {
+ struct ieee80211com sc_ic;
+ struct mbufq sc_snd;
+ device_t sc_dev;
+ struct usb_device *sc_udev;
+ void *sc_rx_dma_buf;
+ void *sc_tx_dma_buf;
+ struct mtx sc_mtx;
+ struct upgt_stat sc_stat;
+ int sc_flags;
+#define UPGT_FLAG_FWLOADED (1 << 0)
+#define UPGT_FLAG_INITDONE (1 << 1)
+#define UPGT_FLAG_DETACHED (1 << 2)
+ int sc_debug;
+
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ int sc_led_blink;
+ struct callout sc_led_ch;
+ uint8_t sc_cur_rateset[8];
+
+ /* watchdog */
+ int sc_tx_timer;
+ struct callout sc_watchdog_ch;
+
+ /* Firmware. */
+ int sc_fw_type;
+ /* memory addresses on device */
+ uint32_t sc_memaddr_frame_start;
+ uint32_t sc_memaddr_frame_end;
+ uint32_t sc_memaddr_rx_start;
+ struct upgt_memory sc_memory;
+
+ /* data which we found in the EEPROM */
+ uint8_t sc_eeprom[2 * UPGT_EEPROM_SIZE] __aligned(4);
+ uint16_t sc_eeprom_hwrx;
+ struct upgt_lmac_freq3 sc_eeprom_freq3[IEEE80211_CHAN_MAX];
+ struct upgt_lmac_freq4 sc_eeprom_freq4[IEEE80211_CHAN_MAX][8];
+ struct upgt_lmac_freq6 sc_eeprom_freq6[IEEE80211_CHAN_MAX];
+ uint8_t sc_eeprom_freq6_settings;
+
+ /* RX/TX */
+ struct usb_xfer *sc_xfer[UPGT_N_XFERS];
+ int sc_rx_no;
+ int sc_tx_no;
+ struct upgt_data sc_rx_data[UPGT_RX_MAXCOUNT];
+ upgt_datahead sc_rx_active;
+ upgt_datahead sc_rx_inactive;
+ struct upgt_data sc_tx_data[UPGT_TX_MAXCOUNT];
+ upgt_datahead sc_tx_active;
+ upgt_datahead sc_tx_inactive;
+ upgt_datahead sc_tx_pending;
+
+ /* BPF */
+ struct upgt_rx_radiotap_header sc_rxtap;
+ struct upgt_tx_radiotap_header sc_txtap;
+};
+
+#define UPGT_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define UPGT_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define UPGT_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
diff --git a/freebsd/sys/dev/usb/wlan/if_ural.c b/freebsd/sys/dev/usb/wlan/if_ural.c
new file mode 100644
index 00000000..d6a199ff
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_ural.c
@@ -0,0 +1,2246 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Copyright (c) 2006, 2008
+ * Hans Petter Selasky <hselasky@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Ralink Technology RT2500USB chipset driver
+ * http://www.ralinktech.com/
+ */
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_ratectl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define USB_DEBUG_VAR ural_debug
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/wlan/if_uralreg.h>
+#include <dev/usb/wlan/if_uralvar.h>
+
+#ifdef USB_DEBUG
+static int ural_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural");
+SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RWTUN, &ural_debug, 0,
+ "Debug level");
+#endif
+
+#define URAL_RSSI(rssi) \
+ ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \
+ ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0)
+
+/* various supported device vendors/products */
+static const STRUCT_USB_HOST_ID ural_devs[] = {
+#define URAL_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+ URAL_DEV(ASUS, WL167G),
+ URAL_DEV(ASUS, RT2570),
+ URAL_DEV(BELKIN, F5D7050),
+ URAL_DEV(BELKIN, F5D7051),
+ URAL_DEV(CISCOLINKSYS, HU200TS),
+ URAL_DEV(CISCOLINKSYS, WUSB54G),
+ URAL_DEV(CISCOLINKSYS, WUSB54GP),
+ URAL_DEV(CONCEPTRONIC2, C54RU),
+ URAL_DEV(DLINK, DWLG122),
+ URAL_DEV(GIGABYTE, GN54G),
+ URAL_DEV(GIGABYTE, GNWBKG),
+ URAL_DEV(GUILLEMOT, HWGUSB254),
+ URAL_DEV(MELCO, KG54),
+ URAL_DEV(MELCO, KG54AI),
+ URAL_DEV(MELCO, KG54YB),
+ URAL_DEV(MELCO, NINWIFI),
+ URAL_DEV(MSI, RT2570),
+ URAL_DEV(MSI, RT2570_2),
+ URAL_DEV(MSI, RT2570_3),
+ URAL_DEV(NOVATECH, NV902),
+ URAL_DEV(RALINK, RT2570),
+ URAL_DEV(RALINK, RT2570_2),
+ URAL_DEV(RALINK, RT2570_3),
+ URAL_DEV(SIEMENS2, WL54G),
+ URAL_DEV(SMC, 2862WG),
+ URAL_DEV(SPHAIRON, UB801R),
+ URAL_DEV(SURECOM, RT2570),
+ URAL_DEV(VTECH, RT2570),
+ URAL_DEV(ZINWELL, RT2570),
+#undef URAL_DEV
+};
+
+static usb_callback_t ural_bulk_read_callback;
+static usb_callback_t ural_bulk_write_callback;
+
+static usb_error_t ural_do_request(struct ural_softc *sc,
+ struct usb_device_request *req, void *data);
+static struct ieee80211vap *ural_vap_create(struct ieee80211com *,
+ const char [IFNAMSIZ], int, enum ieee80211_opmode,
+ int, const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void ural_vap_delete(struct ieee80211vap *);
+static void ural_tx_free(struct ural_tx_data *, int);
+static void ural_setup_tx_list(struct ural_softc *);
+static void ural_unsetup_tx_list(struct ural_softc *);
+static int ural_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static void ural_setup_tx_desc(struct ural_softc *,
+ struct ural_tx_desc *, uint32_t, int, int);
+static int ural_tx_bcn(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int ural_tx_mgt(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int ural_tx_data(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int ural_transmit(struct ieee80211com *, struct mbuf *);
+static void ural_start(struct ural_softc *);
+static void ural_parent(struct ieee80211com *);
+static void ural_set_testmode(struct ural_softc *);
+static void ural_eeprom_read(struct ural_softc *, uint16_t, void *,
+ int);
+static uint16_t ural_read(struct ural_softc *, uint16_t);
+static void ural_read_multi(struct ural_softc *, uint16_t, void *,
+ int);
+static void ural_write(struct ural_softc *, uint16_t, uint16_t);
+static void ural_write_multi(struct ural_softc *, uint16_t, void *,
+ int) __unused;
+static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t);
+static uint8_t ural_bbp_read(struct ural_softc *, uint8_t);
+static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t);
+static void ural_scan_start(struct ieee80211com *);
+static void ural_scan_end(struct ieee80211com *);
+static void ural_getradiocaps(struct ieee80211com *, int, int *,
+ struct ieee80211_channel[]);
+static void ural_set_channel(struct ieee80211com *);
+static void ural_set_chan(struct ural_softc *,
+ struct ieee80211_channel *);
+static void ural_disable_rf_tune(struct ural_softc *);
+static void ural_enable_tsf_sync(struct ural_softc *);
+static void ural_enable_tsf(struct ural_softc *);
+static void ural_update_slot(struct ural_softc *);
+static void ural_set_txpreamble(struct ural_softc *);
+static void ural_set_basicrates(struct ural_softc *,
+ const struct ieee80211_channel *);
+static void ural_set_bssid(struct ural_softc *, const uint8_t *);
+static void ural_set_macaddr(struct ural_softc *, const uint8_t *);
+static void ural_update_promisc(struct ieee80211com *);
+static void ural_setpromisc(struct ural_softc *);
+static const char *ural_get_rf(int);
+static void ural_read_eeprom(struct ural_softc *);
+static int ural_bbp_init(struct ural_softc *);
+static void ural_set_txantenna(struct ural_softc *, int);
+static void ural_set_rxantenna(struct ural_softc *, int);
+static void ural_init(struct ural_softc *);
+static void ural_stop(struct ural_softc *);
+static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void ural_ratectl_start(struct ural_softc *,
+ struct ieee80211_node *);
+static void ural_ratectl_timeout(void *);
+static void ural_ratectl_task(void *, int);
+static int ural_pause(struct ural_softc *sc, int timeout);
+
+/*
+ * Default values for MAC registers; values taken from the reference driver.
+ */
+static const struct {
+ uint16_t reg;
+ uint16_t val;
+} ural_def_mac[] = {
+ { RAL_TXRX_CSR5, 0x8c8d },
+ { RAL_TXRX_CSR6, 0x8b8a },
+ { RAL_TXRX_CSR7, 0x8687 },
+ { RAL_TXRX_CSR8, 0x0085 },
+ { RAL_MAC_CSR13, 0x1111 },
+ { RAL_MAC_CSR14, 0x1e11 },
+ { RAL_TXRX_CSR21, 0xe78f },
+ { RAL_MAC_CSR9, 0xff1d },
+ { RAL_MAC_CSR11, 0x0002 },
+ { RAL_MAC_CSR22, 0x0053 },
+ { RAL_MAC_CSR15, 0x0000 },
+ { RAL_MAC_CSR8, RAL_FRAME_SIZE },
+ { RAL_TXRX_CSR19, 0x0000 },
+ { RAL_TXRX_CSR18, 0x005a },
+ { RAL_PHY_CSR2, 0x0000 },
+ { RAL_TXRX_CSR0, 0x1ec0 },
+ { RAL_PHY_CSR4, 0x000f }
+};
+
+/*
+ * Default values for BBP registers; values taken from the reference driver.
+ */
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} ural_def_bbp[] = {
+ { 3, 0x02 },
+ { 4, 0x19 },
+ { 14, 0x1c },
+ { 15, 0x30 },
+ { 16, 0xac },
+ { 17, 0x48 },
+ { 18, 0x18 },
+ { 19, 0xff },
+ { 20, 0x1e },
+ { 21, 0x08 },
+ { 22, 0x08 },
+ { 23, 0x08 },
+ { 24, 0x80 },
+ { 25, 0x50 },
+ { 26, 0x08 },
+ { 27, 0x23 },
+ { 30, 0x10 },
+ { 31, 0x2b },
+ { 32, 0xb9 },
+ { 34, 0x12 },
+ { 35, 0x50 },
+ { 39, 0xc4 },
+ { 40, 0x02 },
+ { 41, 0x60 },
+ { 53, 0x10 },
+ { 54, 0x18 },
+ { 56, 0x08 },
+ { 57, 0x10 },
+ { 58, 0x08 },
+ { 61, 0x60 },
+ { 62, 0x10 },
+ { 75, 0xff }
+};
+
+/*
+ * Default values for RF register R2 indexed by channel numbers.
+ */
+static const uint32_t ural_rf2522_r2[] = {
+ 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814,
+ 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e
+};
+
+static const uint32_t ural_rf2523_r2[] = {
+ 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,
+ 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346
+};
+
+static const uint32_t ural_rf2524_r2[] = {
+ 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,
+ 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346
+};
+
+static const uint32_t ural_rf2525_r2[] = {
+ 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d,
+ 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346
+};
+
+static const uint32_t ural_rf2525_hi_r2[] = {
+ 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345,
+ 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e
+};
+
+static const uint32_t ural_rf2525e_r2[] = {
+ 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463,
+ 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b
+};
+
+static const uint32_t ural_rf2526_hi_r2[] = {
+ 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d,
+ 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241
+};
+
+static const uint32_t ural_rf2526_r2[] = {
+ 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229,
+ 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d
+};
+
+/*
+ * For dual-band RF, RF registers R1 and R4 also depend on channel number;
+ * values taken from the reference driver.
+ */
+static const struct {
+ uint8_t chan;
+ uint32_t r1;
+ uint32_t r2;
+ uint32_t r4;
+} ural_rf5222[] = {
+ { 1, 0x08808, 0x0044d, 0x00282 },
+ { 2, 0x08808, 0x0044e, 0x00282 },
+ { 3, 0x08808, 0x0044f, 0x00282 },
+ { 4, 0x08808, 0x00460, 0x00282 },
+ { 5, 0x08808, 0x00461, 0x00282 },
+ { 6, 0x08808, 0x00462, 0x00282 },
+ { 7, 0x08808, 0x00463, 0x00282 },
+ { 8, 0x08808, 0x00464, 0x00282 },
+ { 9, 0x08808, 0x00465, 0x00282 },
+ { 10, 0x08808, 0x00466, 0x00282 },
+ { 11, 0x08808, 0x00467, 0x00282 },
+ { 12, 0x08808, 0x00468, 0x00282 },
+ { 13, 0x08808, 0x00469, 0x00282 },
+ { 14, 0x08808, 0x0046b, 0x00286 },
+
+ { 36, 0x08804, 0x06225, 0x00287 },
+ { 40, 0x08804, 0x06226, 0x00287 },
+ { 44, 0x08804, 0x06227, 0x00287 },
+ { 48, 0x08804, 0x06228, 0x00287 },
+ { 52, 0x08804, 0x06229, 0x00287 },
+ { 56, 0x08804, 0x0622a, 0x00287 },
+ { 60, 0x08804, 0x0622b, 0x00287 },
+ { 64, 0x08804, 0x0622c, 0x00287 },
+
+ { 100, 0x08804, 0x02200, 0x00283 },
+ { 104, 0x08804, 0x02201, 0x00283 },
+ { 108, 0x08804, 0x02202, 0x00283 },
+ { 112, 0x08804, 0x02203, 0x00283 },
+ { 116, 0x08804, 0x02204, 0x00283 },
+ { 120, 0x08804, 0x02205, 0x00283 },
+ { 124, 0x08804, 0x02206, 0x00283 },
+ { 128, 0x08804, 0x02207, 0x00283 },
+ { 132, 0x08804, 0x02208, 0x00283 },
+ { 136, 0x08804, 0x02209, 0x00283 },
+ { 140, 0x08804, 0x0220a, 0x00283 },
+
+ { 149, 0x08808, 0x02429, 0x00281 },
+ { 153, 0x08808, 0x0242b, 0x00281 },
+ { 157, 0x08808, 0x0242d, 0x00281 },
+ { 161, 0x08808, 0x0242f, 0x00281 }
+};
+
+static const uint8_t ural_chan_2ghz[] =
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+
+static const uint8_t ural_chan_5ghz[] =
+ { 36, 40, 44, 48, 52, 56, 60, 64,
+ 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140,
+ 149, 153, 157, 161 };
+
+static const struct usb_config ural_config[URAL_N_TRANSFER] = {
+ [URAL_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4),
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = ural_bulk_write_callback,
+ .timeout = 5000, /* ms */
+ },
+ [URAL_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = ural_bulk_read_callback,
+ },
+};
+
+static device_probe_t ural_match;
+static device_attach_t ural_attach;
+static device_detach_t ural_detach;
+
+static device_method_t ural_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ural_match),
+ DEVMETHOD(device_attach, ural_attach),
+ DEVMETHOD(device_detach, ural_detach),
+ DEVMETHOD_END
+};
+
+static driver_t ural_driver = {
+ .name = "ural",
+ .methods = ural_methods,
+ .size = sizeof(struct ural_softc),
+};
+
+static devclass_t ural_devclass;
+
+DRIVER_MODULE(ural, uhub, ural_driver, ural_devclass, NULL, 0);
+MODULE_DEPEND(ural, usb, 1, 1, 1);
+MODULE_DEPEND(ural, wlan, 1, 1, 1);
+MODULE_VERSION(ural, 1);
+USB_PNP_HOST_INFO(ural_devs);
+
+static int
+ural_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != 0)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != RAL_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa));
+}
+
+static int
+ural_attach(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct ural_softc *sc = device_get_softc(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb_desc(self);
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(self),
+ MTX_NETWORK_LOCK, MTX_DEF);
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ iface_index = RAL_IFACE_INDEX;
+ error = usbd_transfer_setup(uaa->device,
+ &iface_index, sc->sc_xfer, ural_config,
+ URAL_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(self, "could not allocate USB transfers, "
+ "err=%s\n", usbd_errstr(error));
+ goto detach;
+ }
+
+ RAL_LOCK(sc);
+ /* retrieve RT2570 rev. no */
+ sc->asic_rev = ural_read(sc, RAL_MAC_CSR0);
+
+ /* retrieve MAC address and various other things from EEPROM */
+ ural_read_eeprom(sc);
+ RAL_UNLOCK(sc);
+
+ device_printf(self, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
+ sc->asic_rev, ural_get_rf(sc->rf_rev));
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(self);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode supported */
+ | IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_HOSTAP /* HostAp mode supported */
+ | IEEE80211_C_TXPMGT /* tx power management */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* bg scanning supported */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ ural_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
+ ic->ic_channels);
+
+ ieee80211_ifattach(ic);
+ ic->ic_update_promisc = ural_update_promisc;
+ ic->ic_raw_xmit = ural_raw_xmit;
+ ic->ic_scan_start = ural_scan_start;
+ ic->ic_scan_end = ural_scan_end;
+ ic->ic_getradiocaps = ural_getradiocaps;
+ ic->ic_set_channel = ural_set_channel;
+ ic->ic_parent = ural_parent;
+ ic->ic_transmit = ural_transmit;
+ ic->ic_vap_create = ural_vap_create;
+ ic->ic_vap_delete = ural_vap_delete;
+
+ ieee80211_radiotap_attach(ic,
+ &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
+ RAL_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ RAL_RX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return (0);
+
+detach:
+ ural_detach(self);
+ return (ENXIO); /* failure */
+}
+
+static int
+ural_detach(device_t self)
+{
+ struct ural_softc *sc = device_get_softc(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ /* prevent further ioctls */
+ RAL_LOCK(sc);
+ sc->sc_detached = 1;
+ RAL_UNLOCK(sc);
+
+ /* stop all USB transfers */
+ usbd_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER);
+
+ /* free TX list, if any */
+ RAL_LOCK(sc);
+ ural_unsetup_tx_list(sc);
+ RAL_UNLOCK(sc);
+
+ if (ic->ic_softc == sc)
+ ieee80211_ifdetach(ic);
+ mbufq_drain(&sc->sc_snd);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static usb_error_t
+ural_do_request(struct ural_softc *sc,
+ struct usb_device_request *req, void *data)
+{
+ usb_error_t err;
+ int ntries = 10;
+
+ while (ntries--) {
+ err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+ req, data, 0, NULL, 250 /* ms */);
+ if (err == 0)
+ break;
+
+ DPRINTFN(1, "Control request failed, %s (retrying)\n",
+ usbd_errstr(err));
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ return (err);
+}
+
+static struct ieee80211vap *
+ural_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ural_softc *sc = ic->ic_softc;
+ struct ural_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ uvp = malloc(sizeof(struct ural_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) {
+ /* out of memory */
+ free(uvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ural_newstate;
+
+ usb_callout_init_mtx(&uvp->ratectl_ch, &sc->sc_mtx, 0);
+ TASK_INIT(&uvp->ratectl_task, 0, ural_ratectl_task, uvp);
+ ieee80211_ratectl_init(vap);
+ ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status, mac);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+ural_vap_delete(struct ieee80211vap *vap)
+{
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+
+ usb_callout_drain(&uvp->ratectl_ch);
+ ieee80211_draintask(ic, &uvp->ratectl_task);
+ ieee80211_ratectl_deinit(vap);
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static void
+ural_tx_free(struct ural_tx_data *data, int txerr)
+{
+ struct ural_softc *sc = data->sc;
+
+ if (data->m != NULL) {
+ ieee80211_tx_complete(data->ni, data->m, txerr);
+ data->m = NULL;
+ data->ni = NULL;
+ }
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+}
+
+static void
+ural_setup_tx_list(struct ural_softc *sc)
+{
+ struct ural_tx_data *data;
+ int i;
+
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ data->sc = sc;
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+ }
+}
+
+static void
+ural_unsetup_tx_list(struct ural_softc *sc)
+{
+ struct ural_tx_data *data;
+ int i;
+
+ /* make sure any subsequent use of the queues will fail */
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ /* free up all node references and mbufs */
+ for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static int
+ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ural_softc *sc = ic->ic_softc;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ DPRINTF("%s -> %s\n",
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ IEEE80211_UNLOCK(ic);
+ RAL_LOCK(sc);
+ usb_callout_stop(&uvp->ratectl_ch);
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ /* abort TSF synchronization */
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+
+ /* force tx led to stop blinking */
+ ural_write(sc, RAL_MAC_CSR20, 0);
+ }
+ break;
+
+ case IEEE80211_S_RUN:
+ ni = ieee80211_ref_node(vap->iv_bss);
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ if (ic->ic_bsschan == IEEE80211_CHAN_ANYC)
+ goto fail;
+
+ ural_update_slot(sc);
+ ural_set_txpreamble(sc);
+ ural_set_basicrates(sc, ic->ic_bsschan);
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ ural_set_bssid(sc, sc->sc_bssid);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ m = ieee80211_beacon_alloc(ni);
+ if (m == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate beacon\n");
+ goto fail;
+ }
+ ieee80211_ref_node(ni);
+ if (ural_tx_bcn(sc, m, ni) != 0) {
+ device_printf(sc->sc_dev,
+ "could not send beacon\n");
+ goto fail;
+ }
+ }
+
+ /* make tx led blink on tx (controlled by ASIC) */
+ ural_write(sc, RAL_MAC_CSR20, 1);
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
+ ural_enable_tsf_sync(sc);
+ else
+ ural_enable_tsf(sc);
+
+ /* enable automatic rate adaptation */
+ /* XXX should use ic_bsschan but not valid until after newstate call below */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ ural_ratectl_start(sc, ni);
+ ieee80211_free_node(ni);
+ break;
+
+ default:
+ break;
+ }
+ RAL_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (uvp->newstate(vap, nstate, arg));
+
+fail:
+ RAL_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ ieee80211_free_node(ni);
+ return (-1);
+}
+
+
+static void
+ural_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ural_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211vap *vap;
+ struct ural_tx_data *data;
+ struct mbuf *m;
+ struct usb_page_cache *pc;
+ int len;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete, %d bytes\n", len);
+
+ /* free resources */
+ data = usbd_xfer_get_priv(xfer);
+ ural_tx_free(data, 0);
+ usbd_xfer_set_priv(xfer, NULL);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&sc->tx_q);
+ if (data) {
+ STAILQ_REMOVE_HEAD(&sc->tx_q, next);
+ m = data->m;
+
+ if (m->m_pkthdr.len > (int)(RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) {
+ DPRINTFN(0, "data overflow, %u bytes\n",
+ m->m_pkthdr.len);
+ m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE);
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &data->desc, RAL_TX_DESC_SIZE);
+ usbd_m_copy_in(pc, RAL_TX_DESC_SIZE, m, 0,
+ m->m_pkthdr.len);
+
+ vap = data->ni->ni_vap;
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = data->rate;
+ tap->wt_antenna = sc->tx_ant;
+
+ ieee80211_radiotap_tx(vap, m);
+ }
+
+ /* xfer length needs to be a multiple of two! */
+ len = (RAL_TX_DESC_SIZE + m->m_pkthdr.len + 1) & ~1;
+ if ((len % 64) == 0)
+ len += 2;
+
+ DPRINTFN(11, "sending frame len=%u xferlen=%u\n",
+ m->m_pkthdr.len, len);
+
+ usbd_xfer_set_frame_len(xfer, 0, len);
+ usbd_xfer_set_priv(xfer, data);
+
+ usbd_transfer_submit(xfer);
+ }
+ ural_start(sc);
+ break;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ data = usbd_xfer_get_priv(xfer);
+ if (data != NULL) {
+ ural_tx_free(data, error);
+ usbd_xfer_set_priv(xfer, NULL);
+ }
+
+ if (error == USB_ERR_STALLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ if (error == USB_ERR_TIMEOUT)
+ device_printf(sc->sc_dev, "device timeout\n");
+ break;
+ }
+}
+
+static void
+ural_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct ural_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL;
+ struct usb_page_cache *pc;
+ uint32_t flags;
+ int8_t rssi = 0, nf = 0;
+ int len;
+
+ usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTFN(15, "rx done, actlen=%d\n", len);
+
+ if (len < (int)(RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN)) {
+ DPRINTF("%s: xfer too short %d\n",
+ device_get_nameunit(sc->sc_dev), len);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+
+ len -= RAL_RX_DESC_SIZE;
+ /* rx descriptor is located at the end */
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, len, &sc->sc_rx_desc, RAL_RX_DESC_SIZE);
+
+ rssi = URAL_RSSI(sc->sc_rx_desc.rssi);
+ nf = RAL_NOISE_FLOOR;
+ flags = le32toh(sc->sc_rx_desc.flags);
+ if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) {
+ /*
+ * This should not happen since we did not
+ * request to receive those frames when we
+ * filled RAL_TXRX_CSR2:
+ */
+ DPRINTFN(5, "PHY or CRC error\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ DPRINTF("could not allocate mbuf\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ usbd_copy_out(pc, 0, mtod(m, uint8_t *), len);
+
+ /* finalize mbuf */
+ m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff;
+
+ if (ieee80211_radiotap_active(ic)) {
+ struct ural_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ /* XXX set once */
+ tap->wr_flags = 0;
+ tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate,
+ (flags & RAL_RX_OFDM) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ tap->wr_antenna = sc->rx_ant;
+ tap->wr_antsignal = nf + rssi;
+ tap->wr_antnoise = nf;
+ }
+ /* Strip trailing 802.11 MAC FCS. */
+ m_adj(m, -IEEE80211_CRC_LEN);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+
+ /*
+ * At the end of a USB callback it is always safe to unlock
+ * the private mutex of a device! That is why we do the
+ * "ieee80211_input" here, and not some lines up!
+ */
+ RAL_UNLOCK(sc);
+ if (m) {
+ ni = ieee80211_find_rxnode(ic,
+ mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, nf);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, nf);
+ }
+ RAL_LOCK(sc);
+ ural_start(sc);
+ return;
+
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static uint8_t
+ural_plcp_signal(int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+
+static void
+ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
+ uint32_t flags, int len, int rate)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint16_t plcp_length;
+ int remainder;
+
+ desc->flags = htole32(flags);
+ desc->flags |= htole32(RAL_TX_NEWSEQ);
+ desc->flags |= htole32(len << 16);
+
+ desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5));
+ desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame)));
+
+ /* setup PLCP fields */
+ desc->plcp_signal = ural_plcp_signal(rate);
+ desc->plcp_service = 4;
+
+ len += IEEE80211_CRC_LEN;
+ if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) {
+ desc->flags |= htole32(RAL_TX_OFDM);
+
+ plcp_length = len & 0xfff;
+ desc->plcp_length_hi = plcp_length >> 6;
+ desc->plcp_length_lo = plcp_length & 0x3f;
+ } else {
+ if (rate == 0)
+ rate = 2; /* avoid division by zero */
+ plcp_length = howmany(16 * len, rate);
+ if (rate == 22) {
+ remainder = (16 * len) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= RAL_PLCP_LENGEXT;
+ }
+ desc->plcp_length_hi = plcp_length >> 8;
+ desc->plcp_length_lo = plcp_length & 0xff;
+
+ if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->plcp_signal |= 0x08;
+ }
+
+ desc->iv = 0;
+ desc->eiv = 0;
+}
+
+#define RAL_TX_TIMEOUT 5000
+
+static int
+ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_txparam *tp;
+ struct ural_tx_data *data;
+
+ if (sc->tx_nfree == 0) {
+ m_freem(m0);
+ ieee80211_free_node(ni);
+ return (EIO);
+ }
+ if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) {
+ m_freem(m0);
+ ieee80211_free_node(ni);
+ return (ENXIO);
+ }
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = tp->mgmtrate;
+
+ ural_setup_tx_desc(sc, &data->desc,
+ RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len,
+ tp->mgmtrate);
+
+ DPRINTFN(10, "sending beacon frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, tp->mgmtrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return (0);
+}
+
+static int
+ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_txparam *tp;
+ struct ural_tx_data *data;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
+ uint32_t flags;
+ uint16_t dur;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = tp->mgmtrate;
+
+ flags = 0;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RAL_TX_ACK;
+
+ dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ USETW(wh->i_dur, dur);
+
+ /* tell hardware to add timestamp for probe responses */
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_MGT &&
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ flags |= RAL_TX_TIMESTAMP;
+ }
+
+ ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, tp->mgmtrate);
+
+ DPRINTFN(10, "sending mgt frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, tp->mgmtrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return 0;
+}
+
+static int
+ural_sendprot(struct ural_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct ural_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(ic->ic_rt, rate);
+ ackrate = ieee80211_ack_rate(ic->ic_rt, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort)
+ + ieee80211_ack_duration(ic->ic_rt, rate, isshort);
+ flags = RAL_TX_RETRY(7);
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort);
+ flags |= RAL_TX_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ data->rate = protrate;
+ ural_setup_tx_desc(sc, &data->desc, flags, mprot->m_pkthdr.len, protrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return 0;
+}
+
+static int
+ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ural_tx_data *data;
+ uint32_t flags;
+ int error;
+ int rate;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ rate = params->ibp_rate0;
+ if (!ieee80211_isratevalid(ic->ic_rt, rate)) {
+ m_freem(m0);
+ return EINVAL;
+ }
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RAL_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = ural_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error || sc->tx_nfree == 0) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = rate;
+
+ /* XXX need to setup descriptor ourself */
+ ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate);
+
+ DPRINTFN(10, "sending raw frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return 0;
+}
+
+static int
+ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ural_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ uint32_t flags = 0;
+ uint16_t dur;
+ int error, rate;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else {
+ (void) ieee80211_ratectl_rate(ni, NULL, 0);
+ rate = ni->ni_txrate;
+ }
+
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = ural_sendprot(sc, m0, ni, prot, rate);
+ if (error || sc->tx_nfree == 0) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
+ }
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = rate;
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RAL_TX_ACK;
+ flags |= RAL_TX_RETRY(7);
+
+ dur = ieee80211_ack_duration(ic->ic_rt, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ USETW(wh->i_dur, dur);
+ }
+
+ ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate);
+
+ DPRINTFN(10, "sending data frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return 0;
+}
+
+static int
+ural_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct ural_softc *sc = ic->ic_softc;
+ int error;
+
+ RAL_LOCK(sc);
+ if (!sc->sc_running) {
+ RAL_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ RAL_UNLOCK(sc);
+ return (error);
+ }
+ ural_start(sc);
+ RAL_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+ural_start(struct ural_softc *sc)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (sc->sc_running == 0)
+ return;
+
+ while (sc->tx_nfree >= RAL_TX_MINFREE &&
+ (m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ if (ural_tx_data(sc, m, ni) != 0) {
+ if_inc_counter(ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ ieee80211_free_node(ni);
+ break;
+ }
+ }
+}
+
+static void
+ural_parent(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_softc;
+ int startall = 0;
+
+ RAL_LOCK(sc);
+ if (sc->sc_detached) {
+ RAL_UNLOCK(sc);
+ return;
+ }
+ if (ic->ic_nrunning > 0) {
+ if (sc->sc_running == 0) {
+ ural_init(sc);
+ startall = 1;
+ } else
+ ural_setpromisc(sc);
+ } else if (sc->sc_running)
+ ural_stop(sc);
+ RAL_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+}
+
+static void
+ural_set_testmode(struct ural_softc *sc)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_VENDOR_REQUEST;
+ USETW(req.wValue, 4);
+ USETW(req.wIndex, 1);
+ USETW(req.wLength, 0);
+
+ error = ural_do_request(sc, &req, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not set test mode: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_EEPROM;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ error = ural_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static uint16_t
+ural_read(struct ural_softc *sc, uint16_t reg)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+ uint16_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, sizeof (uint16_t));
+
+ error = ural_do_request(sc, &req, &val);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usbd_errstr(error));
+ return 0;
+ }
+
+ return le16toh(val);
+}
+
+static void
+ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = ural_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_WRITE_MAC;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ error = ural_do_request(sc, &req, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_WRITE_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = ural_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usbd_errstr(error));
+ }
+}
+
+static void
+ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint16_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY))
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not write to BBP\n");
+ return;
+ }
+
+ tmp = reg << 8 | val;
+ ural_write(sc, RAL_PHY_CSR7, tmp);
+}
+
+static uint8_t
+ural_bbp_read(struct ural_softc *sc, uint8_t reg)
+{
+ uint16_t val;
+ int ntries;
+
+ val = RAL_BBP_WRITE | reg << 8;
+ ural_write(sc, RAL_PHY_CSR7, val);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY))
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+ }
+
+ return ural_read(sc, RAL_PHY_CSR7) & 0xff;
+}
+
+static void
+ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val)
+{
+ uint32_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY))
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not write to RF\n");
+ return;
+ }
+
+ tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3);
+ ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff);
+ ural_write(sc, RAL_PHY_CSR10, tmp >> 16);
+
+ /* remember last written value in sc */
+ sc->rf_regs[reg] = val;
+
+ DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff);
+}
+
+static void
+ural_scan_start(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_softc;
+
+ RAL_LOCK(sc);
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+ ural_set_bssid(sc, ieee80211broadcastaddr);
+ RAL_UNLOCK(sc);
+}
+
+static void
+ural_scan_end(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_softc;
+
+ RAL_LOCK(sc);
+ ural_enable_tsf_sync(sc);
+ ural_set_bssid(sc, sc->sc_bssid);
+ RAL_UNLOCK(sc);
+
+}
+
+static void
+ural_getradiocaps(struct ieee80211com *ic,
+ int maxchans, int *nchans, struct ieee80211_channel chans[])
+{
+ struct ural_softc *sc = ic->ic_softc;
+ uint8_t bands[IEEE80211_MODE_BYTES];
+
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ ieee80211_add_channel_list_2ghz(chans, maxchans, nchans,
+ ural_chan_2ghz, nitems(ural_chan_2ghz), bands, 0);
+
+ if (sc->rf_rev == RAL_RF_5222) {
+ setbit(bands, IEEE80211_MODE_11A);
+ ieee80211_add_channel_list_5ghz(chans, maxchans, nchans,
+ ural_chan_5ghz, nitems(ural_chan_5ghz), bands, 0);
+ }
+}
+
+static void
+ural_set_channel(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_softc;
+
+ RAL_LOCK(sc);
+ ural_set_chan(sc, ic->ic_curchan);
+ RAL_UNLOCK(sc);
+}
+
+static void
+ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t power, tmp;
+ int i, chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+ return;
+
+ if (IEEE80211_IS_CHAN_2GHZ(c))
+ power = min(sc->txpow[chan - 1], 31);
+ else
+ power = 31;
+
+ /* adjust txpower using ifconfig settings */
+ power -= (100 - ic->ic_txpowlimit) / 8;
+
+ DPRINTFN(2, "setting channel to %u, txpower to %u\n", chan, power);
+
+ switch (sc->rf_rev) {
+ case RAL_RF_2522:
+ ural_rf_write(sc, RAL_RF1, 0x00814);
+ ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ break;
+
+ case RAL_RF_2523:
+ ural_rf_write(sc, RAL_RF1, 0x08804);
+ ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2524:
+ ural_rf_write(sc, RAL_RF1, 0x0c808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2525:
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2525E:
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282);
+ break;
+
+ case RAL_RF_2526:
+ ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
+ ural_rf_write(sc, RAL_RF1, 0x08804);
+
+ ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
+ break;
+
+ /* dual-band RF */
+ case RAL_RF_5222:
+ for (i = 0; ural_rf5222[i].chan != chan; i++);
+
+ ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1);
+ ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4);
+ break;
+ }
+
+ if (ic->ic_opmode != IEEE80211_M_MONITOR &&
+ (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ /* set Japan filter bit for channel 14 */
+ tmp = ural_bbp_read(sc, 70);
+
+ tmp &= ~RAL_JAPAN_FILTER;
+ if (chan == 14)
+ tmp |= RAL_JAPAN_FILTER;
+
+ ural_bbp_write(sc, 70, tmp);
+
+ /* clear CRC errors */
+ ural_read(sc, RAL_STA_CSR0);
+
+ ural_pause(sc, hz / 100);
+ ural_disable_rf_tune(sc);
+ }
+
+ /* XXX doesn't belong here */
+ /* update basic rate set */
+ ural_set_basicrates(sc, c);
+
+ /* give the hardware some time to do the switchover */
+ ural_pause(sc, hz / 100);
+}
+
+/*
+ * Disable RF auto-tuning.
+ */
+static void
+ural_disable_rf_tune(struct ural_softc *sc)
+{
+ uint32_t tmp;
+
+ if (sc->rf_rev != RAL_RF_2523) {
+ tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE;
+ ural_rf_write(sc, RAL_RF1, tmp);
+ }
+
+ tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE;
+ ural_rf_write(sc, RAL_RF3, tmp);
+
+ DPRINTFN(2, "disabling RF autotune\n");
+}
+
+/*
+ * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF
+ * synchronization.
+ */
+static void
+ural_enable_tsf_sync(struct ural_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint16_t logcwmin, preload, tmp;
+
+ /* first, disable TSF synchronization */
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+
+ tmp = (16 * vap->iv_bss->ni_intval) << 4;
+ ural_write(sc, RAL_TXRX_CSR18, tmp);
+
+ logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0;
+ preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6;
+ tmp = logcwmin << 12 | preload;
+ ural_write(sc, RAL_TXRX_CSR20, tmp);
+
+ /* finally, enable TSF synchronization */
+ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN;
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ tmp |= RAL_ENABLE_TSF_SYNC(1);
+ else
+ tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR;
+ ural_write(sc, RAL_TXRX_CSR19, tmp);
+
+ DPRINTF("enabling TSF synchronization\n");
+}
+
+static void
+ural_enable_tsf(struct ural_softc *sc)
+{
+ /* first, disable TSF synchronization */
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+ ural_write(sc, RAL_TXRX_CSR19, RAL_ENABLE_TSF | RAL_ENABLE_TSF_SYNC(2));
+}
+
+#define RAL_RXTX_TURNAROUND 5 /* us */
+static void
+ural_update_slot(struct ural_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint16_t slottime, sifs, eifs;
+
+ slottime = IEEE80211_GET_SLOTTIME(ic);
+
+ /*
+ * These settings may sound a bit inconsistent but this is what the
+ * reference driver does.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11B) {
+ sifs = 16 - RAL_RXTX_TURNAROUND;
+ eifs = 364;
+ } else {
+ sifs = 10 - RAL_RXTX_TURNAROUND;
+ eifs = 64;
+ }
+
+ ural_write(sc, RAL_MAC_CSR10, slottime);
+ ural_write(sc, RAL_MAC_CSR11, sifs);
+ ural_write(sc, RAL_MAC_CSR12, eifs);
+}
+
+static void
+ural_set_txpreamble(struct ural_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint16_t tmp;
+
+ tmp = ural_read(sc, RAL_TXRX_CSR10);
+
+ tmp &= ~RAL_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ tmp |= RAL_SHORT_PREAMBLE;
+
+ ural_write(sc, RAL_TXRX_CSR10, tmp);
+}
+
+static void
+ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c)
+{
+ /* XXX wrong, take from rate set */
+ /* update basic rate set */
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ /* 11a basic rates: 6, 12, 24Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x150);
+ } else if (IEEE80211_IS_CHAN_ANYG(c)) {
+ /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x15f);
+ } else {
+ /* 11b basic rates: 1, 2Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x3);
+ }
+}
+
+static void
+ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid)
+{
+ uint16_t tmp;
+
+ tmp = bssid[0] | bssid[1] << 8;
+ ural_write(sc, RAL_MAC_CSR5, tmp);
+
+ tmp = bssid[2] | bssid[3] << 8;
+ ural_write(sc, RAL_MAC_CSR6, tmp);
+
+ tmp = bssid[4] | bssid[5] << 8;
+ ural_write(sc, RAL_MAC_CSR7, tmp);
+
+ DPRINTF("setting BSSID to %6D\n", bssid, ":");
+}
+
+static void
+ural_set_macaddr(struct ural_softc *sc, const uint8_t *addr)
+{
+ uint16_t tmp;
+
+ tmp = addr[0] | addr[1] << 8;
+ ural_write(sc, RAL_MAC_CSR2, tmp);
+
+ tmp = addr[2] | addr[3] << 8;
+ ural_write(sc, RAL_MAC_CSR3, tmp);
+
+ tmp = addr[4] | addr[5] << 8;
+ ural_write(sc, RAL_MAC_CSR4, tmp);
+
+ DPRINTF("setting MAC address to %6D\n", addr, ":");
+}
+
+static void
+ural_setpromisc(struct ural_softc *sc)
+{
+ uint32_t tmp;
+
+ tmp = ural_read(sc, RAL_TXRX_CSR2);
+
+ tmp &= ~RAL_DROP_NOT_TO_ME;
+ if (sc->sc_ic.ic_promisc == 0)
+ tmp |= RAL_DROP_NOT_TO_ME;
+
+ ural_write(sc, RAL_TXRX_CSR2, tmp);
+
+ DPRINTF("%s promiscuous mode\n", sc->sc_ic.ic_promisc ?
+ "entering" : "leaving");
+}
+
+static void
+ural_update_promisc(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_softc;
+
+ RAL_LOCK(sc);
+ if (sc->sc_running)
+ ural_setpromisc(sc);
+ RAL_UNLOCK(sc);
+}
+
+static const char *
+ural_get_rf(int rev)
+{
+ switch (rev) {
+ case RAL_RF_2522: return "RT2522";
+ case RAL_RF_2523: return "RT2523";
+ case RAL_RF_2524: return "RT2524";
+ case RAL_RF_2525: return "RT2525";
+ case RAL_RF_2525E: return "RT2525e";
+ case RAL_RF_2526: return "RT2526";
+ case RAL_RF_5222: return "RT5222";
+ default: return "unknown";
+ }
+}
+
+static void
+ural_read_eeprom(struct ural_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint16_t val;
+
+ ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2);
+ val = le16toh(val);
+ sc->rf_rev = (val >> 11) & 0x7;
+ sc->hw_radio = (val >> 10) & 0x1;
+ sc->led_mode = (val >> 6) & 0x7;
+ sc->rx_ant = (val >> 4) & 0x3;
+ sc->tx_ant = (val >> 2) & 0x3;
+ sc->nb_ant = val & 0x3;
+
+ /* read MAC address */
+ ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, ic->ic_macaddr, 6);
+
+ /* read default values for BBP registers */
+ ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16);
+
+ /* read Tx power for all b/g channels */
+ ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14);
+}
+
+static int
+ural_bbp_init(struct ural_softc *sc)
+{
+ int i, ntries;
+
+ /* wait for BBP to be ready */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0)
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for BBP\n");
+ return EIO;
+ }
+
+ /* initialize BBP registers to default values */
+ for (i = 0; i < nitems(ural_def_bbp); i++)
+ ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val);
+
+#if 0
+ /* initialize BBP registers to values stored in EEPROM */
+ for (i = 0; i < 16; i++) {
+ if (sc->bbp_prom[i].reg == 0xff)
+ continue;
+ ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
+ }
+#endif
+
+ return 0;
+}
+
+static void
+ural_set_txantenna(struct ural_softc *sc, int antenna)
+{
+ uint16_t tmp;
+ uint8_t tx;
+
+ tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK;
+ if (antenna == 1)
+ tx |= RAL_BBP_ANTA;
+ else if (antenna == 2)
+ tx |= RAL_BBP_ANTB;
+ else
+ tx |= RAL_BBP_DIVERSITY;
+
+ /* need to force I/Q flip for RF 2525e, 2526 and 5222 */
+ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 ||
+ sc->rf_rev == RAL_RF_5222)
+ tx |= RAL_BBP_FLIPIQ;
+
+ ural_bbp_write(sc, RAL_BBP_TX, tx);
+
+ /* update values in PHY_CSR5 and PHY_CSR6 */
+ tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7;
+ ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7));
+
+ tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7;
+ ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7));
+}
+
+static void
+ural_set_rxantenna(struct ural_softc *sc, int antenna)
+{
+ uint8_t rx;
+
+ rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK;
+ if (antenna == 1)
+ rx |= RAL_BBP_ANTA;
+ else if (antenna == 2)
+ rx |= RAL_BBP_ANTB;
+ else
+ rx |= RAL_BBP_DIVERSITY;
+
+ /* need to force no I/Q flip for RF 2525e and 2526 */
+ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526)
+ rx &= ~RAL_BBP_FLIPIQ;
+
+ ural_bbp_write(sc, RAL_BBP_RX, rx);
+}
+
+static void
+ural_init(struct ural_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint16_t tmp;
+ int i, ntries;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ ural_set_testmode(sc);
+ ural_write(sc, 0x308, 0x00f0); /* XXX magic */
+
+ ural_stop(sc);
+
+ /* initialize MAC registers to default values */
+ for (i = 0; i < nitems(ural_def_mac); i++)
+ ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val);
+
+ /* wait for BBP and RF to wake up (this can take a long time!) */
+ for (ntries = 0; ntries < 100; ntries++) {
+ tmp = ural_read(sc, RAL_MAC_CSR17);
+ if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) ==
+ (RAL_BBP_AWAKE | RAL_RF_AWAKE))
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
+ goto fail;
+ }
+
+ /* we're ready! */
+ ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY);
+
+ /* set basic rate set (will be updated later) */
+ ural_write(sc, RAL_TXRX_CSR11, 0x15f);
+
+ if (ural_bbp_init(sc) != 0)
+ goto fail;
+
+ ural_set_chan(sc, ic->ic_curchan);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR10) */
+ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ ural_set_txantenna(sc, sc->tx_ant);
+ ural_set_rxantenna(sc, sc->rx_ant);
+
+ ural_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr);
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ ural_setup_tx_list(sc);
+
+ /* kick Rx */
+ tmp = RAL_DROP_PHY | RAL_DROP_CRC;
+ if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION;
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ tmp |= RAL_DROP_TODS;
+ if (ic->ic_promisc == 0)
+ tmp |= RAL_DROP_NOT_TO_ME;
+ }
+ ural_write(sc, RAL_TXRX_CSR2, tmp);
+
+ sc->sc_running = 1;
+ usbd_xfer_set_stall(sc->sc_xfer[URAL_BULK_WR]);
+ usbd_transfer_start(sc->sc_xfer[URAL_BULK_RD]);
+ return;
+
+fail: ural_stop(sc);
+}
+
+static void
+ural_stop(struct ural_softc *sc)
+{
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_running = 0;
+
+ /*
+ * Drain all the transfers, if not already drained:
+ */
+ RAL_UNLOCK(sc);
+ usbd_transfer_drain(sc->sc_xfer[URAL_BULK_WR]);
+ usbd_transfer_drain(sc->sc_xfer[URAL_BULK_RD]);
+ RAL_LOCK(sc);
+
+ ural_unsetup_tx_list(sc);
+
+ /* disable Rx */
+ ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX);
+ /* reset ASIC and BBP (but won't reset MAC registers!) */
+ ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP);
+ /* wait a little */
+ ural_pause(sc, hz / 10);
+ ural_write(sc, RAL_MAC_CSR1, 0);
+ /* wait a little */
+ ural_pause(sc, hz / 10);
+}
+
+static int
+ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ural_softc *sc = ic->ic_softc;
+
+ RAL_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if (!sc->sc_running) {
+ RAL_UNLOCK(sc);
+ m_freem(m);
+ return ENETDOWN;
+ }
+ if (sc->tx_nfree < RAL_TX_MINFREE) {
+ RAL_UNLOCK(sc);
+ m_freem(m);
+ return EIO;
+ }
+
+ if (params == NULL) {
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ */
+ if (ural_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ } else {
+ /*
+ * Caller supplied explicit parameters to use in
+ * sending the frame.
+ */
+ if (ural_tx_raw(sc, m, ni, params) != 0)
+ goto bad;
+ }
+ RAL_UNLOCK(sc);
+ return 0;
+bad:
+ RAL_UNLOCK(sc);
+ return EIO; /* XXX */
+}
+
+static void
+ural_ratectl_start(struct ural_softc *sc, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ural_vap *uvp = URAL_VAP(vap);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR10) */
+ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp);
+}
+
+static void
+ural_ratectl_timeout(void *arg)
+{
+ struct ural_vap *uvp = arg;
+ struct ieee80211vap *vap = &uvp->vap;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ ieee80211_runtask(ic, &uvp->ratectl_task);
+}
+
+static void
+ural_ratectl_task(void *arg, int pending)
+{
+ struct ural_vap *uvp = arg;
+ struct ieee80211vap *vap = &uvp->vap;
+ struct ural_softc *sc = vap->iv_ic->ic_softc;
+ struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs;
+ int fail;
+
+ RAL_LOCK(sc);
+ /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */
+ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta));
+
+ txs->flags = IEEE80211_RATECTL_TX_STATS_RETRIES;
+ txs->nsuccess = sc->sta[7] + /* TX ok w/o retry */
+ sc->sta[8]; /* TX ok w/ retry */
+ fail = sc->sta[9]; /* TX retry-fail count */
+ txs->nframes = txs->nsuccess + fail;
+ /* XXX fail * maxretry */
+ txs->nretries = sc->sta[8] + fail;
+
+ ieee80211_ratectl_tx_update(vap, txs);
+
+ /* count TX retry-fail as Tx errors */
+ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, fail);
+
+ usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp);
+ RAL_UNLOCK(sc);
+}
+
+static int
+ural_pause(struct ural_softc *sc, int timeout)
+{
+
+ usb_pause_mtx(&sc->sc_mtx, timeout);
+ return (0);
+}
diff --git a/freebsd/sys/dev/usb/wlan/if_uralreg.h b/freebsd/sys/dev/usb/wlan/if_uralreg.h
new file mode 100644
index 00000000..3554fe6f
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_uralreg.h
@@ -0,0 +1,211 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RAL_NOISE_FLOOR -95
+#define RAL_RSSI_CORR 120
+
+#define RAL_RX_DESC_SIZE (sizeof (struct ural_rx_desc))
+#define RAL_TX_DESC_SIZE (sizeof (struct ural_tx_desc))
+#define RAL_FRAME_SIZE 0x780 /* NOTE: using 0x980 does not work */
+
+#define RAL_CONFIG_NO 1
+#define RAL_IFACE_INDEX 0
+
+#define RAL_VENDOR_REQUEST 0x01
+#define RAL_WRITE_MAC 0x02
+#define RAL_READ_MAC 0x03
+#define RAL_WRITE_MULTI_MAC 0x06
+#define RAL_READ_MULTI_MAC 0x07
+#define RAL_READ_EEPROM 0x09
+
+/*
+ * MAC registers.
+ */
+#define RAL_MAC_CSR0 0x0400 /* ASIC Version */
+#define RAL_MAC_CSR1 0x0402 /* System control */
+#define RAL_MAC_CSR2 0x0404 /* MAC addr0 */
+#define RAL_MAC_CSR3 0x0406 /* MAC addr1 */
+#define RAL_MAC_CSR4 0x0408 /* MAC addr2 */
+#define RAL_MAC_CSR5 0x040a /* BSSID0 */
+#define RAL_MAC_CSR6 0x040c /* BSSID1 */
+#define RAL_MAC_CSR7 0x040e /* BSSID2 */
+#define RAL_MAC_CSR8 0x0410 /* Max frame length */
+#define RAL_MAC_CSR9 0x0412 /* Timer control */
+#define RAL_MAC_CSR10 0x0414 /* Slot time */
+#define RAL_MAC_CSR11 0x0416 /* IFS */
+#define RAL_MAC_CSR12 0x0418 /* EIFS */
+#define RAL_MAC_CSR13 0x041a /* Power mode0 */
+#define RAL_MAC_CSR14 0x041c /* Power mode1 */
+#define RAL_MAC_CSR15 0x041e /* Power saving transition0 */
+#define RAL_MAC_CSR16 0x0420 /* Power saving transition1 */
+#define RAL_MAC_CSR17 0x0422 /* Power state control */
+#define RAL_MAC_CSR18 0x0424 /* Auto wake-up control */
+#define RAL_MAC_CSR19 0x0426 /* GPIO control */
+#define RAL_MAC_CSR20 0x0428 /* LED control0 */
+#define RAL_MAC_CSR22 0x042c /* XXX not documented */
+
+/*
+ * Tx/Rx Registers.
+ */
+#define RAL_TXRX_CSR0 0x0440 /* Security control */
+#define RAL_TXRX_CSR2 0x0444 /* Rx control */
+#define RAL_TXRX_CSR5 0x044a /* CCK Tx BBP ID0 */
+#define RAL_TXRX_CSR6 0x044c /* CCK Tx BBP ID1 */
+#define RAL_TXRX_CSR7 0x044e /* OFDM Tx BBP ID0 */
+#define RAL_TXRX_CSR8 0x0450 /* OFDM Tx BBP ID1 */
+#define RAL_TXRX_CSR10 0x0454 /* Auto responder control */
+#define RAL_TXRX_CSR11 0x0456 /* Auto responder basic rate */
+#define RAL_TXRX_CSR18 0x0464 /* Beacon interval */
+#define RAL_TXRX_CSR19 0x0466 /* Beacon/sync control */
+#define RAL_TXRX_CSR20 0x0468 /* Beacon alignment */
+#define RAL_TXRX_CSR21 0x046a /* XXX not documented */
+
+/*
+ * Security registers.
+ */
+#define RAL_SEC_CSR0 0x0480 /* Shared key 0, word 0 */
+
+/*
+ * PHY registers.
+ */
+#define RAL_PHY_CSR2 0x04c4 /* Tx MAC configuration */
+#define RAL_PHY_CSR4 0x04c8 /* Interface configuration */
+#define RAL_PHY_CSR5 0x04ca /* BBP Pre-Tx CCK */
+#define RAL_PHY_CSR6 0x04cc /* BBP Pre-Tx OFDM */
+#define RAL_PHY_CSR7 0x04ce /* BBP serial control */
+#define RAL_PHY_CSR8 0x04d0 /* BBP serial status */
+#define RAL_PHY_CSR9 0x04d2 /* RF serial control0 */
+#define RAL_PHY_CSR10 0x04d4 /* RF serial control1 */
+
+/*
+ * Statistics registers.
+ */
+#define RAL_STA_CSR0 0x04e0 /* FCS error */
+
+
+#define RAL_DISABLE_RX (1 << 0)
+#define RAL_DROP_CRC (1 << 1)
+#define RAL_DROP_PHY (1 << 2)
+#define RAL_DROP_CTL (1 << 3)
+#define RAL_DROP_NOT_TO_ME (1 << 4)
+#define RAL_DROP_TODS (1 << 5)
+#define RAL_DROP_BAD_VERSION (1 << 6)
+#define RAL_DROP_MULTICAST (1 << 9)
+#define RAL_DROP_BROADCAST (1 << 10)
+
+#define RAL_SHORT_PREAMBLE (1 << 2)
+
+#define RAL_RESET_ASIC (1 << 0)
+#define RAL_RESET_BBP (1 << 1)
+#define RAL_HOST_READY (1 << 2)
+
+#define RAL_ENABLE_TSF (1 << 0)
+#define RAL_ENABLE_TSF_SYNC(x) (((x) & 0x3) << 1)
+#define RAL_ENABLE_TBCN (1 << 3)
+#define RAL_ENABLE_BEACON_GENERATOR (1 << 4)
+
+#define RAL_RF_AWAKE (3 << 7)
+#define RAL_BBP_AWAKE (3 << 5)
+
+#define RAL_BBP_WRITE (1 << 15)
+#define RAL_BBP_BUSY (1 << 0)
+
+#define RAL_RF1_AUTOTUNE 0x08000
+#define RAL_RF3_AUTOTUNE 0x00040
+
+#define RAL_RF_2522 0x00
+#define RAL_RF_2523 0x01
+#define RAL_RF_2524 0x02
+#define RAL_RF_2525 0x03
+#define RAL_RF_2525E 0x04
+#define RAL_RF_2526 0x05
+/* dual-band RF */
+#define RAL_RF_5222 0x10
+
+#define RAL_BBP_VERSION 0
+#define RAL_BBP_TX 2
+#define RAL_BBP_RX 14
+
+#define RAL_BBP_ANTA 0x00
+#define RAL_BBP_DIVERSITY 0x01
+#define RAL_BBP_ANTB 0x02
+#define RAL_BBP_ANTMASK 0x03
+#define RAL_BBP_FLIPIQ 0x04
+
+#define RAL_JAPAN_FILTER 0x08
+
+struct ural_tx_desc {
+ uint32_t flags;
+#define RAL_TX_RETRY(x) ((x) << 4)
+#define RAL_TX_MORE_FRAG (1 << 8)
+#define RAL_TX_ACK (1 << 9)
+#define RAL_TX_TIMESTAMP (1 << 10)
+#define RAL_TX_OFDM (1 << 11)
+#define RAL_TX_NEWSEQ (1 << 12)
+
+#define RAL_TX_IFS_MASK 0x00006000
+#define RAL_TX_IFS_BACKOFF (0 << 13)
+#define RAL_TX_IFS_SIFS (1 << 13)
+#define RAL_TX_IFS_NEWBACKOFF (2 << 13)
+#define RAL_TX_IFS_NONE (3 << 13)
+
+ uint16_t wme;
+#define RAL_LOGCWMAX(x) (((x) & 0xf) << 12)
+#define RAL_LOGCWMIN(x) (((x) & 0xf) << 8)
+#define RAL_AIFSN(x) (((x) & 0x3) << 6)
+#define RAL_IVOFFSET(x) (((x) & 0x3f))
+
+ uint16_t reserved1;
+ uint8_t plcp_signal;
+ uint8_t plcp_service;
+#define RAL_PLCP_LENGEXT 0x80
+
+ uint8_t plcp_length_lo;
+ uint8_t plcp_length_hi;
+ uint32_t iv;
+ uint32_t eiv;
+} __packed;
+
+struct ural_rx_desc {
+ uint32_t flags;
+#define RAL_RX_CRC_ERROR (1 << 5)
+#define RAL_RX_OFDM (1 << 6)
+#define RAL_RX_PHY_ERROR (1 << 7)
+
+ uint8_t rssi;
+ uint8_t rate;
+ uint16_t reserved;
+
+ uint32_t iv;
+ uint32_t eiv;
+} __packed;
+
+#define RAL_RF_LOBUSY (1 << 15)
+#define RAL_RF_BUSY (1U << 31)
+#define RAL_RF_20BIT (20 << 24)
+
+#define RAL_RF1 0
+#define RAL_RF2 2
+#define RAL_RF3 1
+#define RAL_RF4 3
+
+#define RAL_EEPROM_ADDRESS 0x0004
+#define RAL_EEPROM_TXPOWER 0x003c
+#define RAL_EEPROM_CONFIG0 0x0016
+#define RAL_EEPROM_BBP_BASE 0x001c
diff --git a/freebsd/sys/dev/usb/wlan/if_uralvar.h b/freebsd/sys/dev/usb/wlan/if_uralvar.h
new file mode 100644
index 00000000..dd863fe0
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_uralvar.h
@@ -0,0 +1,136 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RAL_TX_LIST_COUNT 8
+#define RAL_TX_MINFREE 2
+
+#define URAL_SCAN_START 1
+#define URAL_SCAN_END 2
+#define URAL_SET_CHANNEL 3
+
+
+struct ural_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_antsignal;
+ int8_t wr_antnoise;
+ uint8_t wr_antenna;
+} __packed __aligned(8);
+
+#define RAL_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE))
+
+struct ural_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+ uint8_t wt_antenna;
+} __packed __aligned(8);
+
+#define RAL_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+struct ural_softc;
+
+struct ural_tx_data {
+ STAILQ_ENTRY(ural_tx_data) next;
+ struct ural_softc *sc;
+ struct ural_tx_desc desc;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ int rate;
+};
+typedef STAILQ_HEAD(, ural_tx_data) ural_txdhead;
+
+struct ural_vap {
+ struct ieee80211vap vap;
+
+ struct usb_callout ratectl_ch;
+ struct task ratectl_task;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define URAL_VAP(vap) ((struct ural_vap *)(vap))
+
+enum {
+ URAL_BULK_WR,
+ URAL_BULK_RD,
+ URAL_N_TRANSFER = 2,
+};
+
+struct ural_softc {
+ struct ieee80211com sc_ic;
+ struct ieee80211_ratectl_tx_stats sc_txs;
+ struct mbufq sc_snd;
+ device_t sc_dev;
+ struct usb_device *sc_udev;
+
+ uint32_t asic_rev;
+ uint8_t rf_rev;
+
+ struct usb_xfer *sc_xfer[URAL_N_TRANSFER];
+
+ struct ural_tx_data tx_data[RAL_TX_LIST_COUNT];
+ ural_txdhead tx_q;
+ ural_txdhead tx_free;
+ int tx_nfree;
+ struct ural_rx_desc sc_rx_desc;
+
+ struct mtx sc_mtx;
+
+ uint16_t sta[11];
+ uint32_t rf_regs[4];
+ uint8_t txpow[14];
+ u_int sc_detached:1,
+ sc_running:1;
+
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
+
+ struct {
+ uint8_t val;
+ uint8_t reg;
+ } __packed bbp_prom[16];
+
+ int led_mode;
+ int hw_radio;
+ int rx_ant;
+ int tx_ant;
+ int nb_ant;
+
+ struct ural_rx_radiotap_header sc_rxtap;
+ struct ural_tx_radiotap_header sc_txtap;
+};
+
+#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RAL_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t)
diff --git a/freebsd/sys/dev/usb/wlan/if_urtw.c b/freebsd/sys/dev/usb/wlan/if_urtw.c
new file mode 100644
index 00000000..501cc50c
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_urtw.c
@@ -0,0 +1,4406 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <rtems/bsd/sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#include <dev/usb/wlan/if_urtwreg.h>
+#include <dev/usb/wlan/if_urtwvar.h>
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, urtw, CTLFLAG_RW, 0, "USB Realtek 8187L");
+#ifdef URTW_DEBUG
+int urtw_debug = 0;
+SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RWTUN, &urtw_debug, 0,
+ "control debugging printfs");
+enum {
+ URTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ URTW_DEBUG_RECV = 0x00000002, /* basic recv operation */
+ URTW_DEBUG_RESET = 0x00000004, /* reset processing */
+ URTW_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */
+ URTW_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */
+ URTW_DEBUG_STATE = 0x00000020, /* 802.11 state transitions */
+ URTW_DEBUG_STAT = 0x00000040, /* statistic */
+ URTW_DEBUG_INIT = 0x00000080, /* initialization of dev */
+ URTW_DEBUG_TXSTATUS = 0x00000100, /* tx status */
+ URTW_DEBUG_ANY = 0xffffffff
+};
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (sc->sc_debug & (m)) \
+ printf(fmt, __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#endif
+static int urtw_preamble_mode = URTW_PREAMBLE_MODE_LONG;
+SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RWTUN,
+ &urtw_preamble_mode, 0, "set the preable mode (long or short)");
+
+/* recognized device vendors/products */
+#define urtw_lookup(v, p) \
+ ((const struct urtw_type *)usb_lookup(urtw_devs, v, p))
+#define URTW_DEV_B(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187B) }
+#define URTW_DEV_L(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187L) }
+#define URTW_REV_RTL8187B 0
+#define URTW_REV_RTL8187L 1
+static const STRUCT_USB_HOST_ID urtw_devs[] = {
+ URTW_DEV_B(NETGEAR, WG111V3),
+ URTW_DEV_B(REALTEK, RTL8187B_0),
+ URTW_DEV_B(REALTEK, RTL8187B_1),
+ URTW_DEV_B(REALTEK, RTL8187B_2),
+ URTW_DEV_B(SITECOMEU, WL168V4),
+ URTW_DEV_L(ASUS, P5B_WIFI),
+ URTW_DEV_L(BELKIN, F5D7050E),
+ URTW_DEV_L(LINKSYS4, WUSB54GCV2),
+ URTW_DEV_L(NETGEAR, WG111V2),
+ URTW_DEV_L(REALTEK, RTL8187),
+ URTW_DEV_L(SITECOMEU, WL168V1),
+ URTW_DEV_L(SURECOM, EP9001G2A),
+ { USB_VPI(USB_VENDOR_OVISLINK, 0x8187, URTW_REV_RTL8187L) },
+ { USB_VPI(USB_VENDOR_DICKSMITH, 0x9401, URTW_REV_RTL8187L) },
+ { USB_VPI(USB_VENDOR_HP, 0xca02, URTW_REV_RTL8187L) },
+ { USB_VPI(USB_VENDOR_LOGITEC, 0x010c, URTW_REV_RTL8187L) },
+ { USB_VPI(USB_VENDOR_NETGEAR, 0x6100, URTW_REV_RTL8187L) },
+ { USB_VPI(USB_VENDOR_SPHAIRON, 0x0150, URTW_REV_RTL8187L) },
+ { USB_VPI(USB_VENDOR_QCOM, 0x6232, URTW_REV_RTL8187L) },
+#undef URTW_DEV_L
+#undef URTW_DEV_B
+};
+
+#define urtw_read8_m(sc, val, data) do { \
+ error = urtw_read8_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_write8_m(sc, val, data) do { \
+ error = urtw_write8_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_read16_m(sc, val, data) do { \
+ error = urtw_read16_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_write16_m(sc, val, data) do { \
+ error = urtw_write16_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_read32_m(sc, val, data) do { \
+ error = urtw_read32_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_write32_m(sc, val, data) do { \
+ error = urtw_write32_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_8187_write_phy_ofdm(sc, val, data) do { \
+ error = urtw_8187_write_phy_ofdm_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_8187_write_phy_cck(sc, val, data) do { \
+ error = urtw_8187_write_phy_cck_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define urtw_8225_write(sc, val, data) do { \
+ error = urtw_8225_write_c(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+
+struct urtw_pair {
+ uint32_t reg;
+ uint32_t val;
+};
+
+static uint8_t urtw_8225_agc[] = {
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b,
+ 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90,
+ 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85,
+ 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a,
+ 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f,
+ 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24,
+ 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19,
+ 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
+ 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+static uint8_t urtw_8225z2_agc[] = {
+ 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51,
+ 0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b,
+ 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25,
+ 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f,
+ 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2a,
+ 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31
+};
+
+static const uint8_t urtw_chan_2ghz[] =
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+
+static uint32_t urtw_8225_channel[] = {
+ 0x0000, /* dummy channel 0 */
+ 0x085c, /* 1 */
+ 0x08dc, /* 2 */
+ 0x095c, /* 3 */
+ 0x09dc, /* 4 */
+ 0x0a5c, /* 5 */
+ 0x0adc, /* 6 */
+ 0x0b5c, /* 7 */
+ 0x0bdc, /* 8 */
+ 0x0c5c, /* 9 */
+ 0x0cdc, /* 10 */
+ 0x0d5c, /* 11 */
+ 0x0ddc, /* 12 */
+ 0x0e5c, /* 13 */
+ 0x0f72, /* 14 */
+};
+
+static uint8_t urtw_8225_gain[] = {
+ 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */
+ 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */
+ 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */
+ 0x33, 0x80, 0x79, 0xc5, /* -78dbm */
+ 0x43, 0x78, 0x76, 0xc5, /* -74dbm */
+ 0x53, 0x60, 0x73, 0xc5, /* -70dbm */
+ 0x63, 0x58, 0x70, 0xc5, /* -66dbm */
+};
+
+static struct urtw_pair urtw_8225_rf_part1[] = {
+ { 0x00, 0x0067 }, { 0x01, 0x0fe0 }, { 0x02, 0x044d }, { 0x03, 0x0441 },
+ { 0x04, 0x0486 }, { 0x05, 0x0bc0 }, { 0x06, 0x0ae6 }, { 0x07, 0x082a },
+ { 0x08, 0x001f }, { 0x09, 0x0334 }, { 0x0a, 0x0fd4 }, { 0x0b, 0x0391 },
+ { 0x0c, 0x0050 }, { 0x0d, 0x06db }, { 0x0e, 0x0029 }, { 0x0f, 0x0914 },
+};
+
+static struct urtw_pair urtw_8225_rf_part2[] = {
+ { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 },
+ { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 },
+ { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x09 }, { 0x0b, 0x80 },
+ { 0x0c, 0x01 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 },
+ { 0x11, 0x06 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 },
+ { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef },
+ { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x76 }, { 0x1c, 0x04 },
+ { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x27 },
+ { 0x22, 0x16 }, { 0x24, 0x46 }, { 0x25, 0x20 }, { 0x26, 0x90 },
+ { 0x27, 0x88 }
+};
+
+static struct urtw_pair urtw_8225_rf_part3[] = {
+ { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 },
+ { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x10, 0x9b },
+ { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 },
+ { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x40, 0x86 }, { 0x41, 0x8d },
+ { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x1f }, { 0x45, 0x1e },
+ { 0x46, 0x1a }, { 0x47, 0x15 }, { 0x48, 0x10 }, { 0x49, 0x0a },
+ { 0x4a, 0x05 }, { 0x4b, 0x02 }, { 0x4c, 0x05 }
+};
+
+static uint16_t urtw_8225_rxgain[] = {
+ 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
+ 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
+ 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
+ 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
+ 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
+ 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
+ 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
+ 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
+ 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
+ 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
+ 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
+ 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
+};
+
+static uint8_t urtw_8225_threshold[] = {
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd,
+};
+
+static uint8_t urtw_8225_tx_gain_cck_ofdm[] = {
+ 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
+};
+
+static uint8_t urtw_8225_txpwr_cck[] = {
+ 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
+ 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
+ 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
+ 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
+};
+
+static uint8_t urtw_8225_txpwr_cck_ch14[] = {
+ 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
+ 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
+};
+
+static uint8_t urtw_8225_txpwr_ofdm[]={
+ 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
+};
+
+static uint8_t urtw_8225v2_gain_bg[]={
+ 0x23, 0x15, 0xa5, /* -82-1dbm */
+ 0x23, 0x15, 0xb5, /* -82-2dbm */
+ 0x23, 0x15, 0xc5, /* -82-3dbm */
+ 0x33, 0x15, 0xc5, /* -78dbm */
+ 0x43, 0x15, 0xc5, /* -74dbm */
+ 0x53, 0x15, 0xc5, /* -70dbm */
+ 0x63, 0x15, 0xc5, /* -66dbm */
+};
+
+static struct urtw_pair urtw_8225v2_rf_part1[] = {
+ { 0x00, 0x02bf }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 },
+ { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a },
+ { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb },
+ { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 }
+};
+
+static struct urtw_pair urtw_8225v2b_rf_part0[] = {
+ { 0x00, 0x00b7 }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 },
+ { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a },
+ { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb },
+ { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 }
+};
+
+static struct urtw_pair urtw_8225v2b_rf_part1[] = {
+ {0x0f0, 0x32}, {0x0f1, 0x32}, {0x0f2, 0x00},
+ {0x0f3, 0x00}, {0x0f4, 0x32}, {0x0f5, 0x43},
+ {0x0f6, 0x00}, {0x0f7, 0x00}, {0x0f8, 0x46},
+ {0x0f9, 0xa4}, {0x0fa, 0x00}, {0x0fb, 0x00},
+ {0x0fc, 0x96}, {0x0fd, 0xa4}, {0x0fe, 0x00},
+ {0x0ff, 0x00}, {0x158, 0x4b}, {0x159, 0x00},
+ {0x15a, 0x4b}, {0x15b, 0x00}, {0x160, 0x4b},
+ {0x161, 0x09}, {0x162, 0x4b}, {0x163, 0x09},
+ {0x1ce, 0x0f}, {0x1cf, 0x00}, {0x1e0, 0xff},
+ {0x1e1, 0x0f}, {0x1e2, 0x00}, {0x1f0, 0x4e},
+ {0x1f1, 0x01}, {0x1f2, 0x02}, {0x1f3, 0x03},
+ {0x1f4, 0x04}, {0x1f5, 0x05}, {0x1f6, 0x06},
+ {0x1f7, 0x07}, {0x1f8, 0x08}, {0x24e, 0x00},
+ {0x20c, 0x04}, {0x221, 0x61}, {0x222, 0x68},
+ {0x223, 0x6f}, {0x224, 0x76}, {0x225, 0x7d},
+ {0x226, 0x84}, {0x227, 0x8d}, {0x24d, 0x08},
+ {0x250, 0x05}, {0x251, 0xf5}, {0x252, 0x04},
+ {0x253, 0xa0}, {0x254, 0x1f}, {0x255, 0x23},
+ {0x256, 0x45}, {0x257, 0x67}, {0x258, 0x08},
+ {0x259, 0x08}, {0x25a, 0x08}, {0x25b, 0x08},
+ {0x260, 0x08}, {0x261, 0x08}, {0x262, 0x08},
+ {0x263, 0x08}, {0x264, 0xcf}, {0x272, 0x56},
+ {0x273, 0x9a}, {0x034, 0xf0}, {0x035, 0x0f},
+ {0x05b, 0x40}, {0x084, 0x88}, {0x085, 0x24},
+ {0x088, 0x54}, {0x08b, 0xb8}, {0x08c, 0x07},
+ {0x08d, 0x00}, {0x094, 0x1b}, {0x095, 0x12},
+ {0x096, 0x00}, {0x097, 0x06}, {0x09d, 0x1a},
+ {0x09f, 0x10}, {0x0b4, 0x22}, {0x0be, 0x80},
+ {0x0db, 0x00}, {0x0ee, 0x00}, {0x091, 0x03},
+ {0x24c, 0x00}, {0x39f, 0x00}, {0x08c, 0x01},
+ {0x08d, 0x10}, {0x08e, 0x08}, {0x08f, 0x00}
+};
+
+static struct urtw_pair urtw_8225v2_rf_part2[] = {
+ { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 },
+ { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 },
+ { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x08 }, { 0x0b, 0x80 },
+ { 0x0c, 0x01 }, { 0x0d, 0x43 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 },
+ { 0x10, 0x84 }, { 0x11, 0x07 }, { 0x12, 0x20 }, { 0x13, 0x20 },
+ { 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 },
+ { 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x15 },
+ { 0x1c, 0x04 }, { 0x1d, 0xc5 }, { 0x1e, 0x95 }, { 0x1f, 0x75 },
+ { 0x20, 0x1f }, { 0x21, 0x17 }, { 0x22, 0x16 }, { 0x23, 0x80 },
+ { 0x24, 0x46 }, { 0x25, 0x00 }, { 0x26, 0x90 }, { 0x27, 0x88 }
+};
+
+static struct urtw_pair urtw_8225v2b_rf_part2[] = {
+ { 0x00, 0x10 }, { 0x01, 0x0d }, { 0x02, 0x01 }, { 0x03, 0x00 },
+ { 0x04, 0x14 }, { 0x05, 0xfb }, { 0x06, 0xfb }, { 0x07, 0x60 },
+ { 0x08, 0x00 }, { 0x09, 0x60 }, { 0x0a, 0x00 }, { 0x0b, 0x00 },
+ { 0x0c, 0x00 }, { 0x0d, 0x5c }, { 0x0e, 0x00 }, { 0x0f, 0x00 },
+ { 0x10, 0x40 }, { 0x11, 0x00 }, { 0x12, 0x40 }, { 0x13, 0x00 },
+ { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0xa8 }, { 0x17, 0x26 },
+ { 0x18, 0x32 }, { 0x19, 0x33 }, { 0x1a, 0x07 }, { 0x1b, 0xa5 },
+ { 0x1c, 0x6f }, { 0x1d, 0x55 }, { 0x1e, 0xc8 }, { 0x1f, 0xb3 },
+ { 0x20, 0x0a }, { 0x21, 0xe1 }, { 0x22, 0x2C }, { 0x23, 0x8a },
+ { 0x24, 0x86 }, { 0x25, 0x83 }, { 0x26, 0x34 }, { 0x27, 0x0f },
+ { 0x28, 0x4f }, { 0x29, 0x24 }, { 0x2a, 0x6f }, { 0x2b, 0xc2 },
+ { 0x2c, 0x6b }, { 0x2d, 0x40 }, { 0x2e, 0x80 }, { 0x2f, 0x00 },
+ { 0x30, 0xc0 }, { 0x31, 0xc1 }, { 0x32, 0x58 }, { 0x33, 0xf1 },
+ { 0x34, 0x00 }, { 0x35, 0xe4 }, { 0x36, 0x90 }, { 0x37, 0x3e },
+ { 0x38, 0x6d }, { 0x39, 0x3c }, { 0x3a, 0xfb }, { 0x3b, 0x07 }
+};
+
+static struct urtw_pair urtw_8225v2_rf_part3[] = {
+ { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 },
+ { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x09, 0x11 },
+ { 0x0a, 0x17 }, { 0x0b, 0x11 }, { 0x10, 0x9b }, { 0x11, 0x88 },
+ { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 },
+ { 0x1b, 0x08 }, { 0x1d, 0x00 }, { 0x40, 0x86 }, { 0x41, 0x9d },
+ { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x36 }, { 0x45, 0x35 },
+ { 0x46, 0x2e }, { 0x47, 0x25 }, { 0x48, 0x1c }, { 0x49, 0x12 },
+ { 0x4a, 0x09 }, { 0x4b, 0x04 }, { 0x4c, 0x05 }
+};
+
+static uint16_t urtw_8225v2_rxgain[] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009,
+ 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141,
+ 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183,
+ 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244,
+ 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288,
+ 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345,
+ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389,
+ 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393,
+ 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d,
+ 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9,
+ 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
+ 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
+};
+
+static uint16_t urtw_8225v2b_rxgain[] = {
+ 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
+ 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
+ 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
+ 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
+ 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
+ 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
+ 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
+ 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
+ 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
+ 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
+ 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
+ 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
+};
+
+static uint8_t urtw_8225v2_tx_gain_cck_ofdm[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+};
+
+static uint8_t urtw_8225v2_txpwr_cck[] = {
+ 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
+};
+
+static uint8_t urtw_8225v2_txpwr_cck_ch14[] = {
+ 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
+};
+
+static uint8_t urtw_8225v2b_txpwr_cck[] = {
+ 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04,
+ 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03
+};
+
+static uint8_t urtw_8225v2b_txpwr_cck_ch14[] = {
+ 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00
+};
+
+static struct urtw_pair urtw_ratetable[] = {
+ { 2, 0 }, { 4, 1 }, { 11, 2 }, { 12, 4 }, { 18, 5 },
+ { 22, 3 }, { 24, 6 }, { 36, 7 }, { 48, 8 }, { 72, 9 },
+ { 96, 10 }, { 108, 11 }
+};
+
+#if 0
+static const uint8_t urtw_8187b_reg_table[][3] = {
+ { 0xf0, 0x32, 0 }, { 0xf1, 0x32, 0 }, { 0xf2, 0x00, 0 },
+ { 0xf3, 0x00, 0 }, { 0xf4, 0x32, 0 }, { 0xf5, 0x43, 0 },
+ { 0xf6, 0x00, 0 }, { 0xf7, 0x00, 0 }, { 0xf8, 0x46, 0 },
+ { 0xf9, 0xa4, 0 }, { 0xfa, 0x00, 0 }, { 0xfb, 0x00, 0 },
+ { 0xfc, 0x96, 0 }, { 0xfd, 0xa4, 0 }, { 0xfe, 0x00, 0 },
+ { 0xff, 0x00, 0 }, { 0x58, 0x4b, 1 }, { 0x59, 0x00, 1 },
+ { 0x5a, 0x4b, 1 }, { 0x5b, 0x00, 1 }, { 0x60, 0x4b, 1 },
+ { 0x61, 0x09, 1 }, { 0x62, 0x4b, 1 }, { 0x63, 0x09, 1 },
+ { 0xce, 0x0f, 1 }, { 0xcf, 0x00, 1 }, { 0xe0, 0xff, 1 },
+ { 0xe1, 0x0f, 1 }, { 0xe2, 0x00, 1 }, { 0xf0, 0x4e, 1 },
+ { 0xf1, 0x01, 1 }, { 0xf2, 0x02, 1 }, { 0xf3, 0x03, 1 },
+ { 0xf4, 0x04, 1 }, { 0xf5, 0x05, 1 }, { 0xf6, 0x06, 1 },
+ { 0xf7, 0x07, 1 }, { 0xf8, 0x08, 1 }, { 0x4e, 0x00, 2 },
+ { 0x0c, 0x04, 2 }, { 0x21, 0x61, 2 }, { 0x22, 0x68, 2 },
+ { 0x23, 0x6f, 2 }, { 0x24, 0x76, 2 }, { 0x25, 0x7d, 2 },
+ { 0x26, 0x84, 2 }, { 0x27, 0x8d, 2 }, { 0x4d, 0x08, 2 },
+ { 0x50, 0x05, 2 }, { 0x51, 0xf5, 2 }, { 0x52, 0x04, 2 },
+ { 0x53, 0xa0, 2 }, { 0x54, 0x1f, 2 }, { 0x55, 0x23, 2 },
+ { 0x56, 0x45, 2 }, { 0x57, 0x67, 2 }, { 0x58, 0x08, 2 },
+ { 0x59, 0x08, 2 }, { 0x5a, 0x08, 2 }, { 0x5b, 0x08, 2 },
+ { 0x60, 0x08, 2 }, { 0x61, 0x08, 2 }, { 0x62, 0x08, 2 },
+ { 0x63, 0x08, 2 }, { 0x64, 0xcf, 2 }, { 0x72, 0x56, 2 },
+ { 0x73, 0x9a, 2 }, { 0x34, 0xf0, 0 }, { 0x35, 0x0f, 0 },
+ { 0x5b, 0x40, 0 }, { 0x84, 0x88, 0 }, { 0x85, 0x24, 0 },
+ { 0x88, 0x54, 0 }, { 0x8b, 0xb8, 0 }, { 0x8c, 0x07, 0 },
+ { 0x8d, 0x00, 0 }, { 0x94, 0x1b, 0 }, { 0x95, 0x12, 0 },
+ { 0x96, 0x00, 0 }, { 0x97, 0x06, 0 }, { 0x9d, 0x1a, 0 },
+ { 0x9f, 0x10, 0 }, { 0xb4, 0x22, 0 }, { 0xbe, 0x80, 0 },
+ { 0xdb, 0x00, 0 }, { 0xee, 0x00, 0 }, { 0x91, 0x03, 0 },
+ { 0x4c, 0x00, 2 }, { 0x9f, 0x00, 3 }, { 0x8c, 0x01, 0 },
+ { 0x8d, 0x10, 0 }, { 0x8e, 0x08, 0 }, { 0x8f, 0x00, 0 }
+};
+#endif
+
+static usb_callback_t urtw_bulk_rx_callback;
+static usb_callback_t urtw_bulk_tx_callback;
+static usb_callback_t urtw_bulk_tx_status_callback;
+
+static const struct usb_config urtw_8187b_usbconfig[URTW_8187B_N_XFERS] = {
+ [URTW_8187B_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = 0x83,
+ .direction = UE_DIR_IN,
+ .bufsize = MCLBYTES,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = urtw_bulk_rx_callback
+ },
+ [URTW_8187B_BULK_TX_STATUS] = {
+ .type = UE_BULK,
+ .endpoint = 0x89,
+ .direction = UE_DIR_IN,
+ .bufsize = sizeof(uint64_t),
+ .flags = {
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = urtw_bulk_tx_status_callback
+ },
+ [URTW_8187B_BULK_TX_BE] = {
+ .type = UE_BULK,
+ .endpoint = URTW_8187B_TXPIPE_BE,
+ .direction = UE_DIR_OUT,
+ .bufsize = URTW_TX_MAXSIZE * URTW_TX_DATA_LIST_COUNT,
+ .flags = {
+ .force_short_xfer = 1,
+ .pipe_bof = 1,
+ },
+ .callback = urtw_bulk_tx_callback,
+ .timeout = URTW_DATA_TIMEOUT
+ },
+ [URTW_8187B_BULK_TX_BK] = {
+ .type = UE_BULK,
+ .endpoint = URTW_8187B_TXPIPE_BK,
+ .direction = UE_DIR_OUT,
+ .bufsize = URTW_TX_MAXSIZE,
+ .flags = {
+ .ext_buffer = 1,
+ .force_short_xfer = 1,
+ .pipe_bof = 1,
+ },
+ .callback = urtw_bulk_tx_callback,
+ .timeout = URTW_DATA_TIMEOUT
+ },
+ [URTW_8187B_BULK_TX_VI] = {
+ .type = UE_BULK,
+ .endpoint = URTW_8187B_TXPIPE_VI,
+ .direction = UE_DIR_OUT,
+ .bufsize = URTW_TX_MAXSIZE,
+ .flags = {
+ .ext_buffer = 1,
+ .force_short_xfer = 1,
+ .pipe_bof = 1,
+ },
+ .callback = urtw_bulk_tx_callback,
+ .timeout = URTW_DATA_TIMEOUT
+ },
+ [URTW_8187B_BULK_TX_VO] = {
+ .type = UE_BULK,
+ .endpoint = URTW_8187B_TXPIPE_VO,
+ .direction = UE_DIR_OUT,
+ .bufsize = URTW_TX_MAXSIZE,
+ .flags = {
+ .ext_buffer = 1,
+ .force_short_xfer = 1,
+ .pipe_bof = 1,
+ },
+ .callback = urtw_bulk_tx_callback,
+ .timeout = URTW_DATA_TIMEOUT
+ },
+ [URTW_8187B_BULK_TX_EP12] = {
+ .type = UE_BULK,
+ .endpoint = 0xc,
+ .direction = UE_DIR_OUT,
+ .bufsize = URTW_TX_MAXSIZE,
+ .flags = {
+ .ext_buffer = 1,
+ .force_short_xfer = 1,
+ .pipe_bof = 1,
+ },
+ .callback = urtw_bulk_tx_callback,
+ .timeout = URTW_DATA_TIMEOUT
+ }
+};
+
+static const struct usb_config urtw_8187l_usbconfig[URTW_8187L_N_XFERS] = {
+ [URTW_8187L_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = 0x81,
+ .direction = UE_DIR_IN,
+ .bufsize = MCLBYTES,
+ .flags = {
+ .ext_buffer = 1,
+ .pipe_bof = 1,
+ .short_xfer_ok = 1
+ },
+ .callback = urtw_bulk_rx_callback
+ },
+ [URTW_8187L_BULK_TX_LOW] = {
+ .type = UE_BULK,
+ .endpoint = 0x2,
+ .direction = UE_DIR_OUT,
+ .bufsize = URTW_TX_MAXSIZE * URTW_TX_DATA_LIST_COUNT,
+ .flags = {
+ .force_short_xfer = 1,
+ .pipe_bof = 1,
+ },
+ .callback = urtw_bulk_tx_callback,
+ .timeout = URTW_DATA_TIMEOUT
+ },
+ [URTW_8187L_BULK_TX_NORMAL] = {
+ .type = UE_BULK,
+ .endpoint = 0x3,
+ .direction = UE_DIR_OUT,
+ .bufsize = URTW_TX_MAXSIZE,
+ .flags = {
+ .ext_buffer = 1,
+ .force_short_xfer = 1,
+ .pipe_bof = 1,
+ },
+ .callback = urtw_bulk_tx_callback,
+ .timeout = URTW_DATA_TIMEOUT
+ },
+};
+
+static struct ieee80211vap *urtw_vap_create(struct ieee80211com *,
+ const char [IFNAMSIZ], int, enum ieee80211_opmode,
+ int, const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void urtw_vap_delete(struct ieee80211vap *);
+static void urtw_init(struct urtw_softc *);
+static void urtw_stop(struct urtw_softc *);
+static void urtw_parent(struct ieee80211com *);
+static int urtw_transmit(struct ieee80211com *, struct mbuf *);
+static void urtw_start(struct urtw_softc *);
+static int urtw_alloc_rx_data_list(struct urtw_softc *);
+static int urtw_alloc_tx_data_list(struct urtw_softc *);
+static int urtw_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void urtw_scan_start(struct ieee80211com *);
+static void urtw_scan_end(struct ieee80211com *);
+static void urtw_getradiocaps(struct ieee80211com *, int, int *,
+ struct ieee80211_channel[]);
+static void urtw_set_channel(struct ieee80211com *);
+static void urtw_update_mcast(struct ieee80211com *);
+static int urtw_tx_start(struct urtw_softc *,
+ struct ieee80211_node *, struct mbuf *,
+ struct urtw_data *, int);
+static int urtw_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static void urtw_led_ch(void *);
+static void urtw_ledtask(void *, int);
+static void urtw_watchdog(void *);
+static void urtw_set_multi(void *);
+static int urtw_isbmode(uint16_t);
+static uint16_t urtw_rate2rtl(uint32_t);
+static uint16_t urtw_rtl2rate(uint32_t);
+static usb_error_t urtw_set_rate(struct urtw_softc *);
+static usb_error_t urtw_update_msr(struct urtw_softc *);
+static usb_error_t urtw_read8_c(struct urtw_softc *, int, uint8_t *);
+static usb_error_t urtw_read16_c(struct urtw_softc *, int, uint16_t *);
+static usb_error_t urtw_read32_c(struct urtw_softc *, int, uint32_t *);
+static usb_error_t urtw_write8_c(struct urtw_softc *, int, uint8_t);
+static usb_error_t urtw_write16_c(struct urtw_softc *, int, uint16_t);
+static usb_error_t urtw_write32_c(struct urtw_softc *, int, uint32_t);
+static usb_error_t urtw_eprom_cs(struct urtw_softc *, int);
+static usb_error_t urtw_eprom_ck(struct urtw_softc *);
+static usb_error_t urtw_eprom_sendbits(struct urtw_softc *, int16_t *,
+ int);
+static usb_error_t urtw_eprom_read32(struct urtw_softc *, uint32_t,
+ uint32_t *);
+static usb_error_t urtw_eprom_readbit(struct urtw_softc *, int16_t *);
+static usb_error_t urtw_eprom_writebit(struct urtw_softc *, int16_t);
+static usb_error_t urtw_get_macaddr(struct urtw_softc *);
+static usb_error_t urtw_get_txpwr(struct urtw_softc *);
+static usb_error_t urtw_get_rfchip(struct urtw_softc *);
+static usb_error_t urtw_led_init(struct urtw_softc *);
+static usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *);
+static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *, uint8_t);
+static usb_error_t urtw_8187_write_phy(struct urtw_softc *, uint8_t,
+ uint32_t);
+static usb_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *,
+ uint8_t, uint32_t);
+static usb_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t,
+ uint32_t);
+static usb_error_t urtw_8225_setgain(struct urtw_softc *, int16_t);
+static usb_error_t urtw_8225_usb_init(struct urtw_softc *);
+static usb_error_t urtw_8225_write_c(struct urtw_softc *, uint8_t,
+ uint16_t);
+static usb_error_t urtw_8225_write_s16(struct urtw_softc *, uint8_t, int,
+ uint16_t *);
+static usb_error_t urtw_8225_read(struct urtw_softc *, uint8_t,
+ uint32_t *);
+static usb_error_t urtw_8225_rf_init(struct urtw_softc *);
+static usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *, int);
+static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *, int);
+static usb_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *, int);
+static usb_error_t urtw_8225_rf_stop(struct urtw_softc *);
+static usb_error_t urtw_8225v2_rf_init(struct urtw_softc *);
+static usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *, int);
+static usb_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int);
+static usb_error_t urtw_8225v2_setgain(struct urtw_softc *, int16_t);
+static usb_error_t urtw_8225_isv2(struct urtw_softc *, int *);
+static usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *);
+static usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *, int);
+static usb_error_t urtw_read8e(struct urtw_softc *, int, uint8_t *);
+static usb_error_t urtw_write8e(struct urtw_softc *, int, uint8_t);
+static usb_error_t urtw_8180_set_anaparam(struct urtw_softc *, uint32_t);
+static usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t);
+static usb_error_t urtw_intr_enable(struct urtw_softc *);
+static usb_error_t urtw_intr_disable(struct urtw_softc *);
+static usb_error_t urtw_reset(struct urtw_softc *);
+static usb_error_t urtw_led_on(struct urtw_softc *, int);
+static usb_error_t urtw_led_ctl(struct urtw_softc *, int);
+static usb_error_t urtw_led_blink(struct urtw_softc *);
+static usb_error_t urtw_led_mode0(struct urtw_softc *, int);
+static usb_error_t urtw_led_mode1(struct urtw_softc *, int);
+static usb_error_t urtw_led_mode2(struct urtw_softc *, int);
+static usb_error_t urtw_led_mode3(struct urtw_softc *, int);
+static usb_error_t urtw_rx_setconf(struct urtw_softc *);
+static usb_error_t urtw_rx_enable(struct urtw_softc *);
+static usb_error_t urtw_tx_enable(struct urtw_softc *sc);
+static void urtw_free_tx_data_list(struct urtw_softc *);
+static void urtw_free_rx_data_list(struct urtw_softc *);
+static void urtw_free_data_list(struct urtw_softc *,
+ struct urtw_data data[], int, int);
+static usb_error_t urtw_adapter_start(struct urtw_softc *);
+static usb_error_t urtw_adapter_start_b(struct urtw_softc *);
+static usb_error_t urtw_set_mode(struct urtw_softc *, uint32_t);
+static usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *);
+static usb_error_t urtw_do_request(struct urtw_softc *,
+ struct usb_device_request *, void *);
+static usb_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *, int);
+static usb_error_t urtw_led_off(struct urtw_softc *, int);
+static void urtw_abort_xfers(struct urtw_softc *);
+static struct urtw_data *
+ urtw_getbuf(struct urtw_softc *sc);
+static int urtw_compute_txtime(uint16_t, uint16_t, uint8_t,
+ uint8_t);
+static void urtw_updateslot(struct ieee80211com *);
+static void urtw_updateslottask(void *, int);
+static void urtw_sysctl_node(struct urtw_softc *);
+
+static int
+urtw_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != URTW_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != URTW_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(urtw_devs, sizeof(urtw_devs), uaa));
+}
+
+static int
+urtw_attach(device_t dev)
+{
+ const struct usb_config *setup_start;
+ int ret = ENXIO;
+ struct urtw_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t iface_index = URTW_IFACE_INDEX; /* XXX */
+ uint16_t n_setup;
+ uint32_t data;
+ usb_error_t error;
+
+ device_set_usb_desc(dev);
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+ if (USB_GET_DRIVER_INFO(uaa) == URTW_REV_RTL8187B)
+ sc->sc_flags |= URTW_RTL8187B;
+#ifdef URTW_DEBUG
+ sc->sc_debug = urtw_debug;
+#endif
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
+ MTX_DEF);
+ usb_callout_init_mtx(&sc->sc_led_ch, &sc->sc_mtx, 0);
+ TASK_INIT(&sc->sc_led_task, 0, urtw_ledtask, sc);
+ TASK_INIT(&sc->sc_updateslot_task, 0, urtw_updateslottask, sc);
+ callout_init(&sc->sc_watchdog_ch, 0);
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ if (sc->sc_flags & URTW_RTL8187B) {
+ setup_start = urtw_8187b_usbconfig;
+ n_setup = URTW_8187B_N_XFERS;
+ } else {
+ setup_start = urtw_8187l_usbconfig;
+ n_setup = URTW_8187L_N_XFERS;
+ }
+
+ error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ setup_start, n_setup, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "could not allocate USB transfers, "
+ "err=%s\n", usbd_errstr(error));
+ ret = ENXIO;
+ goto fail0;
+ }
+
+ if (sc->sc_flags & URTW_RTL8187B) {
+ sc->sc_tx_dma_buf =
+ usbd_xfer_get_frame_buffer(sc->sc_xfer[
+ URTW_8187B_BULK_TX_BE], 0);
+ } else {
+ sc->sc_tx_dma_buf =
+ usbd_xfer_get_frame_buffer(sc->sc_xfer[
+ URTW_8187L_BULK_TX_LOW], 0);
+ }
+
+ URTW_LOCK(sc);
+
+ urtw_read32_m(sc, URTW_RX, &data);
+ sc->sc_epromtype = (data & URTW_RX_9356SEL) ? URTW_EEPROM_93C56 :
+ URTW_EEPROM_93C46;
+
+ error = urtw_get_rfchip(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_get_macaddr(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_get_txpwr(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_led_init(sc);
+ if (error != 0)
+ goto fail;
+
+ URTW_UNLOCK(sc);
+
+ sc->sc_rts_retry = URTW_DEFAULT_RTS_RETRY;
+ sc->sc_tx_retry = URTW_DEFAULT_TX_RETRY;
+ sc->sc_currate = 3;
+ sc->sc_preamble_mode = urtw_preamble_mode;
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(dev);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA | /* station mode */
+ IEEE80211_C_MONITOR | /* monitor mode supported */
+ IEEE80211_C_TXPMGT | /* tx power management */
+ IEEE80211_C_SHPREAMBLE | /* short preamble supported */
+ IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_BGSCAN | /* capable of bg scanning */
+ IEEE80211_C_WPA; /* 802.11i */
+
+ /* XXX TODO: setup regdomain if URTW_EPROM_CHANPLAN_BY_HW bit is set.*/
+
+ urtw_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
+ ic->ic_channels);
+
+ ieee80211_ifattach(ic);
+ ic->ic_raw_xmit = urtw_raw_xmit;
+ ic->ic_scan_start = urtw_scan_start;
+ ic->ic_scan_end = urtw_scan_end;
+ ic->ic_getradiocaps = urtw_getradiocaps;
+ ic->ic_set_channel = urtw_set_channel;
+ ic->ic_updateslot = urtw_updateslot;
+ ic->ic_vap_create = urtw_vap_create;
+ ic->ic_vap_delete = urtw_vap_delete;
+ ic->ic_update_mcast = urtw_update_mcast;
+ ic->ic_parent = urtw_parent;
+ ic->ic_transmit = urtw_transmit;
+
+ ieee80211_radiotap_attach(ic,
+ &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
+ URTW_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ URTW_RX_RADIOTAP_PRESENT);
+
+ urtw_sysctl_node(sc);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+ return (0);
+
+fail:
+ URTW_UNLOCK(sc);
+ usbd_transfer_unsetup(sc->sc_xfer, (sc->sc_flags & URTW_RTL8187B) ?
+ URTW_8187B_N_XFERS : URTW_8187L_N_XFERS);
+fail0:
+ return (ret);
+}
+
+static int
+urtw_detach(device_t dev)
+{
+ struct urtw_softc *sc = device_get_softc(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ unsigned int x;
+ unsigned int n_xfers;
+
+ /* Prevent further ioctls */
+ URTW_LOCK(sc);
+ sc->sc_flags |= URTW_DETACHED;
+ urtw_stop(sc);
+ URTW_UNLOCK(sc);
+
+ ieee80211_draintask(ic, &sc->sc_updateslot_task);
+ ieee80211_draintask(ic, &sc->sc_led_task);
+
+ usb_callout_drain(&sc->sc_led_ch);
+ callout_drain(&sc->sc_watchdog_ch);
+
+ n_xfers = (sc->sc_flags & URTW_RTL8187B) ?
+ URTW_8187B_N_XFERS : URTW_8187L_N_XFERS;
+
+ /* prevent further allocations from RX/TX data lists */
+ URTW_LOCK(sc);
+ STAILQ_INIT(&sc->sc_tx_active);
+ STAILQ_INIT(&sc->sc_tx_inactive);
+ STAILQ_INIT(&sc->sc_tx_pending);
+
+ STAILQ_INIT(&sc->sc_rx_active);
+ STAILQ_INIT(&sc->sc_rx_inactive);
+ URTW_UNLOCK(sc);
+
+ /* drain USB transfers */
+ for (x = 0; x != n_xfers; x++)
+ usbd_transfer_drain(sc->sc_xfer[x]);
+
+ /* free data buffers */
+ URTW_LOCK(sc);
+ urtw_free_tx_data_list(sc);
+ urtw_free_rx_data_list(sc);
+ URTW_UNLOCK(sc);
+
+ /* free USB transfers and some data buffers */
+ usbd_transfer_unsetup(sc->sc_xfer, n_xfers);
+
+ ieee80211_ifdetach(ic);
+ mbufq_drain(&sc->sc_snd);
+ mtx_destroy(&sc->sc_mtx);
+ return (0);
+}
+
+static void
+urtw_free_tx_data_list(struct urtw_softc *sc)
+{
+ urtw_free_data_list(sc, sc->sc_tx, URTW_TX_DATA_LIST_COUNT, 0);
+}
+
+static void
+urtw_free_rx_data_list(struct urtw_softc *sc)
+{
+ urtw_free_data_list(sc, sc->sc_rx, URTW_RX_DATA_LIST_COUNT, 1);
+}
+
+static void
+urtw_free_data_list(struct urtw_softc *sc, struct urtw_data data[], int ndata,
+ int fillmbuf)
+{
+ int i;
+
+ for (i = 0; i < ndata; i++) {
+ struct urtw_data *dp = &data[i];
+
+ if (fillmbuf == 1) {
+ if (dp->m != NULL) {
+ m_freem(dp->m);
+ dp->m = NULL;
+ dp->buf = NULL;
+ }
+ } else {
+ dp->buf = NULL;
+ }
+ if (dp->ni != NULL) {
+ ieee80211_free_node(dp->ni);
+ dp->ni = NULL;
+ }
+ }
+}
+
+static struct ieee80211vap *
+urtw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct urtw_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return (NULL);
+ uvp = malloc(sizeof(struct urtw_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) {
+ /* out of memory */
+ free(uvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = urtw_newstate;
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status, mac);
+ ic->ic_opmode = opmode;
+ return (vap);
+}
+
+static void
+urtw_vap_delete(struct ieee80211vap *vap)
+{
+ struct urtw_vap *uvp = URTW_VAP(vap);
+
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static void
+urtw_init(struct urtw_softc *sc)
+{
+ usb_error_t error;
+ int ret;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ if (sc->sc_flags & URTW_RUNNING)
+ urtw_stop(sc);
+
+ error = (sc->sc_flags & URTW_RTL8187B) ? urtw_adapter_start_b(sc) :
+ urtw_adapter_start(sc);
+ if (error != 0)
+ goto fail;
+
+ /* reset softc variables */
+ sc->sc_txtimer = 0;
+
+ if (!(sc->sc_flags & URTW_INIT_ONCE)) {
+ ret = urtw_alloc_rx_data_list(sc);
+ if (ret != 0)
+ goto fail;
+ ret = urtw_alloc_tx_data_list(sc);
+ if (ret != 0)
+ goto fail;
+ sc->sc_flags |= URTW_INIT_ONCE;
+ }
+
+ error = urtw_rx_enable(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_tx_enable(sc);
+ if (error != 0)
+ goto fail;
+
+ if (sc->sc_flags & URTW_RTL8187B)
+ usbd_transfer_start(sc->sc_xfer[URTW_8187B_BULK_TX_STATUS]);
+
+ sc->sc_flags |= URTW_RUNNING;
+
+ callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc);
+fail:
+ return;
+}
+
+static usb_error_t
+urtw_adapter_start_b(struct urtw_softc *sc)
+{
+ uint8_t data8;
+ usb_error_t error;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+
+ urtw_read8_m(sc, URTW_CONFIG3, &data8);
+ urtw_write8_m(sc, URTW_CONFIG3,
+ data8 | URTW_CONFIG3_ANAPARAM_WRITE | URTW_CONFIG3_GNT_SELECT);
+ urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8187B_8225_ANAPARAM2_ON);
+ urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_ON);
+ urtw_write8_m(sc, URTW_ANAPARAM3, URTW_8187B_8225_ANAPARAM3_ON);
+
+ urtw_write8_m(sc, 0x61, 0x10);
+ urtw_read8_m(sc, 0x62, &data8);
+ urtw_write8_m(sc, 0x62, data8 & ~(1 << 5));
+ urtw_write8_m(sc, 0x62, data8 | (1 << 5));
+
+ urtw_read8_m(sc, URTW_CONFIG3, &data8);
+ data8 &= ~URTW_CONFIG3_ANAPARAM_WRITE;
+ urtw_write8_m(sc, URTW_CONFIG3, data8);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ error = urtw_8187b_cmd_reset(sc);
+ if (error)
+ goto fail;
+
+ error = sc->sc_rf_init(sc);
+ if (error != 0)
+ goto fail;
+ urtw_write8_m(sc, URTW_CMD, URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE);
+
+ /* fix RTL8187B RX stall */
+ error = urtw_intr_enable(sc);
+ if (error)
+ goto fail;
+
+ error = urtw_write8e(sc, 0x41, 0xf4);
+ if (error)
+ goto fail;
+ error = urtw_write8e(sc, 0x40, 0x00);
+ if (error)
+ goto fail;
+ error = urtw_write8e(sc, 0x42, 0x00);
+ if (error)
+ goto fail;
+ error = urtw_write8e(sc, 0x42, 0x01);
+ if (error)
+ goto fail;
+ error = urtw_write8e(sc, 0x40, 0x0f);
+ if (error)
+ goto fail;
+ error = urtw_write8e(sc, 0x42, 0x00);
+ if (error)
+ goto fail;
+ error = urtw_write8e(sc, 0x42, 0x01);
+ if (error)
+ goto fail;
+
+ urtw_read8_m(sc, 0xdb, &data8);
+ urtw_write8_m(sc, 0xdb, data8 | (1 << 2));
+ urtw_write16_m(sc, 0x372, 0x59fa);
+ urtw_write16_m(sc, 0x374, 0x59d2);
+ urtw_write16_m(sc, 0x376, 0x59d2);
+ urtw_write16_m(sc, 0x378, 0x19fa);
+ urtw_write16_m(sc, 0x37a, 0x19fa);
+ urtw_write16_m(sc, 0x37c, 0x00d0);
+ urtw_write8_m(sc, 0x61, 0);
+
+ urtw_write8_m(sc, 0x180, 0x0f);
+ urtw_write8_m(sc, 0x183, 0x03);
+ urtw_write8_m(sc, 0xda, 0x10);
+ urtw_write8_m(sc, 0x24d, 0x08);
+ urtw_write32_m(sc, URTW_HSSI_PARA, 0x0600321b);
+
+ urtw_write16_m(sc, 0x1ec, 0x800); /* RX MAX SIZE */
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_adapter_start(struct urtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ usb_error_t error;
+
+ error = urtw_reset(sc);
+ if (error)
+ goto fail;
+
+ urtw_write8_m(sc, URTW_ADDR_MAGIC1, 0);
+ urtw_write8_m(sc, URTW_GPIO, 0);
+
+ /* for led */
+ urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4);
+ error = urtw_led_ctl(sc, URTW_LED_CTL_POWER_ON);
+ if (error != 0)
+ goto fail;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+ /* applying MAC address again. */
+ urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)ic->ic_macaddr)[0]);
+ urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)ic->ic_macaddr)[1] & 0xffff);
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ error = urtw_update_msr(sc);
+ if (error)
+ goto fail;
+
+ urtw_write32_m(sc, URTW_INT_TIMEOUT, 0);
+ urtw_write8_m(sc, URTW_WPA_CONFIG, 0);
+ urtw_write8_m(sc, URTW_RATE_FALLBACK, URTW_RATE_FALLBACK_ENABLE | 0x1);
+ error = urtw_set_rate(sc);
+ if (error != 0)
+ goto fail;
+
+ error = sc->sc_rf_init(sc);
+ if (error != 0)
+ goto fail;
+ if (sc->sc_rf_set_sens != NULL)
+ sc->sc_rf_set_sens(sc, sc->sc_sens);
+
+ /* XXX correct? to call write16 */
+ urtw_write16_m(sc, URTW_PSR, 1);
+ urtw_write16_m(sc, URTW_ADDR_MAGIC2, 0x10);
+ urtw_write8_m(sc, URTW_TALLY_SEL, 0x80);
+ urtw_write8_m(sc, URTW_ADDR_MAGIC3, 0x60);
+ /* XXX correct? to call write16 */
+ urtw_write16_m(sc, URTW_PSR, 0);
+ urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4);
+
+ error = urtw_intr_enable(sc);
+ if (error != 0)
+ goto fail;
+
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_set_mode(struct urtw_softc *sc, uint32_t mode)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ data = (data & ~URTW_EPROM_CMD_MASK) | (mode << URTW_EPROM_CMD_SHIFT);
+ data = data & ~(URTW_EPROM_CS | URTW_EPROM_CK);
+ urtw_write8_m(sc, URTW_EPROM_CMD, data);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8187b_cmd_reset(struct urtw_softc *sc)
+{
+ int i;
+ uint8_t data8;
+ usb_error_t error;
+
+ /* XXX the code can be duplicate with urtw_reset(). */
+ urtw_read8_m(sc, URTW_CMD, &data8);
+ data8 = (data8 & 0x2) | URTW_CMD_RST;
+ urtw_write8_m(sc, URTW_CMD, data8);
+
+ for (i = 0; i < 20; i++) {
+ usb_pause_mtx(&sc->sc_mtx, 2);
+ urtw_read8_m(sc, URTW_CMD, &data8);
+ if (!(data8 & URTW_CMD_RST))
+ break;
+ }
+ if (i >= 20) {
+ device_printf(sc->sc_dev, "reset timeout\n");
+ goto fail;
+ }
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_do_request(struct urtw_softc *sc,
+ struct usb_device_request *req, void *data)
+{
+ usb_error_t err;
+ int ntries = 10;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ while (ntries--) {
+ err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+ req, data, 0, NULL, 250 /* ms */);
+ if (err == 0)
+ break;
+
+ DPRINTF(sc, URTW_DEBUG_INIT,
+ "Control request failed, %s (retrying)\n",
+ usbd_errstr(err));
+ usb_pause_mtx(&sc->sc_mtx, hz / 100);
+ }
+ return (err);
+}
+
+static void
+urtw_stop(struct urtw_softc *sc)
+{
+ uint8_t data8;
+ usb_error_t error;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ sc->sc_flags &= ~URTW_RUNNING;
+
+ error = urtw_intr_disable(sc);
+ if (error)
+ goto fail;
+ urtw_read8_m(sc, URTW_CMD, &data8);
+ data8 &= ~(URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE);
+ urtw_write8_m(sc, URTW_CMD, data8);
+
+ error = sc->sc_rf_stop(sc);
+ if (error != 0)
+ goto fail;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+ urtw_read8_m(sc, URTW_CONFIG4, &data8);
+ urtw_write8_m(sc, URTW_CONFIG4, data8 | URTW_CONFIG4_VCOOFF);
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+fail:
+ if (error)
+ device_printf(sc->sc_dev, "failed to stop (%s)\n",
+ usbd_errstr(error));
+
+ usb_callout_stop(&sc->sc_led_ch);
+ callout_stop(&sc->sc_watchdog_ch);
+
+ urtw_abort_xfers(sc);
+}
+
+static void
+urtw_abort_xfers(struct urtw_softc *sc)
+{
+ int i, max;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ max = (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS :
+ URTW_8187L_N_XFERS;
+
+ /* abort any pending transfers */
+ for (i = 0; i < max; i++)
+ usbd_transfer_stop(sc->sc_xfer[i]);
+}
+
+static void
+urtw_parent(struct ieee80211com *ic)
+{
+ struct urtw_softc *sc = ic->ic_softc;
+ int startall = 0;
+
+ URTW_LOCK(sc);
+ if (sc->sc_flags & URTW_DETACHED) {
+ URTW_UNLOCK(sc);
+ return;
+ }
+
+ if (ic->ic_nrunning > 0) {
+ if (sc->sc_flags & URTW_RUNNING) {
+ if (ic->ic_promisc > 0 || ic->ic_allmulti > 0)
+ urtw_set_multi(sc);
+ } else {
+ urtw_init(sc);
+ startall = 1;
+ }
+ } else if (sc->sc_flags & URTW_RUNNING)
+ urtw_stop(sc);
+ URTW_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+}
+
+static int
+urtw_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct urtw_softc *sc = ic->ic_softc;
+ int error;
+
+ URTW_LOCK(sc);
+ if ((sc->sc_flags & URTW_RUNNING) == 0) {
+ URTW_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ URTW_UNLOCK(sc);
+ return (error);
+ }
+ urtw_start(sc);
+ URTW_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+urtw_start(struct urtw_softc *sc)
+{
+ struct urtw_data *bf;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ if ((sc->sc_flags & URTW_RUNNING) == 0)
+ return;
+
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ bf = urtw_getbuf(sc);
+ if (bf == NULL) {
+ mbufq_prepend(&sc->sc_snd, m);
+ break;
+ }
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+
+ if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_NORMAL) != 0) {
+ if_inc_counter(ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
+ ieee80211_free_node(ni);
+ break;
+ }
+
+ sc->sc_txtimer = 5;
+ callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc);
+ }
+}
+
+static int
+urtw_alloc_data_list(struct urtw_softc *sc, struct urtw_data data[],
+ int ndata, int maxsz, void *dma_buf)
+{
+ int i, error;
+
+ for (i = 0; i < ndata; i++) {
+ struct urtw_data *dp = &data[i];
+
+ dp->sc = sc;
+ if (dma_buf == NULL) {
+ dp->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (dp->m == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate rx mbuf\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ dp->buf = mtod(dp->m, uint8_t *);
+ } else {
+ dp->m = NULL;
+ dp->buf = ((uint8_t *)dma_buf) +
+ (i * maxsz);
+ }
+ dp->ni = NULL;
+ }
+ return (0);
+
+fail: urtw_free_data_list(sc, data, ndata, 1);
+ return (error);
+}
+
+static int
+urtw_alloc_rx_data_list(struct urtw_softc *sc)
+{
+ int error, i;
+
+ error = urtw_alloc_data_list(sc,
+ sc->sc_rx, URTW_RX_DATA_LIST_COUNT,
+ MCLBYTES, NULL /* mbufs */);
+ if (error != 0)
+ return (error);
+
+ STAILQ_INIT(&sc->sc_rx_active);
+ STAILQ_INIT(&sc->sc_rx_inactive);
+
+ for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++)
+ STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next);
+
+ return (0);
+}
+
+static int
+urtw_alloc_tx_data_list(struct urtw_softc *sc)
+{
+ int error, i;
+
+ error = urtw_alloc_data_list(sc,
+ sc->sc_tx, URTW_TX_DATA_LIST_COUNT, URTW_TX_MAXSIZE,
+ sc->sc_tx_dma_buf /* no mbufs */);
+ if (error != 0)
+ return (error);
+
+ STAILQ_INIT(&sc->sc_tx_active);
+ STAILQ_INIT(&sc->sc_tx_inactive);
+ STAILQ_INIT(&sc->sc_tx_pending);
+
+ for (i = 0; i < URTW_TX_DATA_LIST_COUNT; i++)
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i],
+ next);
+
+ return (0);
+}
+
+static int
+urtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct urtw_softc *sc = ic->ic_softc;
+ struct urtw_data *bf;
+
+ /* prevent management frames from being sent if we're not ready */
+ if (!(sc->sc_flags & URTW_RUNNING)) {
+ m_freem(m);
+ return ENETDOWN;
+ }
+ URTW_LOCK(sc);
+ bf = urtw_getbuf(sc);
+ if (bf == NULL) {
+ m_freem(m);
+ URTW_UNLOCK(sc);
+ return (ENOBUFS); /* XXX */
+ }
+
+ if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_LOW) != 0) {
+ STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
+ URTW_UNLOCK(sc);
+ return (EIO);
+ }
+ URTW_UNLOCK(sc);
+
+ sc->sc_txtimer = 5;
+ return (0);
+}
+
+static void
+urtw_scan_start(struct ieee80211com *ic)
+{
+
+ /* XXX do nothing? */
+}
+
+static void
+urtw_scan_end(struct ieee80211com *ic)
+{
+
+ /* XXX do nothing? */
+}
+
+static void
+urtw_getradiocaps(struct ieee80211com *ic,
+ int maxchans, int *nchans, struct ieee80211_channel chans[])
+{
+ uint8_t bands[IEEE80211_MODE_BYTES];
+
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ ieee80211_add_channel_list_2ghz(chans, maxchans, nchans,
+ urtw_chan_2ghz, nitems(urtw_chan_2ghz), bands, 0);
+}
+
+static void
+urtw_set_channel(struct ieee80211com *ic)
+{
+ struct urtw_softc *sc = ic->ic_softc;
+ uint32_t data, orig;
+ usb_error_t error;
+
+ /*
+ * if the user set a channel explicitly using ifconfig(8) this function
+ * can be called earlier than we're expected that in some cases the
+ * initialization would be failed if setting a channel is called before
+ * the init have done.
+ */
+ if (!(sc->sc_flags & URTW_RUNNING))
+ return;
+
+ if (sc->sc_curchan != NULL && sc->sc_curchan == ic->ic_curchan)
+ return;
+
+ URTW_LOCK(sc);
+
+ /*
+ * during changing th channel we need to temporarily be disable
+ * TX.
+ */
+ urtw_read32_m(sc, URTW_TX_CONF, &orig);
+ data = orig & ~URTW_TX_LOOPBACK_MASK;
+ urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_MAC);
+
+ error = sc->sc_rf_set_chan(sc, ieee80211_chan2ieee(ic, ic->ic_curchan));
+ if (error != 0)
+ goto fail;
+ usb_pause_mtx(&sc->sc_mtx, 10);
+ urtw_write32_m(sc, URTW_TX_CONF, orig);
+
+ urtw_write16_m(sc, URTW_ATIM_WND, 2);
+ urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100);
+ urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100);
+ urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100);
+
+fail:
+ URTW_UNLOCK(sc);
+
+ sc->sc_curchan = ic->ic_curchan;
+
+ if (error != 0)
+ device_printf(sc->sc_dev, "could not change the channel\n");
+}
+
+static void
+urtw_update_mcast(struct ieee80211com *ic)
+{
+
+ /* XXX do nothing? */
+}
+
+static int
+urtw_tx_start(struct urtw_softc *sc, struct ieee80211_node *ni, struct mbuf *m0,
+ struct urtw_data *data, int prior)
+{
+ struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *);
+ struct ieee80211_key *k;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct usb_xfer *rtl8187b_pipes[URTW_8187B_TXPIPE_MAX] = {
+ sc->sc_xfer[URTW_8187B_BULK_TX_BE],
+ sc->sc_xfer[URTW_8187B_BULK_TX_BK],
+ sc->sc_xfer[URTW_8187B_BULK_TX_VI],
+ sc->sc_xfer[URTW_8187B_BULK_TX_VO]
+ };
+ struct usb_xfer *xfer;
+ int dur = 0, rtsdur = 0, rtsenable = 0, ctsenable = 0, rate,
+ pkttime = 0, txdur = 0, isshort = 0, xferlen;
+ uint16_t acktime, rtstime, ctstime;
+ uint32_t flags;
+ usb_error_t error;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ /*
+ * Software crypto.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ device_printf(sc->sc_dev,
+ "ieee80211_crypto_encap returns NULL.\n");
+ /* XXX we don't expect the fragmented frames */
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+
+ /* in case packet header moved, reset pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct urtw_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ /* XXX Are variables correct? */
+ tap->wt_flags = 0;
+ tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+ ieee80211_radiotap_tx(vap, m0);
+ }
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT ||
+ (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+ rate = tp->mgmtrate;
+ } else {
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ /* for data frames */
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else
+ rate = urtw_rtl2rate(sc->sc_currate);
+ }
+
+ sc->sc_stats.txrates[sc->sc_currate]++;
+
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ txdur = pkttime = urtw_compute_txtime(m0->m_pkthdr.len +
+ IEEE80211_CRC_LEN, rate, 0, 0);
+ else {
+ acktime = urtw_compute_txtime(14, 2,0, 0);
+ if ((m0->m_pkthdr.len + 4) > vap->iv_rtsthreshold) {
+ rtsenable = 1;
+ ctsenable = 0;
+ rtstime = urtw_compute_txtime(URTW_ACKCTS_LEN, 2, 0, 0);
+ ctstime = urtw_compute_txtime(14, 2, 0, 0);
+ pkttime = urtw_compute_txtime(m0->m_pkthdr.len +
+ IEEE80211_CRC_LEN, rate, 0, isshort);
+ rtsdur = ctstime + pkttime + acktime +
+ 3 * URTW_ASIFS_TIME;
+ txdur = rtstime + rtsdur;
+ } else {
+ rtsenable = ctsenable = rtsdur = 0;
+ pkttime = urtw_compute_txtime(m0->m_pkthdr.len +
+ IEEE80211_CRC_LEN, rate, 0, isshort);
+ txdur = pkttime + URTW_ASIFS_TIME + acktime;
+ }
+
+ if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
+ dur = urtw_compute_txtime(m0->m_pkthdr.len +
+ IEEE80211_CRC_LEN, rate, 0, isshort) +
+ 3 * URTW_ASIFS_TIME +
+ 2 * acktime;
+ else
+ dur = URTW_ASIFS_TIME + acktime;
+ }
+ USETW(wh->i_dur, dur);
+
+ xferlen = m0->m_pkthdr.len;
+ xferlen += (sc->sc_flags & URTW_RTL8187B) ? (4 * 8) : (4 * 3);
+ if ((0 == xferlen % 64) || (0 == xferlen % 512))
+ xferlen += 1;
+
+ memset(data->buf, 0, URTW_TX_MAXSIZE);
+ flags = m0->m_pkthdr.len & 0xfff;
+ flags |= URTW_TX_FLAG_NO_ENC;
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) &&
+ (sc->sc_preamble_mode == URTW_PREAMBLE_MODE_SHORT) &&
+ (sc->sc_currate != 0))
+ flags |= URTW_TX_FLAG_SPLCP;
+ if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
+ flags |= URTW_TX_FLAG_MOREFRAG;
+
+ flags |= (sc->sc_currate & 0xf) << URTW_TX_FLAG_TXRATE_SHIFT;
+
+ if (sc->sc_flags & URTW_RTL8187B) {
+ struct urtw_8187b_txhdr *tx;
+
+ tx = (struct urtw_8187b_txhdr *)data->buf;
+ if (ctsenable)
+ flags |= URTW_TX_FLAG_CTS;
+ if (rtsenable) {
+ flags |= URTW_TX_FLAG_RTS;
+ flags |= (urtw_rate2rtl(11) & 0xf) <<
+ URTW_TX_FLAG_RTSRATE_SHIFT;
+ tx->rtsdur = rtsdur;
+ }
+ tx->flag = htole32(flags);
+ tx->txdur = txdur;
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_MGT &&
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ tx->retry = 1;
+ else
+ tx->retry = URTW_TX_MAXRETRY;
+ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1));
+ } else {
+ struct urtw_8187l_txhdr *tx;
+
+ tx = (struct urtw_8187l_txhdr *)data->buf;
+ if (rtsenable) {
+ flags |= URTW_TX_FLAG_RTS;
+ tx->rtsdur = rtsdur;
+ }
+ flags |= (urtw_rate2rtl(11) & 0xf) << URTW_TX_FLAG_RTSRATE_SHIFT;
+ tx->flag = htole32(flags);
+ tx->retry = 3; /* CW minimum */
+ tx->retry |= 7 << 4; /* CW maximum */
+ tx->retry |= URTW_TX_MAXRETRY << 8; /* retry limitation */
+ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1));
+ }
+
+ data->buflen = xferlen;
+ data->ni = ni;
+ data->m = m0;
+
+ if (sc->sc_flags & URTW_RTL8187B) {
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_CTL:
+ case IEEE80211_FC0_TYPE_MGT:
+ xfer = sc->sc_xfer[URTW_8187B_BULK_TX_EP12];
+ break;
+ default:
+ KASSERT(M_WME_GETAC(m0) < URTW_8187B_TXPIPE_MAX,
+ ("unsupported WME pipe %d", M_WME_GETAC(m0)));
+ xfer = rtl8187b_pipes[M_WME_GETAC(m0)];
+ break;
+ }
+ } else
+ xfer = (prior == URTW_PRIORITY_LOW) ?
+ sc->sc_xfer[URTW_8187L_BULK_TX_LOW] :
+ sc->sc_xfer[URTW_8187L_BULK_TX_NORMAL];
+
+ STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next);
+ usbd_transfer_start(xfer);
+
+ error = urtw_led_ctl(sc, URTW_LED_CTL_TX);
+ if (error != 0)
+ device_printf(sc->sc_dev, "could not control LED (%d)\n",
+ error);
+ return (0);
+}
+
+static int
+urtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct urtw_softc *sc = ic->ic_softc;
+ struct urtw_vap *uvp = URTW_VAP(vap);
+ struct ieee80211_node *ni;
+ usb_error_t error = 0;
+
+ DPRINTF(sc, URTW_DEBUG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ sc->sc_state = nstate;
+
+ IEEE80211_UNLOCK(ic);
+ URTW_LOCK(sc);
+ usb_callout_stop(&sc->sc_led_ch);
+ callout_stop(&sc->sc_watchdog_ch);
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ break;
+ case IEEE80211_S_RUN:
+ ni = ieee80211_ref_node(vap->iv_bss);
+ /* setting bssid. */
+ urtw_write32_m(sc, URTW_BSSID, ((uint32_t *)ni->ni_bssid)[0]);
+ urtw_write16_m(sc, URTW_BSSID + 4,
+ ((uint16_t *)ni->ni_bssid)[2]);
+ urtw_update_msr(sc);
+ /* XXX maybe the below would be incorrect. */
+ urtw_write16_m(sc, URTW_ATIM_WND, 2);
+ urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100);
+ urtw_write16_m(sc, URTW_BEACON_INTERVAL, 0x64);
+ urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100);
+ error = urtw_led_ctl(sc, URTW_LED_CTL_LINK);
+ if (error != 0)
+ device_printf(sc->sc_dev,
+ "could not control LED (%d)\n", error);
+ ieee80211_free_node(ni);
+ break;
+ default:
+ break;
+ }
+fail:
+ URTW_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (uvp->newstate(vap, nstate, arg));
+}
+
+static void
+urtw_watchdog(void *arg)
+{
+ struct urtw_softc *sc = arg;
+
+ if (sc->sc_txtimer > 0) {
+ if (--sc->sc_txtimer == 0) {
+ device_printf(sc->sc_dev, "device timeout\n");
+ counter_u64_add(sc->sc_ic.ic_oerrors, 1);
+ return;
+ }
+ callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc);
+ }
+}
+
+static void
+urtw_set_multi(void *arg)
+{
+ /* XXX don't know how to set a device. Lack of docs. */
+}
+
+static usb_error_t
+urtw_set_rate(struct urtw_softc *sc)
+{
+ int i, basic_rate, min_rr_rate, max_rr_rate;
+ uint16_t data;
+ usb_error_t error;
+
+ basic_rate = urtw_rate2rtl(48);
+ min_rr_rate = urtw_rate2rtl(12);
+ max_rr_rate = urtw_rate2rtl(48);
+
+ urtw_write8_m(sc, URTW_RESP_RATE,
+ max_rr_rate << URTW_RESP_MAX_RATE_SHIFT |
+ min_rr_rate << URTW_RESP_MIN_RATE_SHIFT);
+
+ urtw_read16_m(sc, URTW_BRSR, &data);
+ data &= ~URTW_BRSR_MBR_8185;
+
+ for (i = 0; i <= basic_rate; i++)
+ data |= (1 << i);
+
+ urtw_write16_m(sc, URTW_BRSR, data);
+fail:
+ return (error);
+}
+
+static uint16_t
+urtw_rate2rtl(uint32_t rate)
+{
+ unsigned int i;
+
+ for (i = 0; i < nitems(urtw_ratetable); i++) {
+ if (rate == urtw_ratetable[i].reg)
+ return urtw_ratetable[i].val;
+ }
+
+ return (3);
+}
+
+static uint16_t
+urtw_rtl2rate(uint32_t rate)
+{
+ unsigned int i;
+
+ for (i = 0; i < nitems(urtw_ratetable); i++) {
+ if (rate == urtw_ratetable[i].val)
+ return urtw_ratetable[i].reg;
+ }
+
+ return (0);
+}
+
+static usb_error_t
+urtw_update_msr(struct urtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t data;
+ usb_error_t error;
+
+ urtw_read8_m(sc, URTW_MSR, &data);
+ data &= ~URTW_MSR_LINK_MASK;
+
+ if (sc->sc_state == IEEE80211_S_RUN) {
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ case IEEE80211_M_MONITOR:
+ data |= URTW_MSR_LINK_STA;
+ if (sc->sc_flags & URTW_RTL8187B)
+ data |= URTW_MSR_LINK_ENEDCA;
+ break;
+ case IEEE80211_M_IBSS:
+ data |= URTW_MSR_LINK_ADHOC;
+ break;
+ case IEEE80211_M_HOSTAP:
+ data |= URTW_MSR_LINK_HOSTAP;
+ break;
+ default:
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unsupported operation mode 0x%x\n",
+ ic->ic_opmode);
+ error = USB_ERR_INVAL;
+ goto fail;
+ }
+ } else
+ data |= URTW_MSR_LINK_NONE;
+
+ urtw_write8_m(sc, URTW_MSR, data);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_GETREGS_REQ;
+ USETW(req.wValue, (val & 0xff) | 0xff00);
+ USETW(req.wIndex, (val >> 8) & 0x3);
+ USETW(req.wLength, sizeof(uint8_t));
+
+ error = urtw_do_request(sc, &req, data);
+ return (error);
+}
+
+static usb_error_t
+urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_GETREGS_REQ;
+ USETW(req.wValue, (val & 0xff) | 0xff00);
+ USETW(req.wIndex, (val >> 8) & 0x3);
+ USETW(req.wLength, sizeof(uint16_t));
+
+ error = urtw_do_request(sc, &req, data);
+ return (error);
+}
+
+static usb_error_t
+urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_GETREGS_REQ;
+ USETW(req.wValue, (val & 0xff) | 0xff00);
+ USETW(req.wIndex, (val >> 8) & 0x3);
+ USETW(req.wLength, sizeof(uint32_t));
+
+ error = urtw_do_request(sc, &req, data);
+ return (error);
+}
+
+static usb_error_t
+urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data)
+{
+ struct usb_device_request req;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, (val & 0xff) | 0xff00);
+ USETW(req.wIndex, (val >> 8) & 0x3);
+ USETW(req.wLength, sizeof(uint8_t));
+
+ return (urtw_do_request(sc, &req, &data));
+}
+
+static usb_error_t
+urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data)
+{
+ struct usb_device_request req;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, (val & 0xff) | 0xff00);
+ USETW(req.wIndex, (val >> 8) & 0x3);
+ USETW(req.wLength, sizeof(uint16_t));
+
+ return (urtw_do_request(sc, &req, &data));
+}
+
+static usb_error_t
+urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data)
+{
+ struct usb_device_request req;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, (val & 0xff) | 0xff00);
+ USETW(req.wIndex, (val >> 8) & 0x3);
+ USETW(req.wLength, sizeof(uint32_t));
+
+ return (urtw_do_request(sc, &req, &data));
+}
+
+static usb_error_t
+urtw_get_macaddr(struct urtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t data;
+ usb_error_t error;
+
+ error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR, &data);
+ if (error != 0)
+ goto fail;
+ ic->ic_macaddr[0] = data & 0xff;
+ ic->ic_macaddr[1] = (data & 0xff00) >> 8;
+ error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 1, &data);
+ if (error != 0)
+ goto fail;
+ ic->ic_macaddr[2] = data & 0xff;
+ ic->ic_macaddr[3] = (data & 0xff00) >> 8;
+ error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 2, &data);
+ if (error != 0)
+ goto fail;
+ ic->ic_macaddr[4] = data & 0xff;
+ ic->ic_macaddr[5] = (data & 0xff00) >> 8;
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_eprom_read32(struct urtw_softc *sc, uint32_t addr, uint32_t *data)
+{
+#define URTW_READCMD_LEN 3
+ int addrlen, i;
+ int16_t addrstr[8], data16, readcmd[] = { 1, 1, 0 };
+ usb_error_t error;
+
+ /* NB: make sure the buffer is initialized */
+ *data = 0;
+
+ /* enable EPROM programming */
+ urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_PROGRAM_MODE);
+ DELAY(URTW_EPROM_DELAY);
+
+ error = urtw_eprom_cs(sc, URTW_EPROM_ENABLE);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_ck(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_sendbits(sc, readcmd, URTW_READCMD_LEN);
+ if (error != 0)
+ goto fail;
+ if (sc->sc_epromtype == URTW_EEPROM_93C56) {
+ addrlen = 8;
+ addrstr[0] = addr & (1 << 7);
+ addrstr[1] = addr & (1 << 6);
+ addrstr[2] = addr & (1 << 5);
+ addrstr[3] = addr & (1 << 4);
+ addrstr[4] = addr & (1 << 3);
+ addrstr[5] = addr & (1 << 2);
+ addrstr[6] = addr & (1 << 1);
+ addrstr[7] = addr & (1 << 0);
+ } else {
+ addrlen=6;
+ addrstr[0] = addr & (1 << 5);
+ addrstr[1] = addr & (1 << 4);
+ addrstr[2] = addr & (1 << 3);
+ addrstr[3] = addr & (1 << 2);
+ addrstr[4] = addr & (1 << 1);
+ addrstr[5] = addr & (1 << 0);
+ }
+ error = urtw_eprom_sendbits(sc, addrstr, addrlen);
+ if (error != 0)
+ goto fail;
+
+ error = urtw_eprom_writebit(sc, 0);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < 16; i++) {
+ error = urtw_eprom_ck(sc);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_readbit(sc, &data16);
+ if (error != 0)
+ goto fail;
+
+ (*data) |= (data16 << (15 - i));
+ }
+
+ error = urtw_eprom_cs(sc, URTW_EPROM_DISABLE);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_ck(sc);
+ if (error != 0)
+ goto fail;
+
+ /* now disable EPROM programming */
+ urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_NORMAL_MODE);
+fail:
+ return (error);
+#undef URTW_READCMD_LEN
+}
+
+static usb_error_t
+urtw_eprom_cs(struct urtw_softc *sc, int able)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ if (able == URTW_EPROM_ENABLE)
+ urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CS);
+ else
+ urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CS);
+ DELAY(URTW_EPROM_DELAY);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_eprom_ck(struct urtw_softc *sc)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ /* masking */
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CK);
+ DELAY(URTW_EPROM_DELAY);
+ /* unmasking */
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CK);
+ DELAY(URTW_EPROM_DELAY);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data)
+{
+ uint8_t data8;
+ usb_error_t error;
+
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data8);
+ *data = (data8 & URTW_EPROM_READBIT) ? 1 : 0;
+ DELAY(URTW_EPROM_DELAY);
+
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ urtw_read8_m(sc, URTW_EPROM_CMD, &data);
+ if (bit != 0)
+ urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_WRITEBIT);
+ else
+ urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_WRITEBIT);
+ DELAY(URTW_EPROM_DELAY);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen)
+{
+ int i = 0;
+ usb_error_t error = 0;
+
+ for (i = 0; i < buflen; i++) {
+ error = urtw_eprom_writebit(sc, buf[i]);
+ if (error != 0)
+ goto fail;
+ error = urtw_eprom_ck(sc);
+ if (error != 0)
+ goto fail;
+ }
+fail:
+ return (error);
+}
+
+
+static usb_error_t
+urtw_get_txpwr(struct urtw_softc *sc)
+{
+ int i, j;
+ uint32_t data;
+ usb_error_t error;
+
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW_BASE, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck_base = data & 0xf;
+ sc->sc_txpwr_ofdm_base = (data >> 4) & 0xf;
+
+ for (i = 1, j = 0; i < 6; i += 2, j++) {
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW0 + j, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[i] = data & 0xf;
+ sc->sc_txpwr_cck[i + 1] = (data & 0xf00) >> 8;
+ sc->sc_txpwr_ofdm[i] = (data & 0xf0) >> 4;
+ sc->sc_txpwr_ofdm[i + 1] = (data & 0xf000) >> 12;
+ }
+ for (i = 1, j = 0; i < 4; i += 2, j++) {
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW1 + j, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[i + 6] = data & 0xf;
+ sc->sc_txpwr_cck[i + 6 + 1] = (data & 0xf00) >> 8;
+ sc->sc_txpwr_ofdm[i + 6] = (data & 0xf0) >> 4;
+ sc->sc_txpwr_ofdm[i + 6 + 1] = (data & 0xf000) >> 12;
+ }
+ if (sc->sc_flags & URTW_RTL8187B) {
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[1 + 6 + 4] = data & 0xf;
+ sc->sc_txpwr_ofdm[1 + 6 + 4] = (data & 0xf0) >> 4;
+ error = urtw_eprom_read32(sc, 0x0a, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[2 + 6 + 4] = data & 0xf;
+ sc->sc_txpwr_ofdm[2 + 6 + 4] = (data & 0xf0) >> 4;
+ error = urtw_eprom_read32(sc, 0x1c, &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[3 + 6 + 4] = data & 0xf;
+ sc->sc_txpwr_cck[3 + 6 + 4 + 1] = (data & 0xf00) >> 8;
+ sc->sc_txpwr_ofdm[3 + 6 + 4] = (data & 0xf0) >> 4;
+ sc->sc_txpwr_ofdm[3 + 6 + 4 + 1] = (data & 0xf000) >> 12;
+ } else {
+ for (i = 1, j = 0; i < 4; i += 2, j++) {
+ error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2 + j,
+ &data);
+ if (error != 0)
+ goto fail;
+ sc->sc_txpwr_cck[i + 6 + 4] = data & 0xf;
+ sc->sc_txpwr_cck[i + 6 + 4 + 1] = (data & 0xf00) >> 8;
+ sc->sc_txpwr_ofdm[i + 6 + 4] = (data & 0xf0) >> 4;
+ sc->sc_txpwr_ofdm[i + 6 + 4 + 1] = (data & 0xf000) >> 12;
+ }
+ }
+fail:
+ return (error);
+}
+
+
+static usb_error_t
+urtw_get_rfchip(struct urtw_softc *sc)
+{
+ int ret;
+ uint8_t data8;
+ uint32_t data;
+ usb_error_t error;
+
+ if (sc->sc_flags & URTW_RTL8187B) {
+ urtw_read8_m(sc, 0xe1, &data8);
+ switch (data8) {
+ case 0:
+ sc->sc_flags |= URTW_RTL8187B_REV_B;
+ break;
+ case 1:
+ sc->sc_flags |= URTW_RTL8187B_REV_D;
+ break;
+ case 2:
+ sc->sc_flags |= URTW_RTL8187B_REV_E;
+ break;
+ default:
+ device_printf(sc->sc_dev, "unknown type: %#x\n", data8);
+ sc->sc_flags |= URTW_RTL8187B_REV_B;
+ break;
+ }
+ } else {
+ urtw_read32_m(sc, URTW_TX_CONF, &data);
+ switch (data & URTW_TX_HWMASK) {
+ case URTW_TX_R8187vD_B:
+ sc->sc_flags |= URTW_RTL8187B;
+ break;
+ case URTW_TX_R8187vD:
+ break;
+ default:
+ device_printf(sc->sc_dev, "unknown RTL8187L type: %#x\n",
+ data & URTW_TX_HWMASK);
+ break;
+ }
+ }
+
+ error = urtw_eprom_read32(sc, URTW_EPROM_RFCHIPID, &data);
+ if (error != 0)
+ goto fail;
+ switch (data & 0xff) {
+ case URTW_EPROM_RFCHIPID_RTL8225U:
+ error = urtw_8225_isv2(sc, &ret);
+ if (error != 0)
+ goto fail;
+ if (ret == 0) {
+ sc->sc_rf_init = urtw_8225_rf_init;
+ sc->sc_rf_set_sens = urtw_8225_rf_set_sens;
+ sc->sc_rf_set_chan = urtw_8225_rf_set_chan;
+ sc->sc_rf_stop = urtw_8225_rf_stop;
+ } else {
+ sc->sc_rf_init = urtw_8225v2_rf_init;
+ sc->sc_rf_set_chan = urtw_8225v2_rf_set_chan;
+ sc->sc_rf_stop = urtw_8225_rf_stop;
+ }
+ sc->sc_max_sens = URTW_8225_RF_MAX_SENS;
+ sc->sc_sens = URTW_8225_RF_DEF_SENS;
+ break;
+ case URTW_EPROM_RFCHIPID_RTL8225Z2:
+ sc->sc_rf_init = urtw_8225v2b_rf_init;
+ sc->sc_rf_set_chan = urtw_8225v2b_rf_set_chan;
+ sc->sc_max_sens = URTW_8225_RF_MAX_SENS;
+ sc->sc_sens = URTW_8225_RF_DEF_SENS;
+ sc->sc_rf_stop = urtw_8225_rf_stop;
+ break;
+ default:
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unsupported RF chip %d\n", data & 0xff);
+ error = USB_ERR_INVAL;
+ goto fail;
+ }
+
+ device_printf(sc->sc_dev, "%s rf %s hwrev %s\n",
+ (sc->sc_flags & URTW_RTL8187B) ? "rtl8187b" : "rtl8187l",
+ ((data & 0xff) == URTW_EPROM_RFCHIPID_RTL8225U) ? "rtl8225u" :
+ "rtl8225z2",
+ (sc->sc_flags & URTW_RTL8187B) ? ((data8 == 0) ? "b" :
+ (data8 == 1) ? "d" : "e") : "none");
+
+fail:
+ return (error);
+}
+
+
+static usb_error_t
+urtw_led_init(struct urtw_softc *sc)
+{
+ uint32_t rev;
+ usb_error_t error;
+
+ urtw_read8_m(sc, URTW_PSR, &sc->sc_psr);
+ error = urtw_eprom_read32(sc, URTW_EPROM_SWREV, &rev);
+ if (error != 0)
+ goto fail;
+
+ switch (rev & URTW_EPROM_CID_MASK) {
+ case URTW_EPROM_CID_ALPHA0:
+ sc->sc_strategy = URTW_SW_LED_MODE1;
+ break;
+ case URTW_EPROM_CID_SERCOMM_PS:
+ sc->sc_strategy = URTW_SW_LED_MODE3;
+ break;
+ case URTW_EPROM_CID_HW_LED:
+ sc->sc_strategy = URTW_HW_LED;
+ break;
+ case URTW_EPROM_CID_RSVD0:
+ case URTW_EPROM_CID_RSVD1:
+ default:
+ sc->sc_strategy = URTW_SW_LED_MODE0;
+ break;
+ }
+
+ sc->sc_gpio_ledpin = URTW_LED_PIN_GPIO0;
+
+fail:
+ return (error);
+}
+
+
+static usb_error_t
+urtw_8225_rf_init(struct urtw_softc *sc)
+{
+ unsigned int i;
+ uint16_t data;
+ usb_error_t error;
+
+ error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
+ if (error)
+ goto fail;
+
+ error = urtw_8225_usb_init(sc);
+ if (error)
+ goto fail;
+
+ urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008);
+ urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */
+ urtw_write16_m(sc, URTW_BRSR, 0xffff);
+ urtw_write32_m(sc, URTW_RF_PARA, 0x100044);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+ urtw_write8_m(sc, URTW_CONFIG3, 0x44);
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ error = urtw_8185_rf_pins_enable(sc);
+ if (error)
+ goto fail;
+ usb_pause_mtx(&sc->sc_mtx, 1000);
+
+ for (i = 0; i < nitems(urtw_8225_rf_part1); i++) {
+ urtw_8225_write(sc, urtw_8225_rf_part1[i].reg,
+ urtw_8225_rf_part1[i].val);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ }
+ usb_pause_mtx(&sc->sc_mtx, 100);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
+ usb_pause_mtx(&sc->sc_mtx, 200);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
+ usb_pause_mtx(&sc->sc_mtx, 200);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC3);
+
+ for (i = 0; i < 95; i++) {
+ urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1));
+ urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225_rxgain[i]);
+ }
+
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC4);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC5);
+
+ for (i = 0; i < 128; i++) {
+ urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ }
+
+ for (i = 0; i < nitems(urtw_8225_rf_part2); i++) {
+ urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg,
+ urtw_8225_rf_part2[i].val);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ }
+
+ error = urtw_8225_setgain(sc, 4);
+ if (error)
+ goto fail;
+
+ for (i = 0; i < nitems(urtw_8225_rf_part3); i++) {
+ urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg,
+ urtw_8225_rf_part3[i].val);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ }
+
+ urtw_write8_m(sc, URTW_TESTR, 0x0d);
+
+ error = urtw_8225_set_txpwrlvl(sc, 1);
+ if (error)
+ goto fail;
+
+ urtw_8187_write_phy_cck(sc, 0x10, 0x9b);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8187_write_phy_ofdm(sc, 0x26, 0x90);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+
+ /* TX ant A, 0x0 for B */
+ error = urtw_8185_tx_antenna(sc, 0x3);
+ if (error)
+ goto fail;
+ urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002);
+
+ error = urtw_8225_rf_set_chan(sc, 1);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8185_rf_pins_enable(struct urtw_softc *sc)
+{
+ usb_error_t error = 0;
+
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant)
+{
+ usb_error_t error;
+
+ urtw_write8_m(sc, URTW_TX_ANTENNA, ant);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8187_write_phy_ofdm_c(struct urtw_softc *sc, uint8_t addr, uint32_t data)
+{
+
+ data = data & 0xff;
+ return urtw_8187_write_phy(sc, addr, data);
+}
+
+static usb_error_t
+urtw_8187_write_phy_cck_c(struct urtw_softc *sc, uint8_t addr, uint32_t data)
+{
+
+ data = data & 0xff;
+ return urtw_8187_write_phy(sc, addr, data | 0x10000);
+}
+
+static usb_error_t
+urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data)
+{
+ uint32_t phyw;
+ usb_error_t error;
+
+ phyw = ((data << 8) | (addr | 0x80));
+ urtw_write8_m(sc, URTW_PHY_MAGIC4, ((phyw & 0xff000000) >> 24));
+ urtw_write8_m(sc, URTW_PHY_MAGIC3, ((phyw & 0x00ff0000) >> 16));
+ urtw_write8_m(sc, URTW_PHY_MAGIC2, ((phyw & 0x0000ff00) >> 8));
+ urtw_write8_m(sc, URTW_PHY_MAGIC1, ((phyw & 0x000000ff)));
+ usb_pause_mtx(&sc->sc_mtx, 1);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_setgain(struct urtw_softc *sc, int16_t gain)
+{
+ usb_error_t error;
+
+ urtw_8187_write_phy_ofdm(sc, 0x0d, urtw_8225_gain[gain * 4]);
+ urtw_8187_write_phy_ofdm(sc, 0x1b, urtw_8225_gain[gain * 4 + 2]);
+ urtw_8187_write_phy_ofdm(sc, 0x1d, urtw_8225_gain[gain * 4 + 3]);
+ urtw_8187_write_phy_ofdm(sc, 0x23, urtw_8225_gain[gain * 4 + 1]);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_usb_init(struct urtw_softc *sc)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 0);
+ urtw_write8_m(sc, URTW_GPIO, 0);
+ error = urtw_read8e(sc, 0x53, &data);
+ if (error)
+ goto fail;
+ error = urtw_write8e(sc, 0x53, data | (1 << 7));
+ if (error)
+ goto fail;
+ urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 4);
+ urtw_write8_m(sc, URTW_GPIO, 0x20);
+ urtw_write8_m(sc, URTW_GP_ENABLE, 0);
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x80);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x80);
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x80);
+
+ usb_pause_mtx(&sc->sc_mtx, 500);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data)
+{
+ uint16_t d80, d82, d84;
+ usb_error_t error;
+
+ urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &d80);
+ d80 &= URTW_RF_PINS_MAGIC1;
+ urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &d82);
+ urtw_read16_m(sc, URTW_RF_PINS_SELECT, &d84);
+ d84 &= URTW_RF_PINS_MAGIC2;
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, d82 | URTW_RF_PINS_MAGIC3);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84 | URTW_RF_PINS_MAGIC3);
+ DELAY(10);
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80);
+ DELAY(10);
+
+ error = urtw_8225_write_s16(sc, addr, 0x8225, &data);
+ if (error != 0)
+ goto fail;
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
+ DELAY(10);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84);
+ usb_pause_mtx(&sc->sc_mtx, 2);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index,
+ uint16_t *data)
+{
+ uint8_t buf[2];
+ uint16_t data16;
+ struct usb_device_request req;
+ usb_error_t error = 0;
+
+ data16 = *data;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, sizeof(uint16_t));
+ buf[0] = (data16 & 0x00ff);
+ buf[1] = (data16 & 0xff00) >> 8;
+
+ error = urtw_do_request(sc, &req, buf);
+
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan)
+{
+ usb_error_t error;
+
+ error = urtw_8225_set_txpwrlvl(sc, chan);
+ if (error)
+ goto fail;
+ urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]);
+ usb_pause_mtx(&sc->sc_mtx, 10);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens)
+{
+ usb_error_t error;
+
+ if (sens < 0 || sens > 6)
+ return -1;
+
+ if (sens > 4)
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC1);
+ else
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC2);
+
+ sens = 6 - sens;
+ error = urtw_8225_setgain(sc, sens);
+ if (error)
+ goto fail;
+
+ urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[sens]);
+
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_set_txpwrlvl(struct urtw_softc *sc, int chan)
+{
+ int i, idx, set;
+ uint8_t *cck_pwltable;
+ uint8_t cck_pwrlvl_max, ofdm_pwrlvl_min, ofdm_pwrlvl_max;
+ uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff;
+ uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff;
+ usb_error_t error;
+
+ cck_pwrlvl_max = 11;
+ ofdm_pwrlvl_max = 25; /* 12 -> 25 */
+ ofdm_pwrlvl_min = 10;
+
+ /* CCK power setting */
+ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl;
+ idx = cck_pwrlvl % 6;
+ set = cck_pwrlvl / 6;
+ cck_pwltable = (chan == 14) ? urtw_8225_txpwr_cck_ch14 :
+ urtw_8225_txpwr_cck;
+
+ urtw_write8_m(sc, URTW_TX_GAIN_CCK,
+ urtw_8225_tx_gain_cck_ofdm[set] >> 1);
+ for (i = 0; i < 8; i++) {
+ urtw_8187_write_phy_cck(sc, 0x44 + i,
+ cck_pwltable[idx * 8 + i]);
+ }
+ usb_pause_mtx(&sc->sc_mtx, 1);
+
+ /* OFDM power setting */
+ ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ?
+ ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min;
+ ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl;
+
+ idx = ofdm_pwrlvl % 6;
+ set = ofdm_pwrlvl / 6;
+
+ error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
+ if (error)
+ goto fail;
+ urtw_8187_write_phy_ofdm(sc, 2, 0x42);
+ urtw_8187_write_phy_ofdm(sc, 6, 0);
+ urtw_8187_write_phy_ofdm(sc, 8, 0);
+
+ urtw_write8_m(sc, URTW_TX_GAIN_OFDM,
+ urtw_8225_tx_gain_cck_ofdm[set] >> 1);
+ urtw_8187_write_phy_ofdm(sc, 0x5, urtw_8225_txpwr_ofdm[idx]);
+ urtw_8187_write_phy_ofdm(sc, 0x7, urtw_8225_txpwr_ofdm[idx]);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+fail:
+ return (error);
+}
+
+
+static usb_error_t
+urtw_8225_rf_stop(struct urtw_softc *sc)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ urtw_8225_write(sc, 0x4, 0x1f);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE);
+ if (sc->sc_flags & URTW_RTL8187B) {
+ urtw_write32_m(sc, URTW_ANAPARAM2,
+ URTW_8187B_8225_ANAPARAM2_OFF);
+ urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_OFF);
+ urtw_write32_m(sc, URTW_ANAPARAM3,
+ URTW_8187B_8225_ANAPARAM3_OFF);
+ } else {
+ urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8225_ANAPARAM2_OFF);
+ urtw_write32_m(sc, URTW_ANAPARAM, URTW_8225_ANAPARAM_OFF);
+ }
+
+ urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE);
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225v2_rf_init(struct urtw_softc *sc)
+{
+ unsigned int i;
+ uint16_t data;
+ uint32_t data32;
+ usb_error_t error;
+
+ error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
+ if (error)
+ goto fail;
+
+ error = urtw_8225_usb_init(sc);
+ if (error)
+ goto fail;
+
+ urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008);
+ urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */
+ urtw_write16_m(sc, URTW_BRSR, 0xffff);
+ urtw_write32_m(sc, URTW_RF_PARA, 0x100044);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+ urtw_write8_m(sc, URTW_CONFIG3, 0x44);
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ error = urtw_8185_rf_pins_enable(sc);
+ if (error)
+ goto fail;
+
+ usb_pause_mtx(&sc->sc_mtx, 500);
+
+ for (i = 0; i < nitems(urtw_8225v2_rf_part1); i++) {
+ urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg,
+ urtw_8225v2_rf_part1[i].val);
+ }
+ usb_pause_mtx(&sc->sc_mtx, 50);
+
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1);
+
+ for (i = 0; i < 95; i++) {
+ urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1));
+ urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC,
+ urtw_8225v2_rxgain[i]);
+ }
+
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_3_MAGIC, URTW_8225_ADDR_3_DATA_MAGIC1);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_5_MAGIC, URTW_8225_ADDR_5_DATA_MAGIC1);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
+ usb_pause_mtx(&sc->sc_mtx, 100);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
+ usb_pause_mtx(&sc->sc_mtx, 100);
+
+ error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32);
+ if (error != 0)
+ goto fail;
+ if (data32 != URTW_8225_ADDR_6_DATA_MAGIC1)
+ device_printf(sc->sc_dev, "expect 0xe6!! (0x%x)\n", data32);
+ if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) {
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
+ usb_pause_mtx(&sc->sc_mtx, 100);
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
+ usb_pause_mtx(&sc->sc_mtx, 50);
+ error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32);
+ if (error != 0)
+ goto fail;
+ if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2))
+ device_printf(sc->sc_dev, "RF calibration failed\n");
+ }
+ usb_pause_mtx(&sc->sc_mtx, 100);
+
+ urtw_8225_write(sc,
+ URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC6);
+ for (i = 0; i < 128; i++) {
+ urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]);
+ urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80);
+ }
+
+ for (i = 0; i < nitems(urtw_8225v2_rf_part2); i++) {
+ urtw_8187_write_phy_ofdm(sc, urtw_8225v2_rf_part2[i].reg,
+ urtw_8225v2_rf_part2[i].val);
+ }
+
+ error = urtw_8225v2_setgain(sc, 4);
+ if (error)
+ goto fail;
+
+ for (i = 0; i < nitems(urtw_8225v2_rf_part3); i++) {
+ urtw_8187_write_phy_cck(sc, urtw_8225v2_rf_part3[i].reg,
+ urtw_8225v2_rf_part3[i].val);
+ }
+
+ urtw_write8_m(sc, URTW_TESTR, 0x0d);
+
+ error = urtw_8225v2_set_txpwrlvl(sc, 1);
+ if (error)
+ goto fail;
+
+ urtw_8187_write_phy_cck(sc, 0x10, 0x9b);
+ urtw_8187_write_phy_ofdm(sc, 0x26, 0x90);
+
+ /* TX ant A, 0x0 for B */
+ error = urtw_8185_tx_antenna(sc, 0x3);
+ if (error)
+ goto fail;
+ urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002);
+
+ error = urtw_8225_rf_set_chan(sc, 1);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan)
+{
+ usb_error_t error;
+
+ error = urtw_8225v2_set_txpwrlvl(sc, chan);
+ if (error)
+ goto fail;
+
+ urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]);
+ usb_pause_mtx(&sc->sc_mtx, 10);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_read(struct urtw_softc *sc, uint8_t addr, uint32_t *data)
+{
+ int i;
+ int16_t bit;
+ uint8_t rlen = 12, wlen = 6;
+ uint16_t o1, o2, o3, tmp;
+ uint32_t d2w = ((uint32_t)(addr & 0x1f)) << 27;
+ uint32_t mask = 0x80000000, value = 0;
+ usb_error_t error;
+
+ urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &o1);
+ urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &o2);
+ urtw_read16_m(sc, URTW_RF_PINS_SELECT, &o3);
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2 | URTW_RF_PINS_MAGIC4);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3 | URTW_RF_PINS_MAGIC4);
+ o1 &= ~URTW_RF_PINS_MAGIC4;
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN);
+ DELAY(5);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1);
+ DELAY(5);
+
+ for (i = 0; i < (wlen / 2); i++, mask = mask >> 1) {
+ bit = ((d2w & mask) != 0) ? 1 : 0;
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ mask = mask >> 1;
+ if (i == 2)
+ break;
+ bit = ((d2w & mask) != 0) ? 1 : 0;
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1);
+ DELAY(1);
+ }
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW |
+ URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+
+ mask = 0x800;
+ for (i = 0; i < rlen; i++, mask = mask >> 1) {
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
+ DELAY(2);
+
+ urtw_read16_m(sc, URTW_RF_PINS_INPUT, &tmp);
+ value |= ((tmp & URTW_BB_HOST_BANG_CLK) ? mask : 0);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
+ o1 | URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+ }
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN |
+ URTW_BB_HOST_BANG_RW);
+ DELAY(2);
+
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3);
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_OUTPUT_MAGIC1);
+
+ if (data != NULL)
+ *data = value;
+fail:
+ return (error);
+}
+
+
+static usb_error_t
+urtw_8225v2_set_txpwrlvl(struct urtw_softc *sc, int chan)
+{
+ int i;
+ uint8_t *cck_pwrtable;
+ uint8_t cck_pwrlvl_max = 15, ofdm_pwrlvl_max = 25, ofdm_pwrlvl_min = 10;
+ uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff;
+ uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff;
+ usb_error_t error;
+
+ /* CCK power setting */
+ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl;
+ cck_pwrlvl += sc->sc_txpwr_cck_base;
+ cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl;
+ cck_pwrtable = (chan == 14) ? urtw_8225v2_txpwr_cck_ch14 :
+ urtw_8225v2_txpwr_cck;
+
+ for (i = 0; i < 8; i++)
+ urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]);
+
+ urtw_write8_m(sc, URTW_TX_GAIN_CCK,
+ urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl]);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+
+ /* OFDM power setting */
+ ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ?
+ ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min;
+ ofdm_pwrlvl += sc->sc_txpwr_ofdm_base;
+ ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl;
+
+ error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
+ if (error)
+ goto fail;
+
+ urtw_8187_write_phy_ofdm(sc, 2, 0x42);
+ urtw_8187_write_phy_ofdm(sc, 5, 0x0);
+ urtw_8187_write_phy_ofdm(sc, 6, 0x40);
+ urtw_8187_write_phy_ofdm(sc, 7, 0x0);
+ urtw_8187_write_phy_ofdm(sc, 8, 0x40);
+
+ urtw_write8_m(sc, URTW_TX_GAIN_OFDM,
+ urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl]);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain)
+{
+ uint8_t *gainp;
+ usb_error_t error;
+
+ /* XXX for A? */
+ gainp = urtw_8225v2_gain_bg;
+ urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8187_write_phy_ofdm(sc, 0x21, 0x17);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225_isv2(struct urtw_softc *sc, int *ret)
+{
+ uint32_t data;
+ usb_error_t error;
+
+ *ret = 1;
+
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_MAGIC5);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, URTW_RF_PINS_MAGIC5);
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, URTW_RF_PINS_MAGIC5);
+ usb_pause_mtx(&sc->sc_mtx, 500);
+
+ urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC,
+ URTW_8225_ADDR_0_DATA_MAGIC1);
+
+ error = urtw_8225_read(sc, URTW_8225_ADDR_8_MAGIC, &data);
+ if (error != 0)
+ goto fail;
+ if (data != URTW_8225_ADDR_8_DATA_MAGIC1)
+ *ret = 0;
+ else {
+ error = urtw_8225_read(sc, URTW_8225_ADDR_9_MAGIC, &data);
+ if (error != 0)
+ goto fail;
+ if (data != URTW_8225_ADDR_9_DATA_MAGIC1)
+ *ret = 0;
+ }
+
+ urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC,
+ URTW_8225_ADDR_0_DATA_MAGIC2);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225v2b_rf_init(struct urtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ unsigned int i;
+ uint8_t data8;
+ usb_error_t error;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+
+ /*
+ * initialize extra registers on 8187
+ */
+ urtw_write16_m(sc, URTW_BRSR_8187B, 0xfff);
+
+ /* retry limit */
+ urtw_read8_m(sc, URTW_CW_CONF, &data8);
+ data8 |= URTW_CW_CONF_PERPACKET_RETRY;
+ urtw_write8_m(sc, URTW_CW_CONF, data8);
+
+ /* TX AGC */
+ urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8);
+ data8 |= URTW_TX_AGC_CTL_PERPACKET_GAIN;
+ urtw_write8_m(sc, URTW_TX_AGC_CTL, data8);
+
+ /* Auto Rate Fallback Control */
+#define URTW_ARFR 0x1e0
+ urtw_write16_m(sc, URTW_ARFR, 0xfff);
+ urtw_read8_m(sc, URTW_RATE_FALLBACK, &data8);
+ urtw_write8_m(sc, URTW_RATE_FALLBACK,
+ data8 | URTW_RATE_FALLBACK_ENABLE);
+
+ urtw_read8_m(sc, URTW_MSR, &data8);
+ urtw_write8_m(sc, URTW_MSR, data8 & 0xf3);
+ urtw_read8_m(sc, URTW_MSR, &data8);
+ urtw_write8_m(sc, URTW_MSR, data8 | URTW_MSR_LINK_ENEDCA);
+ urtw_write8_m(sc, URTW_ACM_CONTROL, sc->sc_acmctl);
+
+ urtw_write16_m(sc, URTW_ATIM_WND, 2);
+ urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100);
+#define URTW_FEMR_FOR_8187B 0x1d4
+ urtw_write16_m(sc, URTW_FEMR_FOR_8187B, 0xffff);
+
+ /* led type */
+ urtw_read8_m(sc, URTW_CONFIG1, &data8);
+ data8 = (data8 & 0x3f) | 0x80;
+ urtw_write8_m(sc, URTW_CONFIG1, data8);
+
+ /* applying MAC address again. */
+ urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)ic->ic_macaddr)[0]);
+ urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)ic->ic_macaddr)[1] & 0xffff);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ urtw_write8_m(sc, URTW_WPA_CONFIG, 0);
+
+ /*
+ * MAC configuration
+ */
+ for (i = 0; i < nitems(urtw_8225v2b_rf_part1); i++)
+ urtw_write8_m(sc, urtw_8225v2b_rf_part1[i].reg,
+ urtw_8225v2b_rf_part1[i].val);
+ urtw_write16_m(sc, URTW_TID_AC_MAP, 0xfa50);
+ urtw_write16_m(sc, URTW_INT_MIG, 0x0000);
+ urtw_write32_m(sc, 0x1f0, 0);
+ urtw_write32_m(sc, 0x1f4, 0);
+ urtw_write8_m(sc, 0x1f8, 0);
+ urtw_write32_m(sc, URTW_RF_TIMING, 0x4001);
+
+#define URTW_RFSW_CTRL 0x272
+ urtw_write16_m(sc, URTW_RFSW_CTRL, 0x569a);
+
+ /*
+ * initialize PHY
+ */
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+ urtw_read8_m(sc, URTW_CONFIG3, &data8);
+ urtw_write8_m(sc, URTW_CONFIG3,
+ data8 | URTW_CONFIG3_ANAPARAM_WRITE);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+
+ /* setup RFE initial timing */
+ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x0480);
+ urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x2488);
+ urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1fff);
+ usb_pause_mtx(&sc->sc_mtx, 1100);
+
+ for (i = 0; i < nitems(urtw_8225v2b_rf_part0); i++) {
+ urtw_8225_write(sc, urtw_8225v2b_rf_part0[i].reg,
+ urtw_8225v2b_rf_part0[i].val);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ }
+ urtw_8225_write(sc, 0x00, 0x01b7);
+
+ for (i = 0; i < 95; i++) {
+ urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1));
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC,
+ urtw_8225v2b_rxgain[i]);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ }
+
+ urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, 0x080);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, 0x004);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x0b7);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ usb_pause_mtx(&sc->sc_mtx, 3000);
+ urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0xc4d);
+ usb_pause_mtx(&sc->sc_mtx, 2000);
+ urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0x44d);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+ urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x2bf);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+
+ urtw_write8_m(sc, URTW_TX_GAIN_CCK, 0x03);
+ urtw_write8_m(sc, URTW_TX_GAIN_OFDM, 0x07);
+ urtw_write8_m(sc, URTW_TX_ANTENNA, 0x03);
+
+ urtw_8187_write_phy_ofdm(sc, 0x80, 0x12);
+ for (i = 0; i < 128; i++) {
+ uint32_t addr, data;
+
+ data = (urtw_8225z2_agc[i] << 8) | 0x0000008f;
+ addr = ((i + 0x80) << 8) | 0x0000008e;
+
+ urtw_8187_write_phy_ofdm(sc, data & 0x7f, (data >> 8) & 0xff);
+ urtw_8187_write_phy_ofdm(sc, addr & 0x7f, (addr >> 8) & 0xff);
+ urtw_8187_write_phy_ofdm(sc, 0x0e, 0x00);
+ }
+ urtw_8187_write_phy_ofdm(sc, 0x80, 0x10);
+
+ for (i = 0; i < nitems(urtw_8225v2b_rf_part2); i++)
+ urtw_8187_write_phy_ofdm(sc, i, urtw_8225v2b_rf_part2[i].val);
+
+ urtw_write32_m(sc, URTW_8187B_AC_VO, (7 << 12) | (3 << 8) | 0x1c);
+ urtw_write32_m(sc, URTW_8187B_AC_VI, (7 << 12) | (3 << 8) | 0x1c);
+ urtw_write32_m(sc, URTW_8187B_AC_BE, (7 << 12) | (3 << 8) | 0x1c);
+ urtw_write32_m(sc, URTW_8187B_AC_BK, (7 << 12) | (3 << 8) | 0x1c);
+
+ urtw_8187_write_phy_ofdm(sc, 0x97, 0x46);
+ urtw_8187_write_phy_ofdm(sc, 0xa4, 0xb6);
+ urtw_8187_write_phy_ofdm(sc, 0x85, 0xfc);
+ urtw_8187_write_phy_cck(sc, 0xc1, 0x88);
+
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225v2b_rf_set_chan(struct urtw_softc *sc, int chan)
+{
+ usb_error_t error;
+
+ error = urtw_8225v2b_set_txpwrlvl(sc, chan);
+ if (error)
+ goto fail;
+
+ urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]);
+ usb_pause_mtx(&sc->sc_mtx, 10);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8225v2b_set_txpwrlvl(struct urtw_softc *sc, int chan)
+{
+ int i;
+ uint8_t *cck_pwrtable;
+ uint8_t cck_pwrlvl_max = 15;
+ uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff;
+ uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff;
+ usb_error_t error;
+
+ /* CCK power setting */
+ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ?
+ ((sc->sc_flags & URTW_RTL8187B_REV_B) ? cck_pwrlvl_max : 22) :
+ (cck_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 0 : 7));
+ cck_pwrlvl += sc->sc_txpwr_cck_base;
+ cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl;
+ cck_pwrtable = (chan == 14) ? urtw_8225v2b_txpwr_cck_ch14 :
+ urtw_8225v2b_txpwr_cck;
+
+ if (sc->sc_flags & URTW_RTL8187B_REV_B)
+ cck_pwrtable += (cck_pwrlvl <= 6) ? 0 :
+ ((cck_pwrlvl <= 11) ? 8 : 16);
+ else
+ cck_pwrtable += (cck_pwrlvl <= 5) ? 0 :
+ ((cck_pwrlvl <= 11) ? 8 : ((cck_pwrlvl <= 17) ? 16 : 24));
+
+ for (i = 0; i < 8; i++)
+ urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]);
+
+ urtw_write8_m(sc, URTW_TX_GAIN_CCK,
+ urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl] << 1);
+ usb_pause_mtx(&sc->sc_mtx, 1);
+
+ /* OFDM power setting */
+ ofdm_pwrlvl = (ofdm_pwrlvl > 15) ?
+ ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 17 : 25) :
+ (ofdm_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 2 : 10));
+ ofdm_pwrlvl += sc->sc_txpwr_ofdm_base;
+ ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl;
+
+ urtw_write8_m(sc, URTW_TX_GAIN_OFDM,
+ urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl] << 1);
+
+ if (sc->sc_flags & URTW_RTL8187B_REV_B) {
+ if (ofdm_pwrlvl <= 11) {
+ urtw_8187_write_phy_ofdm(sc, 0x87, 0x60);
+ urtw_8187_write_phy_ofdm(sc, 0x89, 0x60);
+ } else {
+ urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c);
+ urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c);
+ }
+ } else {
+ if (ofdm_pwrlvl <= 11) {
+ urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c);
+ urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c);
+ } else if (ofdm_pwrlvl <= 17) {
+ urtw_8187_write_phy_ofdm(sc, 0x87, 0x54);
+ urtw_8187_write_phy_ofdm(sc, 0x89, 0x54);
+ } else {
+ urtw_8187_write_phy_ofdm(sc, 0x87, 0x50);
+ urtw_8187_write_phy_ofdm(sc, 0x89, 0x50);
+ }
+ }
+ usb_pause_mtx(&sc->sc_mtx, 1);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_GETREGS_REQ;
+ USETW(req.wValue, val | 0xfe00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint8_t));
+
+ error = urtw_do_request(sc, &req, data);
+ return (error);
+}
+
+static usb_error_t
+urtw_write8e(struct urtw_softc *sc, int val, uint8_t data)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = URTW_8187_SETREGS_REQ;
+ USETW(req.wValue, val | 0xfe00);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(uint8_t));
+
+ return (urtw_do_request(sc, &req, &data));
+}
+
+static usb_error_t
+urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE);
+ urtw_write32_m(sc, URTW_ANAPARAM, val);
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
+ if (error)
+ goto fail;
+
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE);
+ urtw_write32_m(sc, URTW_ANAPARAM2, val);
+ urtw_read8_m(sc, URTW_CONFIG3, &data);
+ urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE);
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
+ if (error)
+ goto fail;
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_intr_enable(struct urtw_softc *sc)
+{
+ usb_error_t error;
+
+ urtw_write16_m(sc, URTW_INTR_MASK, 0xffff);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_intr_disable(struct urtw_softc *sc)
+{
+ usb_error_t error;
+
+ urtw_write16_m(sc, URTW_INTR_MASK, 0);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_reset(struct urtw_softc *sc)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
+ if (error)
+ goto fail;
+ error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
+ if (error)
+ goto fail;
+
+ error = urtw_intr_disable(sc);
+ if (error)
+ goto fail;
+ usb_pause_mtx(&sc->sc_mtx, 100);
+
+ error = urtw_write8e(sc, 0x18, 0x10);
+ if (error != 0)
+ goto fail;
+ error = urtw_write8e(sc, 0x18, 0x11);
+ if (error != 0)
+ goto fail;
+ error = urtw_write8e(sc, 0x18, 0x00);
+ if (error != 0)
+ goto fail;
+ usb_pause_mtx(&sc->sc_mtx, 100);
+
+ urtw_read8_m(sc, URTW_CMD, &data);
+ data = (data & 0x2) | URTW_CMD_RST;
+ urtw_write8_m(sc, URTW_CMD, data);
+ usb_pause_mtx(&sc->sc_mtx, 100);
+
+ urtw_read8_m(sc, URTW_CMD, &data);
+ if (data & URTW_CMD_RST) {
+ device_printf(sc->sc_dev, "reset timeout\n");
+ goto fail;
+ }
+
+ error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD);
+ if (error)
+ goto fail;
+ usb_pause_mtx(&sc->sc_mtx, 100);
+
+ error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
+ if (error)
+ goto fail;
+ error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
+ if (error)
+ goto fail;
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_led_ctl(struct urtw_softc *sc, int mode)
+{
+ usb_error_t error = 0;
+
+ switch (sc->sc_strategy) {
+ case URTW_SW_LED_MODE0:
+ error = urtw_led_mode0(sc, mode);
+ break;
+ case URTW_SW_LED_MODE1:
+ error = urtw_led_mode1(sc, mode);
+ break;
+ case URTW_SW_LED_MODE2:
+ error = urtw_led_mode2(sc, mode);
+ break;
+ case URTW_SW_LED_MODE3:
+ error = urtw_led_mode3(sc, mode);
+ break;
+ default:
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unsupported LED mode %d\n", sc->sc_strategy);
+ error = USB_ERR_INVAL;
+ break;
+ }
+
+ return (error);
+}
+
+static usb_error_t
+urtw_led_mode0(struct urtw_softc *sc, int mode)
+{
+
+ switch (mode) {
+ case URTW_LED_CTL_POWER_ON:
+ sc->sc_gpio_ledstate = URTW_LED_POWER_ON_BLINK;
+ break;
+ case URTW_LED_CTL_TX:
+ if (sc->sc_gpio_ledinprogress == 1)
+ return (0);
+
+ sc->sc_gpio_ledstate = URTW_LED_BLINK_NORMAL;
+ sc->sc_gpio_blinktime = 2;
+ break;
+ case URTW_LED_CTL_LINK:
+ sc->sc_gpio_ledstate = URTW_LED_ON;
+ break;
+ default:
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unsupported LED mode 0x%x", mode);
+ return (USB_ERR_INVAL);
+ }
+
+ switch (sc->sc_gpio_ledstate) {
+ case URTW_LED_ON:
+ if (sc->sc_gpio_ledinprogress != 0)
+ break;
+ urtw_led_on(sc, URTW_LED_GPIO);
+ break;
+ case URTW_LED_BLINK_NORMAL:
+ if (sc->sc_gpio_ledinprogress != 0)
+ break;
+ sc->sc_gpio_ledinprogress = 1;
+ sc->sc_gpio_blinkstate = (sc->sc_gpio_ledon != 0) ?
+ URTW_LED_OFF : URTW_LED_ON;
+ usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc);
+ break;
+ case URTW_LED_POWER_ON_BLINK:
+ urtw_led_on(sc, URTW_LED_GPIO);
+ usb_pause_mtx(&sc->sc_mtx, 100);
+ urtw_led_off(sc, URTW_LED_GPIO);
+ break;
+ default:
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unknown LED status 0x%x", sc->sc_gpio_ledstate);
+ return (USB_ERR_INVAL);
+ }
+ return (0);
+}
+
+static usb_error_t
+urtw_led_mode1(struct urtw_softc *sc, int mode)
+{
+ return (USB_ERR_INVAL);
+}
+
+static usb_error_t
+urtw_led_mode2(struct urtw_softc *sc, int mode)
+{
+ return (USB_ERR_INVAL);
+}
+
+static usb_error_t
+urtw_led_mode3(struct urtw_softc *sc, int mode)
+{
+ return (USB_ERR_INVAL);
+}
+
+static usb_error_t
+urtw_led_on(struct urtw_softc *sc, int type)
+{
+ usb_error_t error;
+
+ if (type == URTW_LED_GPIO) {
+ switch (sc->sc_gpio_ledpin) {
+ case URTW_LED_PIN_GPIO0:
+ urtw_write8_m(sc, URTW_GPIO, 0x01);
+ urtw_write8_m(sc, URTW_GP_ENABLE, 0x00);
+ break;
+ default:
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unsupported LED PIN type 0x%x",
+ sc->sc_gpio_ledpin);
+ error = USB_ERR_INVAL;
+ goto fail;
+ }
+ } else {
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unsupported LED type 0x%x", type);
+ error = USB_ERR_INVAL;
+ goto fail;
+ }
+
+ sc->sc_gpio_ledon = 1;
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_led_off(struct urtw_softc *sc, int type)
+{
+ usb_error_t error;
+
+ if (type == URTW_LED_GPIO) {
+ switch (sc->sc_gpio_ledpin) {
+ case URTW_LED_PIN_GPIO0:
+ urtw_write8_m(sc, URTW_GPIO, URTW_GPIO_DATA_MAGIC1);
+ urtw_write8_m(sc,
+ URTW_GP_ENABLE, URTW_GP_ENABLE_DATA_MAGIC1);
+ break;
+ default:
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unsupported LED PIN type 0x%x",
+ sc->sc_gpio_ledpin);
+ error = USB_ERR_INVAL;
+ goto fail;
+ }
+ } else {
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unsupported LED type 0x%x", type);
+ error = USB_ERR_INVAL;
+ goto fail;
+ }
+
+ sc->sc_gpio_ledon = 0;
+
+fail:
+ return (error);
+}
+
+static void
+urtw_led_ch(void *arg)
+{
+ struct urtw_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ ieee80211_runtask(ic, &sc->sc_led_task);
+}
+
+static void
+urtw_ledtask(void *arg, int pending)
+{
+ struct urtw_softc *sc = arg;
+
+ if (sc->sc_strategy != URTW_SW_LED_MODE0) {
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "could not process a LED strategy 0x%x",
+ sc->sc_strategy);
+ return;
+ }
+
+ URTW_LOCK(sc);
+ urtw_led_blink(sc);
+ URTW_UNLOCK(sc);
+}
+
+static usb_error_t
+urtw_led_blink(struct urtw_softc *sc)
+{
+ uint8_t ing = 0;
+ usb_error_t error;
+
+ if (sc->sc_gpio_blinkstate == URTW_LED_ON)
+ error = urtw_led_on(sc, URTW_LED_GPIO);
+ else
+ error = urtw_led_off(sc, URTW_LED_GPIO);
+ sc->sc_gpio_blinktime--;
+ if (sc->sc_gpio_blinktime == 0)
+ ing = 1;
+ else {
+ if (sc->sc_gpio_ledstate != URTW_LED_BLINK_NORMAL &&
+ sc->sc_gpio_ledstate != URTW_LED_BLINK_SLOWLY &&
+ sc->sc_gpio_ledstate != URTW_LED_BLINK_CM3)
+ ing = 1;
+ }
+ if (ing == 1) {
+ if (sc->sc_gpio_ledstate == URTW_LED_ON &&
+ sc->sc_gpio_ledon == 0)
+ error = urtw_led_on(sc, URTW_LED_GPIO);
+ else if (sc->sc_gpio_ledstate == URTW_LED_OFF &&
+ sc->sc_gpio_ledon == 1)
+ error = urtw_led_off(sc, URTW_LED_GPIO);
+
+ sc->sc_gpio_blinktime = 0;
+ sc->sc_gpio_ledinprogress = 0;
+ return (0);
+ }
+
+ sc->sc_gpio_blinkstate = (sc->sc_gpio_blinkstate != URTW_LED_ON) ?
+ URTW_LED_ON : URTW_LED_OFF;
+
+ switch (sc->sc_gpio_ledstate) {
+ case URTW_LED_BLINK_NORMAL:
+ usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc);
+ break;
+ default:
+ DPRINTF(sc, URTW_DEBUG_STATE,
+ "unknown LED status 0x%x",
+ sc->sc_gpio_ledstate);
+ return (USB_ERR_INVAL);
+ }
+ return (0);
+}
+
+static usb_error_t
+urtw_rx_enable(struct urtw_softc *sc)
+{
+ uint8_t data;
+ usb_error_t error;
+
+ usbd_transfer_start((sc->sc_flags & URTW_RTL8187B) ?
+ sc->sc_xfer[URTW_8187B_BULK_RX] : sc->sc_xfer[URTW_8187L_BULK_RX]);
+
+ error = urtw_rx_setconf(sc);
+ if (error != 0)
+ goto fail;
+
+ if ((sc->sc_flags & URTW_RTL8187B) == 0) {
+ urtw_read8_m(sc, URTW_CMD, &data);
+ urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE);
+ }
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_tx_enable(struct urtw_softc *sc)
+{
+ uint8_t data8;
+ uint32_t data;
+ usb_error_t error;
+
+ if (sc->sc_flags & URTW_RTL8187B) {
+ urtw_read32_m(sc, URTW_TX_CONF, &data);
+ data &= ~URTW_TX_LOOPBACK_MASK;
+ data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK);
+ data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK);
+ data &= ~URTW_TX_SWPLCPLEN;
+ data |= URTW_TX_HW_SEQNUM | URTW_TX_DISREQQSIZE |
+ (7 << 8) | /* short retry limit */
+ (7 << 0) | /* long retry limit */
+ (7 << 21); /* MAX TX DMA */
+ urtw_write32_m(sc, URTW_TX_CONF, data);
+
+ urtw_read8_m(sc, URTW_MSR, &data8);
+ data8 |= URTW_MSR_LINK_ENEDCA;
+ urtw_write8_m(sc, URTW_MSR, data8);
+ return (error);
+ }
+
+ urtw_read8_m(sc, URTW_CW_CONF, &data8);
+ data8 &= ~(URTW_CW_CONF_PERPACKET_CW | URTW_CW_CONF_PERPACKET_RETRY);
+ urtw_write8_m(sc, URTW_CW_CONF, data8);
+
+ urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8);
+ data8 &= ~URTW_TX_AGC_CTL_PERPACKET_GAIN;
+ data8 &= ~URTW_TX_AGC_CTL_PERPACKET_ANTSEL;
+ data8 &= ~URTW_TX_AGC_CTL_FEEDBACK_ANT;
+ urtw_write8_m(sc, URTW_TX_AGC_CTL, data8);
+
+ urtw_read32_m(sc, URTW_TX_CONF, &data);
+ data &= ~URTW_TX_LOOPBACK_MASK;
+ data |= URTW_TX_LOOPBACK_NONE;
+ data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK);
+ data |= sc->sc_tx_retry << URTW_TX_DPRETRY_SHIFT;
+ data |= sc->sc_rts_retry << URTW_TX_RTSRETRY_SHIFT;
+ data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK);
+ data |= URTW_TX_MXDMA_2048 | URTW_TX_CWMIN | URTW_TX_DISCW;
+ data &= ~URTW_TX_SWPLCPLEN;
+ data |= URTW_TX_NOICV;
+ urtw_write32_m(sc, URTW_TX_CONF, data);
+
+ urtw_read8_m(sc, URTW_CMD, &data8);
+ urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE);
+fail:
+ return (error);
+}
+
+static usb_error_t
+urtw_rx_setconf(struct urtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t data;
+ usb_error_t error;
+
+ urtw_read32_m(sc, URTW_RX, &data);
+ data = data &~ URTW_RX_FILTER_MASK;
+ if (sc->sc_flags & URTW_RTL8187B) {
+ data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA |
+ URTW_RX_FILTER_MCAST | URTW_RX_FILTER_BCAST |
+ URTW_RX_FILTER_NICMAC | URTW_RX_CHECK_BSSID |
+ URTW_RX_FIFO_THRESHOLD_NONE |
+ URTW_MAX_RX_DMA_2048 |
+ URTW_RX_AUTORESETPHY | URTW_RCR_ONLYERLPKT;
+ } else {
+ data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA;
+ data = data | URTW_RX_FILTER_BCAST | URTW_RX_FILTER_MCAST;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ data = data | URTW_RX_FILTER_ICVERR;
+ data = data | URTW_RX_FILTER_PWR;
+ }
+ if (sc->sc_crcmon == 1 && ic->ic_opmode == IEEE80211_M_MONITOR)
+ data = data | URTW_RX_FILTER_CRCERR;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+ ic->ic_promisc > 0 || ic->ic_allmulti > 0) {
+ data = data | URTW_RX_FILTER_ALLMAC;
+ } else {
+ data = data | URTW_RX_FILTER_NICMAC;
+ data = data | URTW_RX_CHECK_BSSID;
+ }
+
+ data = data &~ URTW_RX_FIFO_THRESHOLD_MASK;
+ data = data | URTW_RX_FIFO_THRESHOLD_NONE |
+ URTW_RX_AUTORESETPHY;
+ data = data &~ URTW_MAX_RX_DMA_MASK;
+ data = data | URTW_MAX_RX_DMA_2048 | URTW_RCR_ONLYERLPKT;
+ }
+
+ urtw_write32_m(sc, URTW_RX, data);
+fail:
+ return (error);
+}
+
+static struct mbuf *
+urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p,
+ int8_t *nf_p)
+{
+ int actlen, flen, rssi;
+ struct ieee80211_frame *wh;
+ struct mbuf *m, *mnew;
+ struct urtw_softc *sc = data->sc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t noise = 0, rate;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ if (actlen < (int)URTW_MIN_RXBUFSZ) {
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+ }
+
+ if (sc->sc_flags & URTW_RTL8187B) {
+ struct urtw_8187b_rxhdr *rx;
+
+ rx = (struct urtw_8187b_rxhdr *)(data->buf +
+ (actlen - (sizeof(struct urtw_8187b_rxhdr))));
+ flen = le32toh(rx->flag) & 0xfff;
+ if (flen > actlen) {
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+ }
+ rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf;
+ /* XXX correct? */
+ rssi = rx->rssi & URTW_RX_RSSI_MASK;
+ noise = rx->noise;
+ } else {
+ struct urtw_8187l_rxhdr *rx;
+
+ rx = (struct urtw_8187l_rxhdr *)(data->buf +
+ (actlen - (sizeof(struct urtw_8187l_rxhdr))));
+ flen = le32toh(rx->flag) & 0xfff;
+ if (flen > actlen) {
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+ }
+
+ rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf;
+ /* XXX correct? */
+ rssi = rx->rssi & URTW_RX_8187L_RSSI_MASK;
+ noise = rx->noise;
+ }
+
+ mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (mnew == NULL) {
+ counter_u64_add(ic->ic_ierrors, 1);
+ return (NULL);
+ }
+
+ m = data->m;
+ data->m = mnew;
+ data->buf = mtod(mnew, uint8_t *);
+
+ /* finalize mbuf */
+ m->m_pkthdr.len = m->m_len = flen - IEEE80211_CRC_LEN;
+
+ if (ieee80211_radiotap_active(ic)) {
+ struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ /* XXX Are variables correct? */
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_dbm_antsignal = (int8_t)rssi;
+ }
+
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA)
+ sc->sc_currate = (rate > 0) ? rate : sc->sc_currate;
+
+ *rssi_p = rssi;
+ *nf_p = noise; /* XXX correct? */
+
+ return (m);
+}
+
+static void
+urtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct urtw_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL;
+ struct urtw_data *data;
+ int8_t nf = -95;
+ int rssi = 1;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&sc->sc_rx_active);
+ if (data == NULL)
+ goto setup;
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
+ m = urtw_rxeof(xfer, data, &rssi, &nf);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+setup:
+ data = STAILQ_FIRST(&sc->sc_rx_inactive);
+ if (data == NULL) {
+ KASSERT(m == NULL, ("mbuf isn't NULL"));
+ return;
+ }
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
+ usbd_xfer_set_frame_data(xfer, 0, data->buf,
+ usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+
+ /*
+ * To avoid LOR we should unlock our private mutex here to call
+ * ieee80211_input() because here is at the end of a USB
+ * callback and safe to unlock.
+ */
+ URTW_UNLOCK(sc);
+ if (m != NULL) {
+ wh = mtod(m, struct ieee80211_frame *);
+ ni = ieee80211_find_rxnode(ic,
+ (struct ieee80211_frame_min *)wh);
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi, nf);
+ /* node is no longer needed */
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi, nf);
+ m = NULL;
+ }
+ URTW_LOCK(sc);
+ break;
+ default:
+ /* needs it to the inactive queue due to a error. */
+ data = STAILQ_FIRST(&sc->sc_rx_active);
+ if (data != NULL) {
+ STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
+ STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
+ }
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto setup;
+ }
+ break;
+ }
+}
+
+#define URTW_STATUS_TYPE_TXCLOSE 1
+#define URTW_STATUS_TYPE_BEACON_INTR 0
+
+static void
+urtw_txstatus_eof(struct usb_xfer *xfer)
+{
+ struct urtw_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ int actlen, type, pktretry, seq;
+ uint64_t val;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ if (actlen != sizeof(uint64_t))
+ return;
+
+ val = le64toh(sc->sc_txstatus);
+ type = (val >> 30) & 0x3;
+ if (type == URTW_STATUS_TYPE_TXCLOSE) {
+ pktretry = val & 0xff;
+ seq = (val >> 16) & 0xff;
+ if (pktretry == URTW_TX_MAXRETRY)
+ counter_u64_add(ic->ic_oerrors, 1);
+ DPRINTF(sc, URTW_DEBUG_TXSTATUS, "pktretry %d seq %#x\n",
+ pktretry, seq);
+ }
+}
+
+static void
+urtw_bulk_tx_status_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct urtw_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ void *dma_buf = usbd_xfer_get_frame_buffer(xfer, 0);
+
+ URTW_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ urtw_txstatus_eof(xfer);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+setup:
+ memcpy(dma_buf, &sc->sc_txstatus, sizeof(uint64_t));
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(uint64_t));
+ usbd_transfer_submit(xfer);
+ break;
+ default:
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto setup;
+ }
+ break;
+ }
+}
+
+static void
+urtw_txeof(struct usb_xfer *xfer, struct urtw_data *data)
+{
+ struct urtw_softc *sc = usbd_xfer_softc(xfer);
+
+ URTW_ASSERT_LOCKED(sc);
+
+ if (data->m) {
+ /* XXX status? */
+ ieee80211_tx_complete(data->ni, data->m, 0);
+ data->m = NULL;
+ data->ni = NULL;
+ }
+ sc->sc_txtimer = 0;
+}
+
+static void
+urtw_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct urtw_softc *sc = usbd_xfer_softc(xfer);
+ struct urtw_data *data;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ data = STAILQ_FIRST(&sc->sc_tx_active);
+ if (data == NULL)
+ goto setup;
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next);
+ urtw_txeof(xfer, data);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+setup:
+ data = STAILQ_FIRST(&sc->sc_tx_pending);
+ if (data == NULL) {
+ DPRINTF(sc, URTW_DEBUG_XMIT,
+ "%s: empty pending queue\n", __func__);
+ return;
+ }
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next);
+ STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next);
+
+ usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
+ usbd_transfer_submit(xfer);
+
+ urtw_start(sc);
+ break;
+ default:
+ data = STAILQ_FIRST(&sc->sc_tx_active);
+ if (data == NULL)
+ goto setup;
+ if (data->ni != NULL) {
+ if_inc_counter(data->ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ if (error != USB_ERR_CANCELLED) {
+ usbd_xfer_set_stall(xfer);
+ goto setup;
+ }
+ break;
+ }
+}
+
+static struct urtw_data *
+_urtw_getbuf(struct urtw_softc *sc)
+{
+ struct urtw_data *bf;
+
+ bf = STAILQ_FIRST(&sc->sc_tx_inactive);
+ if (bf != NULL)
+ STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
+ else
+ bf = NULL;
+ if (bf == NULL)
+ DPRINTF(sc, URTW_DEBUG_XMIT, "%s: %s\n", __func__,
+ "out of xmit buffers");
+ return (bf);
+}
+
+static struct urtw_data *
+urtw_getbuf(struct urtw_softc *sc)
+{
+ struct urtw_data *bf;
+
+ URTW_ASSERT_LOCKED(sc);
+
+ bf = _urtw_getbuf(sc);
+ if (bf == NULL)
+ DPRINTF(sc, URTW_DEBUG_XMIT, "%s: stop queue\n", __func__);
+ return (bf);
+}
+
+static int
+urtw_isbmode(uint16_t rate)
+{
+
+ return ((rate <= 22 && rate != 12 && rate != 18) ||
+ rate == 44) ? (1) : (0);
+}
+
+static uint16_t
+urtw_rate2dbps(uint16_t rate)
+{
+
+ switch(rate) {
+ case 12:
+ case 18:
+ case 24:
+ case 36:
+ case 48:
+ case 72:
+ case 96:
+ case 108:
+ return (rate * 2);
+ default:
+ break;
+ }
+ return (24);
+}
+
+static int
+urtw_compute_txtime(uint16_t framelen, uint16_t rate,
+ uint8_t ismgt, uint8_t isshort)
+{
+ uint16_t ceiling, frametime, n_dbps;
+
+ if (urtw_isbmode(rate)) {
+ if (ismgt || !isshort || rate == 2)
+ frametime = (uint16_t)(144 + 48 +
+ (framelen * 8 / (rate / 2)));
+ else
+ frametime = (uint16_t)(72 + 24 +
+ (framelen * 8 / (rate / 2)));
+ if ((framelen * 8 % (rate / 2)) != 0)
+ frametime++;
+ } else {
+ n_dbps = urtw_rate2dbps(rate);
+ ceiling = (16 + 8 * framelen + 6) / n_dbps
+ + (((16 + 8 * framelen + 6) % n_dbps) ? 1 : 0);
+ frametime = (uint16_t)(16 + 4 + 4 * ceiling + 6);
+ }
+ return (frametime);
+}
+
+/*
+ * Callback from the 802.11 layer to update the
+ * slot time based on the current setting.
+ */
+static void
+urtw_updateslot(struct ieee80211com *ic)
+{
+ struct urtw_softc *sc = ic->ic_softc;
+
+ ieee80211_runtask(ic, &sc->sc_updateslot_task);
+}
+
+static void
+urtw_updateslottask(void *arg, int pending)
+{
+ struct urtw_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ int error;
+
+ URTW_LOCK(sc);
+ if ((sc->sc_flags & URTW_RUNNING) == 0) {
+ URTW_UNLOCK(sc);
+ return;
+ }
+ if (sc->sc_flags & URTW_RTL8187B) {
+ urtw_write8_m(sc, URTW_SIFS, 0x22);
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
+ urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SHSLOT);
+ else
+ urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SLOT);
+ urtw_write8_m(sc, URTW_8187B_EIFS, 0x5b);
+ urtw_write8_m(sc, URTW_CARRIER_SCOUNT, 0x5b);
+ } else {
+ urtw_write8_m(sc, URTW_SIFS, 0x22);
+ if (sc->sc_state == IEEE80211_S_ASSOC &&
+ ic->ic_flags & IEEE80211_F_SHSLOT)
+ urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SHSLOT);
+ else
+ urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SLOT);
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
+ urtw_write8_m(sc, URTW_DIFS, 0x14);
+ urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14);
+ urtw_write8_m(sc, URTW_CW_VAL, 0x73);
+ } else {
+ urtw_write8_m(sc, URTW_DIFS, 0x24);
+ urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24);
+ urtw_write8_m(sc, URTW_CW_VAL, 0xa5);
+ }
+ }
+fail:
+ URTW_UNLOCK(sc);
+}
+
+static void
+urtw_sysctl_node(struct urtw_softc *sc)
+{
+#define URTW_SYSCTL_STAT_ADD32(c, h, n, p, d) \
+ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d)
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *child, *parent;
+ struct sysctl_oid *tree;
+ struct urtw_stats *stats = &sc->sc_stats;
+
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
+
+ tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
+ NULL, "URTW statistics");
+ parent = SYSCTL_CHILDREN(tree);
+
+ /* Tx statistics. */
+ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD,
+ NULL, "Tx MAC statistics");
+ child = SYSCTL_CHILDREN(tree);
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "1m", &stats->txrates[0],
+ "1 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "2m", &stats->txrates[1],
+ "2 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "5.5m", &stats->txrates[2],
+ "5.5 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "6m", &stats->txrates[4],
+ "6 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "9m", &stats->txrates[5],
+ "9 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "11m", &stats->txrates[3],
+ "11 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "12m", &stats->txrates[6],
+ "12 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "18m", &stats->txrates[7],
+ "18 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "24m", &stats->txrates[8],
+ "24 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "36m", &stats->txrates[9],
+ "36 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "48m", &stats->txrates[10],
+ "48 Mbit/s");
+ URTW_SYSCTL_STAT_ADD32(ctx, child, "54m", &stats->txrates[11],
+ "54 Mbit/s");
+#undef URTW_SYSCTL_STAT_ADD32
+}
+
+static device_method_t urtw_methods[] = {
+ DEVMETHOD(device_probe, urtw_match),
+ DEVMETHOD(device_attach, urtw_attach),
+ DEVMETHOD(device_detach, urtw_detach),
+ DEVMETHOD_END
+};
+static driver_t urtw_driver = {
+ .name = "urtw",
+ .methods = urtw_methods,
+ .size = sizeof(struct urtw_softc)
+};
+static devclass_t urtw_devclass;
+
+DRIVER_MODULE(urtw, uhub, urtw_driver, urtw_devclass, NULL, 0);
+MODULE_DEPEND(urtw, wlan, 1, 1, 1);
+MODULE_DEPEND(urtw, usb, 1, 1, 1);
+MODULE_VERSION(urtw, 1);
+USB_PNP_HOST_INFO(urtw_devs);
diff --git a/freebsd/sys/dev/usb/wlan/if_urtwreg.h b/freebsd/sys/dev/usb/wlan/if_urtwreg.h
new file mode 100644
index 00000000..5021e5ac
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_urtwreg.h
@@ -0,0 +1,433 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define URTW_CONFIG_INDEX 0
+#define URTW_IFACE_INDEX 0
+
+/* for 8187 */
+#define URTW_MAC0 0x0000 /* 1 byte */
+#define URTW_MAC1 0x0001 /* 1 byte */
+#define URTW_MAC2 0x0002 /* 1 byte */
+#define URTW_MAC3 0x0003 /* 1 byte */
+#define URTW_MAC4 0x0004 /* 1 byte */
+#define URTW_MAC5 0x0005 /* 1 byte */
+#define URTW_MAR 0x0008 /* 6 byte */
+#define URTW_RXFIFO_CNT 0x0010 /* 1 byte */
+#define URTW_TXFIFO_CNT 0x0012 /* 1 byte */
+#define URTW_BQREQ 0x0013 /* 1 byte */
+#define URTW_TSFT 0x0018 /* 6 byte */
+#define URTW_TLPDA 0x0020 /* 4 byte */
+#define URTW_TNPDA 0x0024 /* 4 byte */
+#define URTW_THPDA 0x0028 /* 4 byte */
+#define URTW_BRSR 0x002c /* 2 byte */
+#define URTW_BRSR_MBR_8185 (0x0fff)
+#define URTW_8187B_EIFS 0x002d /* 1 byte for 8187B */
+#define URTW_BSSID 0x002e /* 6 byte */
+#define URTW_BRSR_8187B 0x0034 /* 2 byte for 8187B */
+#define URTW_RESP_RATE 0x0034 /* 1 byte for 8187L */
+#define URTW_RESP_MAX_RATE_SHIFT (4)
+#define URTW_RESP_MIN_RATE_SHIFT (0)
+#define URTW_EIFS 0x0035 /* 1 byte */
+#define URTW_CMD 0x0037 /* 1 byte */
+#define URTW_CMD_TX_ENABLE (0x4)
+#define URTW_CMD_RX_ENABLE (0x8)
+#define URTW_CMD_RST (0x10)
+#define URTW_INTR_MASK 0x003c /* 2 byte */
+#define URTW_INTR_STATUS 0x003e /* 2 byte */
+#define URTW_TX_CONF 0x0040 /* 4 byte */
+#define URTW_TX_LOOPBACK_SHIFT (17)
+#define URTW_TX_LOOPBACK_NONE (0 << URTW_TX_LOOPBACK_SHIFT)
+#define URTW_TX_LOOPBACK_MAC (1 << URTW_TX_LOOPBACK_SHIFT)
+#define URTW_TX_LOOPBACK_BASEBAND (2 << URTW_TX_LOOPBACK_SHIFT)
+#define URTW_TX_LOOPBACK_CONTINUE (3 << URTW_TX_LOOPBACK_SHIFT)
+#define URTW_TX_LOOPBACK_MASK (0x60000)
+#define URTW_TX_DPRETRY_MASK (0xff00)
+#define URTW_TX_RTSRETRY_MASK (0xff)
+#define URTW_TX_DPRETRY_SHIFT (0)
+#define URTW_TX_RTSRETRY_SHIFT (8)
+#define URTW_TX_NOCRC (0x10000)
+#define URTW_TX_MXDMA_MASK (0xe00000)
+#define URTW_TX_MXDMA_1024 (6 << URTW_TX_MXDMA_SHIFT)
+#define URTW_TX_MXDMA_2048 (7 << URTW_TX_MXDMA_SHIFT)
+#define URTW_TX_MXDMA_SHIFT (21)
+#define URTW_TX_DISCW (1 << 20)
+#define URTW_TX_SWPLCPLEN (1 << 24)
+#define URTW_TX_R8187vD (5 << 25)
+#define URTW_TX_R8187vD_B (6 << 25)
+#define URTW_TX_HWMASK (7 << 25)
+#define URTW_TX_DISREQQSIZE (1 << 28)
+#define URTW_TX_HW_SEQNUM (1 << 30)
+#define URTW_TX_CWMIN (1U << 31)
+#define URTW_TX_NOICV (0x80000)
+#define URTW_RX 0x0044 /* 4 byte */
+#define URTW_RX_9356SEL (1 << 6)
+#define URTW_RX_FILTER_MASK \
+ (URTW_RX_FILTER_ALLMAC | URTW_RX_FILTER_NICMAC | URTW_RX_FILTER_MCAST | \
+ URTW_RX_FILTER_BCAST | URTW_RX_FILTER_CRCERR | URTW_RX_FILTER_ICVERR | \
+ URTW_RX_FILTER_DATA | URTW_RX_FILTER_CTL | URTW_RX_FILTER_MNG | \
+ (1 << 21) | \
+ URTW_RX_FILTER_PWR | URTW_RX_CHECK_BSSID)
+#define URTW_RX_FILTER_ALLMAC (0x00000001)
+#define URTW_RX_FILTER_NICMAC (0x00000002)
+#define URTW_RX_FILTER_MCAST (0x00000004)
+#define URTW_RX_FILTER_BCAST (0x00000008)
+#define URTW_RX_FILTER_CRCERR (0x00000020)
+#define URTW_RX_FILTER_ICVERR (0x00001000)
+#define URTW_RX_FILTER_DATA (0x00040000)
+#define URTW_RX_FILTER_CTL (0x00080000)
+#define URTW_RX_FILTER_MNG (0x00100000)
+#define URTW_RX_FILTER_PWR (0x00400000)
+#define URTW_RX_CHECK_BSSID (0x00800000)
+#define URTW_RX_FIFO_THRESHOLD_MASK ((1 << 13) | (1 << 14) | (1 << 15))
+#define URTW_RX_FIFO_THRESHOLD_SHIFT (13)
+#define URTW_RX_FIFO_THRESHOLD_128 (3)
+#define URTW_RX_FIFO_THRESHOLD_256 (4)
+#define URTW_RX_FIFO_THRESHOLD_512 (5)
+#define URTW_RX_FIFO_THRESHOLD_1024 (6)
+#define URTW_RX_FIFO_THRESHOLD_NONE (7 << URTW_RX_FIFO_THRESHOLD_SHIFT)
+#define URTW_RX_AUTORESETPHY (1 << URTW_RX_AUTORESETPHY_SHIFT)
+#define URTW_RX_AUTORESETPHY_SHIFT (28)
+#define URTW_MAX_RX_DMA_MASK ((1<<8) | (1<<9) | (1<<10))
+#define URTW_MAX_RX_DMA_2048 (7 << URTW_MAX_RX_DMA_SHIFT)
+#define URTW_MAX_RX_DMA_1024 (6)
+#define URTW_MAX_RX_DMA_SHIFT (10)
+#define URTW_RCR_ONLYERLPKT (1U << 31)
+#define URTW_INT_TIMEOUT 0x0048 /* 4 byte */
+#define URTW_INT_TBDA 0x004c /* 4 byte */
+#define URTW_EPROM_CMD 0x0050 /* 1 byte */
+#define URTW_EPROM_CMD_NORMAL (0x0)
+#define URTW_EPROM_CMD_NORMAL_MODE \
+ (URTW_EPROM_CMD_NORMAL << URTW_EPROM_CMD_SHIFT)
+#define URTW_EPROM_CMD_LOAD (0x1)
+#define URTW_EPROM_CMD_PROGRAM (0x2)
+#define URTW_EPROM_CMD_PROGRAM_MODE \
+ (URTW_EPROM_CMD_PROGRAM << URTW_EPROM_CMD_SHIFT)
+#define URTW_EPROM_CMD_CONFIG (0x3)
+#define URTW_EPROM_CMD_SHIFT (6)
+#define URTW_EPROM_CMD_MASK ((1 << 7) | (1 << 6))
+#define URTW_EPROM_READBIT (0x1)
+#define URTW_EPROM_WRITEBIT (0x2)
+#define URTW_EPROM_CK (0x4)
+#define URTW_EPROM_CS (0x8)
+#define URTW_CONFIG0 0x0051 /* 1 byte */
+#define URTW_CONFIG1 0x0052 /* 1 byte */
+#define URTW_CONFIG2 0x0053 /* 1 byte */
+#define URTW_ANAPARAM 0x0054 /* 4 byte */
+#define URTW_8225_ANAPARAM_ON (0xa0000a59)
+#define URTW_8225_ANAPARAM_OFF (0xa00beb59)
+#define URTW_8187B_8225_ANAPARAM_ON (0x45090658)
+#define URTW_8187B_8225_ANAPARAM_OFF (0x55480658)
+#define URTW_MSR 0x0058 /* 1 byte */
+#define URTW_MSR_LINK_MASK ((1 << 2) | (1 << 3))
+#define URTW_MSR_LINK_SHIFT (2)
+#define URTW_MSR_LINK_NONE (0 << URTW_MSR_LINK_SHIFT)
+#define URTW_MSR_LINK_ADHOC (1 << URTW_MSR_LINK_SHIFT)
+#define URTW_MSR_LINK_STA (2 << URTW_MSR_LINK_SHIFT)
+#define URTW_MSR_LINK_HOSTAP (3 << URTW_MSR_LINK_SHIFT)
+#define URTW_MSR_LINK_ENEDCA (1 << 4)
+#define URTW_CONFIG3 0x0059 /* 1 byte */
+#define URTW_CONFIG3_ANAPARAM_WRITE (0x40)
+#define URTW_CONFIG3_GNT_SELECT (0x80)
+#define URTW_CONFIG3_ANAPARAM_W_SHIFT (6)
+#define URTW_CONFIG4 0x005a /* 1 byte */
+#define URTW_CONFIG4_VCOOFF (1 << 7)
+#define URTW_TESTR 0x005b /* 1 byte */
+#define URTW_PSR 0x005e /* 1 byte */
+#define URTW_SECURITY 0x005f /* 1 byte */
+#define URTW_ANAPARAM2 0x0060 /* 4 byte */
+#define URTW_8225_ANAPARAM2_ON (0x860c7312)
+#define URTW_8225_ANAPARAM2_OFF (0x840dec11)
+#define URTW_8187B_8225_ANAPARAM2_ON (0x727f3f52)
+#define URTW_8187B_8225_ANAPARAM2_OFF (0x72003f50)
+#define URTW_BEACON_INTERVAL 0x0070 /* 2 byte */
+#define URTW_ATIM_WND 0x0072 /* 2 byte */
+#define URTW_BEACON_INTERVAL_TIME 0x0074 /* 2 byte */
+#define URTW_ATIM_TR_ITV 0x0076 /* 2 byte */
+#define URTW_PHY_DELAY 0x0078 /* 1 byte */
+#define URTW_CARRIER_SCOUNT 0x0079 /* 1 byte */
+#define URTW_PHY_MAGIC1 0x007c /* 1 byte */
+#define URTW_PHY_MAGIC2 0x007d /* 1 byte */
+#define URTW_PHY_MAGIC3 0x007e /* 1 byte */
+#define URTW_PHY_MAGIC4 0x007f /* 1 byte */
+#define URTW_RF_PINS_OUTPUT 0x0080 /* 2 byte */
+#define URTW_RF_PINS_OUTPUT_MAGIC1 (0x3a0)
+#define URTW_BB_HOST_BANG_CLK (1 << 1)
+#define URTW_BB_HOST_BANG_EN (1 << 2)
+#define URTW_BB_HOST_BANG_RW (1 << 3)
+#define URTW_RF_PINS_ENABLE 0x0082 /* 2 byte */
+#define URTW_RF_PINS_SELECT 0x0084 /* 2 byte */
+#define URTW_ADDR_MAGIC1 0x0085 /* broken? */
+#define URTW_RF_PINS_INPUT 0x0086 /* 2 byte */
+#define URTW_RF_PINS_MAGIC1 (0xfff3)
+#define URTW_RF_PINS_MAGIC2 (0xfff0)
+#define URTW_RF_PINS_MAGIC3 (0x0007)
+#define URTW_RF_PINS_MAGIC4 (0xf)
+#define URTW_RF_PINS_MAGIC5 (0x0080)
+#define URTW_RF_PARA 0x0088 /* 4 byte */
+#define URTW_RF_TIMING 0x008c /* 4 byte */
+#define URTW_GP_ENABLE 0x0090 /* 1 byte */
+#define URTW_GP_ENABLE_DATA_MAGIC1 (0x1)
+#define URTW_GPIO 0x0091 /* 1 byte */
+#define URTW_GPIO_DATA_MAGIC1 (0x1)
+#define URTW_HSSI_PARA 0x0094 /* 4 byte */
+#define URTW_TX_AGC_CTL 0x009c /* 1 byte */
+#define URTW_TX_AGC_CTL_PERPACKET_GAIN (0x1)
+#define URTW_TX_AGC_CTL_PERPACKET_ANTSEL (0x2)
+#define URTW_TX_AGC_CTL_FEEDBACK_ANT (0x4)
+#define URTW_TX_GAIN_CCK 0x009d /* 1 byte */
+#define URTW_TX_GAIN_OFDM 0x009e /* 1 byte */
+#define URTW_TX_ANTENNA 0x009f /* 1 byte */
+#define URTW_WPA_CONFIG 0x00b0 /* 1 byte */
+#define URTW_SIFS 0x00b4 /* 1 byte */
+#define URTW_DIFS 0x00b5 /* 1 byte */
+#define URTW_SLOT 0x00b6 /* 1 byte */
+#define URTW_CW_CONF 0x00bc /* 1 byte */
+#define URTW_CW_CONF_PERPACKET_RETRY (0x2)
+#define URTW_CW_CONF_PERPACKET_CW (0x1)
+#define URTW_CW_VAL 0x00bd /* 1 byte */
+#define URTW_RATE_FALLBACK 0x00be /* 1 byte */
+#define URTW_RATE_FALLBACK_ENABLE (0x80)
+#define URTW_ACM_CONTROL 0x00bf /* 1 byte */
+#define URTW_CONFIG5 0x00d8 /* 1 byte */
+#define URTW_TXDMA_POLLING 0x00d9 /* 1 byte */
+#define URTW_CWR 0x00dc /* 2 byte */
+#define URTW_RETRY_CTR 0x00de /* 1 byte */
+#define URTW_INT_MIG 0x00e2 /* 2 byte */
+#define URTW_RDSAR 0x00e4 /* 4 byte */
+#define URTW_TID_AC_MAP 0x00e8 /* 2 byte */
+#define URTW_ANAPARAM3 0x00ee /* 1 byte */
+#define URTW_8187B_8225_ANAPARAM3_ON (0x0)
+#define URTW_8187B_8225_ANAPARAM3_OFF (0x0)
+#define URTW_8187B_AC_VO 0x00f0 /* 4 byte for 8187B */
+#define URTW_FEMR 0x00f4 /* 2 byte */
+#define URTW_8187B_AC_VI 0x00f4 /* 4 byte for 8187B */
+#define URTW_8187B_AC_BE 0x00f8 /* 4 byte for 8187B */
+#define URTW_TALLY_CNT 0x00fa /* 2 byte */
+#define URTW_TALLY_SEL 0x00fc /* 1 byte */
+#define URTW_8187B_AC_BK 0x00fc /* 4 byte for 8187B */
+#define URTW_ADDR_MAGIC2 0x00fe /* 2 byte */
+#define URTW_ADDR_MAGIC3 0x00ff /* 1 byte */
+
+/* for 8225 */
+#define URTW_8225_ADDR_0_MAGIC 0x0
+#define URTW_8225_ADDR_0_DATA_MAGIC1 (0x1b7)
+#define URTW_8225_ADDR_0_DATA_MAGIC2 (0x0b7)
+#define URTW_8225_ADDR_0_DATA_MAGIC3 (0x127)
+#define URTW_8225_ADDR_0_DATA_MAGIC4 (0x027)
+#define URTW_8225_ADDR_0_DATA_MAGIC5 (0x22f)
+#define URTW_8225_ADDR_0_DATA_MAGIC6 (0x2bf)
+#define URTW_8225_ADDR_1_MAGIC 0x1
+#define URTW_8225_ADDR_2_MAGIC 0x2
+#define URTW_8225_ADDR_2_DATA_MAGIC1 (0xc4d)
+#define URTW_8225_ADDR_2_DATA_MAGIC2 (0x44d)
+#define URTW_8225_ADDR_3_MAGIC 0x3
+#define URTW_8225_ADDR_3_DATA_MAGIC1 (0x2)
+#define URTW_8225_ADDR_5_MAGIC 0x5
+#define URTW_8225_ADDR_5_DATA_MAGIC1 (0x4)
+#define URTW_8225_ADDR_6_MAGIC 0x6
+#define URTW_8225_ADDR_6_DATA_MAGIC1 (0xe6)
+#define URTW_8225_ADDR_6_DATA_MAGIC2 (0x80)
+#define URTW_8225_ADDR_7_MAGIC 0x7
+#define URTW_8225_ADDR_8_MAGIC 0x8
+#define URTW_8225_ADDR_8_DATA_MAGIC1 (0x588)
+#define URTW_8225_ADDR_9_MAGIC 0x9
+#define URTW_8225_ADDR_9_DATA_MAGIC1 (0x700)
+#define URTW_8225_ADDR_C_MAGIC 0xc
+#define URTW_8225_ADDR_C_DATA_MAGIC1 (0x850)
+#define URTW_8225_ADDR_C_DATA_MAGIC2 (0x050)
+
+/* for EEPROM */
+#define URTW_EPROM_CHANPLAN 0x03
+#define URTW_EPROM_CHANPLAN_BY_HW (0x80)
+#define URTW_EPROM_TXPW_BASE 0x05
+#define URTW_EPROM_RFCHIPID 0x06
+#define URTW_EPROM_RFCHIPID_RTL8225U (5)
+#define URTW_EPROM_RFCHIPID_RTL8225Z2 (6)
+#define URTW_EPROM_MACADDR 0x07
+#define URTW_EPROM_TXPW0 0x16
+#define URTW_EPROM_TXPW2 0x1b
+#define URTW_EPROM_TXPW1 0x3d
+#define URTW_EPROM_SWREV 0x3f
+#define URTW_EPROM_CID_MASK (0xff)
+#define URTW_EPROM_CID_RSVD0 (0x00)
+#define URTW_EPROM_CID_RSVD1 (0xff)
+#define URTW_EPROM_CID_ALPHA0 (0x01)
+#define URTW_EPROM_CID_SERCOMM_PS (0x02)
+#define URTW_EPROM_CID_HW_LED (0x03)
+
+/* LED */
+#define URTW_CID_DEFAULT 0
+#define URTW_CID_8187_ALPHA0 1
+#define URTW_CID_8187_SERCOMM_PS 2
+#define URTW_CID_8187_HW_LED 3
+#define URTW_SW_LED_MODE0 0
+#define URTW_SW_LED_MODE1 1
+#define URTW_SW_LED_MODE2 2
+#define URTW_SW_LED_MODE3 3
+#define URTW_HW_LED 4
+#define URTW_LED_CTL_POWER_ON 0
+#define URTW_LED_CTL_LINK 2
+#define URTW_LED_CTL_TX 4
+#define URTW_LED_PIN_GPIO0 0
+#define URTW_LED_PIN_LED0 1
+#define URTW_LED_PIN_LED1 2
+#define URTW_LED_UNKNOWN 0
+#define URTW_LED_ON 1
+#define URTW_LED_OFF 2
+#define URTW_LED_BLINK_NORMAL 3
+#define URTW_LED_BLINK_SLOWLY 4
+#define URTW_LED_POWER_ON_BLINK 5
+#define URTW_LED_SCAN_BLINK 6
+#define URTW_LED_NO_LINK_BLINK 7
+#define URTW_LED_BLINK_CM3 8
+
+/* for extra area */
+#define URTW_EPROM_DISABLE 0
+#define URTW_EPROM_ENABLE 1
+#define URTW_EPROM_DELAY 10
+#define URTW_8187_GETREGS_REQ 5
+#define URTW_8187_SETREGS_REQ 5
+#define URTW_8225_RF_MAX_SENS 6
+#define URTW_8225_RF_DEF_SENS 4
+#define URTW_DEFAULT_RTS_RETRY 7
+#define URTW_DEFAULT_TX_RETRY 7
+#define URTW_DEFAULT_RTS_THRESHOLD 2342U
+
+#define URTW_ASIFS_TIME 10
+#define URTW_ACKCTS_LEN 14 /* len for ACK and CTS */
+
+struct urtw_8187b_rxhdr {
+ uint32_t flag;
+#define URTW_RX_FLAG_LEN /* 0 ~ 11 bits */
+#define URTW_RX_FLAG_ICV_ERR (1 << 12)
+#define URTW_RX_FLAG_CRC32_ERR (1 << 13)
+#define URTW_RX_FLAG_PM (1 << 14)
+#define URTW_RX_FLAG_RX_ERR (1 << 15)
+#define URTW_RX_FLAG_BCAST (1 << 16)
+#define URTW_RX_FLAG_PAM (1 << 17)
+#define URTW_RX_FLAG_MCAST (1 << 18)
+#define URTW_RX_FLAG_QOS (1 << 19) /* only for RTL8187B */
+#define URTW_RX_FLAG_RXRATE /* 20 ~ 23 bits */
+#define URTW_RX_FLAG_RXRATE_SHIFT 20
+#define URTW_RX_FLAG_TRSW (1 << 24) /* only for RTL8187B */
+#define URTW_RX_FLAG_SPLCP (1 << 25)
+#define URTW_RX_FLAG_FOF (1 << 26)
+#define URTW_RX_FLAG_DMA_FAIL (1 << 27)
+#define URTW_RX_FLAG_LAST (1 << 28)
+#define URTW_RX_FLAG_FIRST (1 << 29)
+#define URTW_RX_FLAG_EOR (1 << 30)
+#define URTW_RX_FLAG_OWN (1U << 31)
+ uint64_t mactime;
+ uint8_t noise;
+ uint8_t rssi;
+#define URTW_RX_RSSI /* 0 ~ 6 bits */
+#define URTW_RX_RSSI_MASK 0x3f
+#define URTW_RX_ANTENNA (1 << 7)
+ uint8_t agc;
+ uint8_t flag2;
+#define URTW_RX_FLAG2_DECRYPTED (1 << 0)
+#define URTW_RX_FLAG2_WAKUP (1 << 1)
+#define URTW_RX_FLAG2_SHIFT (1 << 2)
+#define URTW_RX_FLAG2_RSVD0 /* 3 ~ 7 bits */
+ uint16_t flag3;
+#define URTW_RX_FLAG3_NUMMCSI /* 0 ~ 3 bits */
+#define URTW_RX_FLAG3_SNR_L2E /* 4 ~ 9 bits */
+#define URTW_RX_FLAG3_CFO_BIAS /* 10 ~ 15 bits */
+ int8_t pwdb;
+ uint8_t fot;
+} __packed;
+
+struct urtw_8187b_txhdr {
+ uint32_t flag;
+#define URTW_TX_FLAG_PKTLEN /* 0 ~ 11 bits */
+#define URTW_TX_FLAG_RSVD0 /* 12 ~ 14 bits */
+#define URTW_TX_FLAG_NO_ENC (1 << 15)
+#define URTW_TX_FLAG_SPLCP (1 << 16)
+#define URTW_TX_FLAG_MOREFRAG (1 << 17)
+#define URTW_TX_FLAG_CTS (1 << 18)
+#define URTW_TX_FLAG_RTSRATE /* 19 ~ 22 bits */
+#define URTW_TX_FLAG_RTSRATE_SHIFT 19
+#define URTW_TX_FLAG_RTS (1 << 23)
+#define URTW_TX_FLAG_TXRATE /* 24 ~ 27 bits */
+#define URTW_TX_FLAG_TXRATE_SHIFT 24
+#define URTW_TX_FLAG_LAST (1 << 28)
+#define URTW_TX_FLAG_FIRST (1 << 29)
+#define URTW_TX_FLAG_DMA (1 << 30)
+#define URTW_TX_FLAG_OWN (1U << 31)
+ uint16_t rtsdur;
+ uint16_t len;
+#define URTW_TX_LEN /* 0 ~ 14 bits */
+#define URTW_TX_LEN_EXT (1 << 15)
+ uint32_t bufaddr;
+ uint16_t flag1;
+#define URTW_TX_FLAG1_RXLEN /* 0 ~ 11 bits */
+#define URTW_TX_FLAG1_RSVD0 /* 12 ~ 14 bits */
+#define URTW_TX_FLAG1_MICCAL (1 << 15)
+ uint16_t txdur;
+ uint32_t nextdescaddr;
+ uint8_t rtsagc;
+ uint8_t retry;
+ uint16_t flag2;
+#define URTW_TX_FLAG2_RTDB (1 << 0)
+#define URTW_TX_FLAG2_NOACM (1 << 1)
+#define URTW_TX_FLAG2_PIFS (1 << 2)
+#define URTW_TX_FLAG2_RSVD0 /* 3 ~ 6 bits */
+#define URTW_TX_FLAG2_RTSRATEFALLBACK /* 7 ~ 10 bits */
+#define URTW_TX_FLAG2_RATEFALLBACK /* 11 ~ 15 bits */
+ uint16_t delaybound;
+ uint16_t flag3;
+#define URTW_TX_FLAG3_RSVD0 /* 0 ~ 3 bits */
+#define URTW_TX_FLAG3_AGC /* 4 ~ 11 bits */
+#define URTW_TX_FLAG3_ANTENNA (1 << 12)
+#define URTW_TX_FLAG3_SPC /* 13 ~ 14 bits */
+#define URTW_TX_FLAG3_RSVD1 (1 << 15)
+ uint32_t flag4;
+#define URTW_TX_FLAG4_LENADJUST /* 0 ~ 1 bits */
+#define URTW_TX_FLAG4_RSVD0 (1 << 2)
+#define URTW_TX_FLAG4_TPCDESEN (1 << 3)
+#define URTW_TX_FLAG4_TPCPOLARITY /* 4 ~ 5 bits */
+#define URTW_TX_FLAG4_TPCEN (1 << 6)
+#define URTW_TX_FLAG4_PTEN (1 << 7)
+#define URTW_TX_FLAG4_BCKEY /* 8 ~ 13 bits */
+#define URTW_TX_FLAG4_ENBCKEY (1 << 14)
+#define URTW_TX_FLAG4_ENPMPD (1 << 15)
+#define URTW_TX_FLAG4_FRAGQSZ /* 16 ~ 31 bits */
+} __packed;
+
+struct urtw_8187l_rxhdr {
+ uint32_t flag;
+ uint8_t noise;
+ uint8_t rssi;
+#define URTW_RX_8187L_RSSI /* 0 ~ 6 bits */
+#define URTW_RX_8187L_RSSI_MASK 0x3f
+#define URTW_RX_8187L_ANTENNA (1 << 7)
+ uint8_t agc;
+ uint8_t flag2;
+#define URTW_RX_8187L_DECRYPTED (1 << 0)
+#define URTW_RX_8187L_WAKEUP (1 << 1)
+#define URTW_RX_8187L_SHIFT (1 << 2)
+#define URTW_RX_8187L_RSVD0 /* 3 ~ 7 bits */
+ uint64_t mactime;
+} __packed;
+
+struct urtw_8187l_txhdr {
+ uint32_t flag;
+ uint16_t rtsdur;
+ uint16_t len;
+ uint32_t retry;
+} __packed;
diff --git a/freebsd/sys/dev/usb/wlan/if_urtwvar.h b/freebsd/sys/dev/usb/wlan/if_urtwvar.h
new file mode 100644
index 00000000..08ffc8f3
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_urtwvar.h
@@ -0,0 +1,186 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+enum {
+ URTW_8187B_BULK_RX,
+ URTW_8187B_BULK_TX_STATUS,
+ URTW_8187B_BULK_TX_BE,
+ URTW_8187B_BULK_TX_BK,
+ URTW_8187B_BULK_TX_VI,
+ URTW_8187B_BULK_TX_VO,
+ URTW_8187B_BULK_TX_EP12,
+ URTW_8187B_N_XFERS = 7
+};
+
+enum {
+ URTW_8187L_BULK_RX,
+ URTW_8187L_BULK_TX_LOW,
+ URTW_8187L_BULK_TX_NORMAL,
+ URTW_8187L_N_XFERS = 3
+};
+
+/* XXX no definition at net80211? */
+#define URTW_MAX_CHANNELS 15
+
+struct urtw_data {
+ struct urtw_softc *sc;
+ uint8_t *buf;
+ uint16_t buflen;
+ struct mbuf *m;
+ struct ieee80211_node *ni; /* NB: tx only */
+ STAILQ_ENTRY(urtw_data) next;
+};
+typedef STAILQ_HEAD(, urtw_data) urtw_datahead;
+
+/* XXX not correct.. */
+#define URTW_MIN_RXBUFSZ \
+ (sizeof(struct ieee80211_frame_min))
+
+#define URTW_RX_DATA_LIST_COUNT 4
+#define URTW_TX_DATA_LIST_COUNT 16
+#define URTW_RX_MAXSIZE 0x9c4
+#define URTW_TX_MAXSIZE 0x9c4
+#define URTW_TX_MAXRETRY 11
+
+struct urtw_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_dbm_antsignal;
+} __packed __aligned(8);
+
+#define URTW_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL))
+
+struct urtw_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed __aligned(8);
+
+#define URTW_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct urtw_stats {
+ unsigned int txrates[12];
+};
+
+struct urtw_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define URTW_VAP(vap) ((struct urtw_vap *)(vap))
+
+struct urtw_softc {
+ struct ieee80211com sc_ic;
+ struct mbufq sc_snd;
+ device_t sc_dev;
+ struct usb_device *sc_udev;
+ struct mtx sc_mtx;
+ void *sc_tx_dma_buf;
+
+ int sc_debug;
+ int sc_flags;
+#define URTW_INIT_ONCE (1 << 1)
+#define URTW_RTL8187B (1 << 2)
+#define URTW_RTL8187B_REV_B (1 << 3)
+#define URTW_RTL8187B_REV_D (1 << 4)
+#define URTW_RTL8187B_REV_E (1 << 5)
+#define URTW_DETACHED (1 << 6)
+#define URTW_RUNNING (1 << 7)
+ enum ieee80211_state sc_state;
+
+ int sc_epromtype;
+#define URTW_EEPROM_93C46 0
+#define URTW_EEPROM_93C56 1
+ uint8_t sc_crcmon;
+
+ struct ieee80211_channel *sc_curchan;
+
+ /* for RF */
+ usb_error_t (*sc_rf_init)(struct urtw_softc *);
+ usb_error_t (*sc_rf_set_chan)(struct urtw_softc *,
+ int);
+ usb_error_t (*sc_rf_set_sens)(struct urtw_softc *,
+ int);
+ usb_error_t (*sc_rf_stop)(struct urtw_softc *);
+ uint8_t sc_rfchip;
+ uint32_t sc_max_sens;
+ uint32_t sc_sens;
+ /* for LED */
+ struct usb_callout sc_led_ch;
+ struct task sc_led_task;
+ uint8_t sc_psr;
+ uint8_t sc_strategy;
+#define URTW_LED_GPIO 1
+ uint8_t sc_gpio_ledon;
+ uint8_t sc_gpio_ledinprogress;
+ uint8_t sc_gpio_ledstate;
+ uint8_t sc_gpio_ledpin;
+ uint8_t sc_gpio_blinktime;
+ uint8_t sc_gpio_blinkstate;
+ /* RX/TX */
+ struct usb_xfer *sc_xfer[URTW_8187B_N_XFERS];
+#define URTW_PRIORITY_LOW 0
+#define URTW_PRIORITY_NORMAL 1
+#define URTW_DATA_TIMEOUT 10000 /* 10 sec */
+#define URTW_8187B_TXPIPE_BE 0x6 /* best effort */
+#define URTW_8187B_TXPIPE_BK 0x7 /* background */
+#define URTW_8187B_TXPIPE_VI 0x5 /* video */
+#define URTW_8187B_TXPIPE_VO 0x4 /* voice */
+#define URTW_8187B_TXPIPE_MAX 4
+ struct urtw_data sc_rx[URTW_RX_DATA_LIST_COUNT];
+ urtw_datahead sc_rx_active;
+ urtw_datahead sc_rx_inactive;
+ struct urtw_data sc_tx[URTW_TX_DATA_LIST_COUNT];
+ urtw_datahead sc_tx_active;
+ urtw_datahead sc_tx_inactive;
+ urtw_datahead sc_tx_pending;
+ uint8_t sc_rts_retry;
+ uint8_t sc_tx_retry;
+ uint8_t sc_preamble_mode;
+#define URTW_PREAMBLE_MODE_SHORT 1
+#define URTW_PREAMBLE_MODE_LONG 2
+ struct callout sc_watchdog_ch;
+ int sc_txtimer;
+ int sc_currate;
+ /* TX power */
+ uint8_t sc_txpwr_cck[URTW_MAX_CHANNELS];
+ uint8_t sc_txpwr_cck_base;
+ uint8_t sc_txpwr_ofdm[URTW_MAX_CHANNELS];
+ uint8_t sc_txpwr_ofdm_base;
+
+ uint8_t sc_acmctl;
+ uint64_t sc_txstatus; /* only for 8187B */
+ struct task sc_updateslot_task;
+
+ struct urtw_stats sc_stats;
+
+ struct urtw_rx_radiotap_header sc_rxtap;
+ struct urtw_tx_radiotap_header sc_txtap;
+};
+
+#define URTW_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define URTW_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define URTW_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
diff --git a/freebsd/sys/dev/usb/wlan/if_zyd.c b/freebsd/sys/dev/usb/wlan/if_zyd.c
new file mode 100644
index 00000000..f935bfc9
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_zyd.c
@@ -0,0 +1,2924 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $OpenBSD: if_zyd.c,v 1.52 2007/02/11 00:08:04 jsg Exp $ */
+/* $NetBSD: if_zyd.c,v 1.7 2007/06/21 04:04:29 kiyohara Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ZyDAS ZD1211/ZD1211B USB WLAN driver.
+ */
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_ratectl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#include <dev/usb/wlan/if_zydreg.h>
+#include <dev/usb/wlan/if_zydfw.h>
+
+#ifdef USB_DEBUG
+static int zyd_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd");
+SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RWTUN, &zyd_debug, 0,
+ "zyd debug level");
+
+enum {
+ ZYD_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ ZYD_DEBUG_RECV = 0x00000002, /* basic recv operation */
+ ZYD_DEBUG_RESET = 0x00000004, /* reset processing */
+ ZYD_DEBUG_INIT = 0x00000008, /* device init */
+ ZYD_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */
+ ZYD_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */
+ ZYD_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */
+ ZYD_DEBUG_STAT = 0x00000080, /* statistic */
+ ZYD_DEBUG_FW = 0x00000100, /* firmware */
+ ZYD_DEBUG_CMD = 0x00000200, /* fw commands */
+ ZYD_DEBUG_ANY = 0xffffffff
+};
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (zyd_debug & (m)) \
+ printf("%s: " fmt, __func__, ## __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#endif
+
+#define zyd_do_request(sc,req,data) \
+ usbd_do_request_flags((sc)->sc_udev, &(sc)->sc_mtx, req, data, 0, NULL, 5000)
+
+static device_probe_t zyd_match;
+static device_attach_t zyd_attach;
+static device_detach_t zyd_detach;
+
+static usb_callback_t zyd_intr_read_callback;
+static usb_callback_t zyd_intr_write_callback;
+static usb_callback_t zyd_bulk_read_callback;
+static usb_callback_t zyd_bulk_write_callback;
+
+static struct ieee80211vap *zyd_vap_create(struct ieee80211com *,
+ const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN]);
+static void zyd_vap_delete(struct ieee80211vap *);
+static void zyd_tx_free(struct zyd_tx_data *, int);
+static void zyd_setup_tx_list(struct zyd_softc *);
+static void zyd_unsetup_tx_list(struct zyd_softc *);
+static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int,
+ void *, int, int);
+static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *);
+static int zyd_read32(struct zyd_softc *, uint16_t, uint32_t *);
+static int zyd_write16(struct zyd_softc *, uint16_t, uint16_t);
+static int zyd_write32(struct zyd_softc *, uint16_t, uint32_t);
+static int zyd_rfwrite(struct zyd_softc *, uint32_t);
+static int zyd_lock_phy(struct zyd_softc *);
+static int zyd_unlock_phy(struct zyd_softc *);
+static int zyd_rf_attach(struct zyd_softc *, uint8_t);
+static const char *zyd_rf_name(uint8_t);
+static int zyd_hw_init(struct zyd_softc *);
+static int zyd_read_pod(struct zyd_softc *);
+static int zyd_read_eeprom(struct zyd_softc *);
+static int zyd_get_macaddr(struct zyd_softc *);
+static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *);
+static int zyd_set_bssid(struct zyd_softc *, const uint8_t *);
+static int zyd_switch_radio(struct zyd_softc *, int);
+static int zyd_set_led(struct zyd_softc *, int, int);
+static void zyd_set_multi(struct zyd_softc *);
+static void zyd_update_mcast(struct ieee80211com *);
+static int zyd_set_rxfilter(struct zyd_softc *);
+static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *);
+static int zyd_set_beacon_interval(struct zyd_softc *, int);
+static void zyd_rx_data(struct usb_xfer *, int, uint16_t);
+static int zyd_tx_start(struct zyd_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int zyd_transmit(struct ieee80211com *, struct mbuf *);
+static void zyd_start(struct zyd_softc *);
+static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void zyd_parent(struct ieee80211com *);
+static void zyd_init_locked(struct zyd_softc *);
+static void zyd_stop(struct zyd_softc *);
+static int zyd_loadfirmware(struct zyd_softc *);
+static void zyd_scan_start(struct ieee80211com *);
+static void zyd_scan_end(struct ieee80211com *);
+static void zyd_getradiocaps(struct ieee80211com *, int, int *,
+ struct ieee80211_channel[]);
+static void zyd_set_channel(struct ieee80211com *);
+static int zyd_rfmd_init(struct zyd_rf *);
+static int zyd_rfmd_switch_radio(struct zyd_rf *, int);
+static int zyd_rfmd_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2230_init(struct zyd_rf *);
+static int zyd_al2230_switch_radio(struct zyd_rf *, int);
+static int zyd_al2230_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2230_set_channel_b(struct zyd_rf *, uint8_t);
+static int zyd_al2230_init_b(struct zyd_rf *);
+static int zyd_al7230B_init(struct zyd_rf *);
+static int zyd_al7230B_switch_radio(struct zyd_rf *, int);
+static int zyd_al7230B_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2210_init(struct zyd_rf *);
+static int zyd_al2210_switch_radio(struct zyd_rf *, int);
+static int zyd_al2210_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_gct_init(struct zyd_rf *);
+static int zyd_gct_switch_radio(struct zyd_rf *, int);
+static int zyd_gct_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_gct_mode(struct zyd_rf *);
+static int zyd_gct_set_channel_synth(struct zyd_rf *, int, int);
+static int zyd_gct_write(struct zyd_rf *, uint16_t);
+static int zyd_gct_txgain(struct zyd_rf *, uint8_t);
+static int zyd_maxim2_init(struct zyd_rf *);
+static int zyd_maxim2_switch_radio(struct zyd_rf *, int);
+static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t);
+
+static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY;
+static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB;
+
+/* various supported device vendors/products */
+#define ZYD_ZD1211 0
+#define ZYD_ZD1211B 1
+
+#define ZYD_ZD1211_DEV(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, ZYD_ZD1211) }
+#define ZYD_ZD1211B_DEV(v,p) \
+ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, ZYD_ZD1211B) }
+static const STRUCT_USB_HOST_ID zyd_devs[] = {
+ /* ZYD_ZD1211 */
+ ZYD_ZD1211_DEV(3COM2, 3CRUSB10075),
+ ZYD_ZD1211_DEV(ABOCOM, WL54),
+ ZYD_ZD1211_DEV(ASUS, WL159G),
+ ZYD_ZD1211_DEV(CYBERTAN, TG54USB),
+ ZYD_ZD1211_DEV(DRAYTEK, VIGOR550),
+ ZYD_ZD1211_DEV(PLANEX2, GWUS54GD),
+ ZYD_ZD1211_DEV(PLANEX2, GWUS54GZL),
+ ZYD_ZD1211_DEV(PLANEX3, GWUS54GZ),
+ ZYD_ZD1211_DEV(PLANEX3, GWUS54MINI),
+ ZYD_ZD1211_DEV(SAGEM, XG760A),
+ ZYD_ZD1211_DEV(SENAO, NUB8301),
+ ZYD_ZD1211_DEV(SITECOMEU, WL113),
+ ZYD_ZD1211_DEV(SWEEX, ZD1211),
+ ZYD_ZD1211_DEV(TEKRAM, QUICKWLAN),
+ ZYD_ZD1211_DEV(TEKRAM, ZD1211_1),
+ ZYD_ZD1211_DEV(TEKRAM, ZD1211_2),
+ ZYD_ZD1211_DEV(TWINMOS, G240),
+ ZYD_ZD1211_DEV(UMEDIA, ALL0298V2),
+ ZYD_ZD1211_DEV(UMEDIA, TEW429UB_A),
+ ZYD_ZD1211_DEV(UMEDIA, TEW429UB),
+ ZYD_ZD1211_DEV(WISTRONNEWEB, UR055G),
+ ZYD_ZD1211_DEV(ZCOM, ZD1211),
+ ZYD_ZD1211_DEV(ZYDAS, ZD1211),
+ ZYD_ZD1211_DEV(ZYXEL, AG225H),
+ ZYD_ZD1211_DEV(ZYXEL, ZYAIRG220),
+ ZYD_ZD1211_DEV(ZYXEL, G200V2),
+ /* ZYD_ZD1211B */
+ ZYD_ZD1211B_DEV(ACCTON, SMCWUSBG_NF),
+ ZYD_ZD1211B_DEV(ACCTON, SMCWUSBG),
+ ZYD_ZD1211B_DEV(ACCTON, ZD1211B),
+ ZYD_ZD1211B_DEV(ASUS, A9T_WIFI),
+ ZYD_ZD1211B_DEV(BELKIN, F5D7050_V4000),
+ ZYD_ZD1211B_DEV(BELKIN, ZD1211B),
+ ZYD_ZD1211B_DEV(CISCOLINKSYS, WUSBF54G),
+ ZYD_ZD1211B_DEV(FIBERLINE, WL430U),
+ ZYD_ZD1211B_DEV(MELCO, KG54L),
+ ZYD_ZD1211B_DEV(PHILIPS, SNU5600),
+ ZYD_ZD1211B_DEV(PLANEX2, GW_US54GXS),
+ ZYD_ZD1211B_DEV(SAGEM, XG76NA),
+ ZYD_ZD1211B_DEV(SITECOMEU, ZD1211B),
+ ZYD_ZD1211B_DEV(UMEDIA, TEW429UBC1),
+ ZYD_ZD1211B_DEV(USR, USR5423),
+ ZYD_ZD1211B_DEV(VTECH, ZD1211B),
+ ZYD_ZD1211B_DEV(ZCOM, ZD1211B),
+ ZYD_ZD1211B_DEV(ZYDAS, ZD1211B),
+ ZYD_ZD1211B_DEV(ZYXEL, M202),
+ ZYD_ZD1211B_DEV(ZYXEL, G202),
+ ZYD_ZD1211B_DEV(ZYXEL, G220V2)
+};
+
+static const struct usb_config zyd_config[ZYD_N_TRANSFER] = {
+ [ZYD_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = ZYD_MAX_TXBUFSZ,
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = zyd_bulk_write_callback,
+ .ep_index = 0,
+ .timeout = 10000, /* 10 seconds */
+ },
+ [ZYD_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = ZYX_MAX_RXBUFSZ,
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = zyd_bulk_read_callback,
+ .ep_index = 0,
+ },
+ [ZYD_INTR_WR] = {
+ .type = UE_BULK_INTR,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = sizeof(struct zyd_cmd),
+ .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .callback = zyd_intr_write_callback,
+ .timeout = 1000, /* 1 second */
+ .ep_index = 1,
+ },
+ [ZYD_INTR_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = sizeof(struct zyd_cmd),
+ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .callback = zyd_intr_read_callback,
+ },
+};
+#define zyd_read16_m(sc, val, data) do { \
+ error = zyd_read16(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_write16_m(sc, val, data) do { \
+ error = zyd_write16(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_read32_m(sc, val, data) do { \
+ error = zyd_read32(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_write32_m(sc, val, data) do { \
+ error = zyd_write32(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+
+static int
+zyd_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != ZYD_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa));
+}
+
+static int
+zyd_attach(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ struct zyd_softc *sc = device_get_softc(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t iface_index;
+ int error;
+
+ if (uaa->info.bcdDevice < 0x4330) {
+ device_printf(dev, "device version mismatch: 0x%X "
+ "(only >= 43.30 supported)\n",
+ uaa->info.bcdDevice);
+ return (EINVAL);
+ }
+
+ device_set_usb_desc(dev);
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+ sc->sc_macrev = USB_GET_DRIVER_INFO(uaa);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+ STAILQ_INIT(&sc->sc_rqh);
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ iface_index = ZYD_IFACE_INDEX;
+ error = usbd_transfer_setup(uaa->device,
+ &iface_index, sc->sc_xfer, zyd_config,
+ ZYD_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "could not allocate USB transfers, "
+ "err=%s\n", usbd_errstr(error));
+ goto detach;
+ }
+
+ ZYD_LOCK(sc);
+ if ((error = zyd_get_macaddr(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ ZYD_UNLOCK(sc);
+ goto detach;
+ }
+ ZYD_UNLOCK(sc);
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(dev);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA;
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ zyd_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
+ ic->ic_channels);
+
+ ieee80211_ifattach(ic);
+ ic->ic_raw_xmit = zyd_raw_xmit;
+ ic->ic_scan_start = zyd_scan_start;
+ ic->ic_scan_end = zyd_scan_end;
+ ic->ic_getradiocaps = zyd_getradiocaps;
+ ic->ic_set_channel = zyd_set_channel;
+ ic->ic_vap_create = zyd_vap_create;
+ ic->ic_vap_delete = zyd_vap_delete;
+ ic->ic_update_mcast = zyd_update_mcast;
+ ic->ic_update_promisc = zyd_update_mcast;
+ ic->ic_parent = zyd_parent;
+ ic->ic_transmit = zyd_transmit;
+
+ ieee80211_radiotap_attach(ic,
+ &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
+ ZYD_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ ZYD_RX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return (0);
+
+detach:
+ zyd_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static void
+zyd_drain_mbufq(struct zyd_softc *sc)
+{
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+
+ ZYD_LOCK_ASSERT(sc, MA_OWNED);
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ }
+}
+
+
+static int
+zyd_detach(device_t dev)
+{
+ struct zyd_softc *sc = device_get_softc(dev);
+ struct ieee80211com *ic = &sc->sc_ic;
+ unsigned int x;
+
+ /*
+ * Prevent further allocations from RX/TX data
+ * lists and ioctls:
+ */
+ ZYD_LOCK(sc);
+ sc->sc_flags |= ZYD_FLAG_DETACHED;
+ zyd_drain_mbufq(sc);
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+ ZYD_UNLOCK(sc);
+
+ /* drain USB transfers */
+ for (x = 0; x != ZYD_N_TRANSFER; x++)
+ usbd_transfer_drain(sc->sc_xfer[x]);
+
+ /* free TX list, if any */
+ ZYD_LOCK(sc);
+ zyd_unsetup_tx_list(sc);
+ ZYD_UNLOCK(sc);
+
+ /* free USB transfers and some data buffers */
+ usbd_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER);
+
+ if (ic->ic_softc == sc)
+ ieee80211_ifdetach(ic);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static struct ieee80211vap *
+zyd_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct zyd_vap *zvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return (NULL);
+ zvp = malloc(sizeof(struct zyd_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &zvp->vap;
+
+ /* enable s/w bmiss handling for sta mode */
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) {
+ /* out of memory */
+ free(zvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ /* override state transition machine */
+ zvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = zyd_newstate;
+
+ ieee80211_ratectl_init(vap);
+ ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status, mac);
+ ic->ic_opmode = opmode;
+ return (vap);
+}
+
+static void
+zyd_vap_delete(struct ieee80211vap *vap)
+{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+
+ ieee80211_ratectl_deinit(vap);
+ ieee80211_vap_detach(vap);
+ free(zvp, M_80211_VAP);
+}
+
+static void
+zyd_tx_free(struct zyd_tx_data *data, int txerr)
+{
+ struct zyd_softc *sc = data->sc;
+
+ if (data->m != NULL) {
+ ieee80211_tx_complete(data->ni, data->m, txerr);
+ data->m = NULL;
+ data->ni = NULL;
+ }
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+}
+
+static void
+zyd_setup_tx_list(struct zyd_softc *sc)
+{
+ struct zyd_tx_data *data;
+ int i;
+
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ for (i = 0; i < ZYD_TX_LIST_CNT; i++) {
+ data = &sc->tx_data[i];
+
+ data->sc = sc;
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+ }
+}
+
+static void
+zyd_unsetup_tx_list(struct zyd_softc *sc)
+{
+ struct zyd_tx_data *data;
+ int i;
+
+ /* make sure any subsequent use of the queues will fail */
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ /* free up all node references and mbufs */
+ for (i = 0; i < ZYD_TX_LIST_CNT; i++) {
+ data = &sc->tx_data[i];
+
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static int
+zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct zyd_softc *sc = ic->ic_softc;
+ int error;
+
+ DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ IEEE80211_UNLOCK(ic);
+ ZYD_LOCK(sc);
+ switch (nstate) {
+ case IEEE80211_S_AUTH:
+ zyd_set_chan(sc, ic->ic_curchan);
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_opmode == IEEE80211_M_MONITOR)
+ break;
+
+ /* turn link LED on */
+ error = zyd_set_led(sc, ZYD_LED1, 1);
+ if (error != 0)
+ break;
+
+ /* make data LED blink upon Tx */
+ zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1);
+
+ IEEE80211_ADDR_COPY(sc->sc_bssid, vap->iv_bss->ni_bssid);
+ zyd_set_bssid(sc, sc->sc_bssid);
+ break;
+ default:
+ break;
+ }
+fail:
+ ZYD_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (zvp->newstate(vap, nstate, arg));
+}
+
+/*
+ * Callback handler for interrupt transfer
+ */
+static void
+zyd_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct zyd_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni;
+ struct zyd_cmd *cmd = &sc->sc_ibuf;
+ struct usb_page_cache *pc;
+ int datalen;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, 0, cmd, sizeof(*cmd));
+
+ switch (le16toh(cmd->code)) {
+ case ZYD_NOTIF_RETRYSTATUS:
+ {
+ struct zyd_notif_retry *retry =
+ (struct zyd_notif_retry *)cmd->data;
+
+ DPRINTF(sc, ZYD_DEBUG_TX_PROC,
+ "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n",
+ le16toh(retry->rate), ether_sprintf(retry->macaddr),
+ le16toh(retry->count)&0xff, le16toh(retry->count));
+
+ /*
+ * Find the node to which the packet was sent and
+ * update its retry statistics. In BSS mode, this node
+ * is the AP we're associated to so no lookup is
+ * actually needed.
+ */
+ ni = ieee80211_find_txnode(vap, retry->macaddr);
+ if (ni != NULL) {
+ struct ieee80211_ratectl_tx_status *txs =
+ &sc->sc_txs;
+ int retrycnt =
+ (int)(le16toh(retry->count) & 0xff);
+
+ txs->flags =
+ IEEE80211_RATECTL_STATUS_LONG_RETRY;
+ txs->long_retries = retrycnt;
+ if (le16toh(retry->count) & 0x100) {
+ txs->status =
+ IEEE80211_RATECTL_TX_FAIL_LONG;
+ } else {
+ txs->status =
+ IEEE80211_RATECTL_TX_SUCCESS;
+ }
+
+
+ ieee80211_ratectl_tx_complete(ni, txs);
+ ieee80211_free_node(ni);
+ }
+ if (le16toh(retry->count) & 0x100)
+ /* too many retries */
+ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS,
+ 1);
+ break;
+ }
+ case ZYD_NOTIF_IORD:
+ {
+ struct zyd_rq *rqp;
+
+ if (le16toh(*(uint16_t *)cmd->data) == ZYD_CR_INTERRUPT)
+ break; /* HMAC interrupt */
+
+ datalen = actlen - sizeof(cmd->code);
+ datalen -= 2; /* XXX: padding? */
+
+ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) {
+ int i;
+ int count;
+
+ if (rqp->olen != datalen)
+ continue;
+ count = rqp->olen / sizeof(struct zyd_pair);
+ for (i = 0; i < count; i++) {
+ if (*(((const uint16_t *)rqp->idata) + i) !=
+ (((struct zyd_pair *)cmd->data) + i)->reg)
+ break;
+ }
+ if (i != count)
+ continue;
+ /* copy answer into caller-supplied buffer */
+ memcpy(rqp->odata, cmd->data, rqp->olen);
+ DPRINTF(sc, ZYD_DEBUG_CMD,
+ "command %p complete, data = %*D \n",
+ rqp, rqp->olen, (char *)rqp->odata, ":");
+ wakeup(rqp); /* wakeup caller */
+ break;
+ }
+ if (rqp == NULL) {
+ device_printf(sc->sc_dev,
+ "unexpected IORD notification %*D\n",
+ datalen, cmd->data, ":");
+ }
+ break;
+ }
+ default:
+ device_printf(sc->sc_dev, "unknown notification %x\n",
+ le16toh(cmd->code));
+ }
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+ break;
+
+ default: /* Error */
+ DPRINTF(sc, ZYD_DEBUG_CMD, "error = %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+zyd_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct zyd_softc *sc = usbd_xfer_softc(xfer);
+ struct zyd_rq *rqp, *cmd;
+ struct usb_page_cache *pc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ cmd = usbd_xfer_get_priv(xfer);
+ DPRINTF(sc, ZYD_DEBUG_CMD, "command %p transferred\n", cmd);
+ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) {
+ /* Ensure the cached rq pointer is still valid */
+ if (rqp == cmd &&
+ (rqp->flags & ZYD_CMD_FLAG_READ) == 0)
+ wakeup(rqp); /* wakeup caller */
+ }
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) {
+ if (rqp->flags & ZYD_CMD_FLAG_SENT)
+ continue;
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, rqp->cmd, rqp->ilen);
+
+ usbd_xfer_set_frame_len(xfer, 0, rqp->ilen);
+ usbd_xfer_set_priv(xfer, rqp);
+ rqp->flags |= ZYD_CMD_FLAG_SENT;
+ usbd_transfer_submit(xfer);
+ break;
+ }
+ break;
+
+ default: /* Error */
+ DPRINTF(sc, ZYD_DEBUG_ANY, "error = %s\n",
+ usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static int
+zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen,
+ void *odata, int olen, int flags)
+{
+ struct zyd_cmd cmd;
+ struct zyd_rq rq;
+ int error;
+
+ if (ilen > (int)sizeof(cmd.data))
+ return (EINVAL);
+
+ cmd.code = htole16(code);
+ memcpy(cmd.data, idata, ilen);
+ DPRINTF(sc, ZYD_DEBUG_CMD, "sending cmd %p = %*D\n",
+ &rq, ilen, idata, ":");
+
+ rq.cmd = &cmd;
+ rq.idata = idata;
+ rq.odata = odata;
+ rq.ilen = sizeof(uint16_t) + ilen;
+ rq.olen = olen;
+ rq.flags = flags;
+ STAILQ_INSERT_TAIL(&sc->sc_rqh, &rq, rq);
+ usbd_transfer_start(sc->sc_xfer[ZYD_INTR_RD]);
+ usbd_transfer_start(sc->sc_xfer[ZYD_INTR_WR]);
+
+ /* wait at most one second for command reply */
+ error = mtx_sleep(&rq, &sc->sc_mtx, 0 , "zydcmd", hz);
+ if (error)
+ device_printf(sc->sc_dev, "command timeout\n");
+ STAILQ_REMOVE(&sc->sc_rqh, &rq, zyd_rq, rq);
+ DPRINTF(sc, ZYD_DEBUG_CMD, "finsihed cmd %p, error = %d \n",
+ &rq, error);
+
+ return (error);
+}
+
+static int
+zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val)
+{
+ struct zyd_pair tmp;
+ int error;
+
+ reg = htole16(reg);
+ error = zyd_cmd(sc, ZYD_CMD_IORD, &reg, sizeof(reg), &tmp, sizeof(tmp),
+ ZYD_CMD_FLAG_READ);
+ if (error == 0)
+ *val = le16toh(tmp.val);
+ return (error);
+}
+
+static int
+zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val)
+{
+ struct zyd_pair tmp[2];
+ uint16_t regs[2];
+ int error;
+
+ regs[0] = htole16(ZYD_REG32_HI(reg));
+ regs[1] = htole16(ZYD_REG32_LO(reg));
+ error = zyd_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp),
+ ZYD_CMD_FLAG_READ);
+ if (error == 0)
+ *val = le16toh(tmp[0].val) << 16 | le16toh(tmp[1].val);
+ return (error);
+}
+
+static int
+zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct zyd_pair pair;
+
+ pair.reg = htole16(reg);
+ pair.val = htole16(val);
+
+ return zyd_cmd(sc, ZYD_CMD_IOWR, &pair, sizeof(pair), NULL, 0, 0);
+}
+
+static int
+zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val)
+{
+ struct zyd_pair pair[2];
+
+ pair[0].reg = htole16(ZYD_REG32_HI(reg));
+ pair[0].val = htole16(val >> 16);
+ pair[1].reg = htole16(ZYD_REG32_LO(reg));
+ pair[1].val = htole16(val & 0xffff);
+
+ return zyd_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0);
+}
+
+static int
+zyd_rfwrite(struct zyd_softc *sc, uint32_t val)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+ struct zyd_rfwrite_cmd req;
+ uint16_t cr203;
+ int error, i;
+
+ zyd_read16_m(sc, ZYD_CR203, &cr203);
+ cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA);
+
+ req.code = htole16(2);
+ req.width = htole16(rf->width);
+ for (i = 0; i < rf->width; i++) {
+ req.bit[i] = htole16(cr203);
+ if (val & (1 << (rf->width - 1 - i)))
+ req.bit[i] |= htole16(ZYD_RF_DATA);
+ }
+ error = zyd_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + 2 * rf->width, NULL, 0, 0);
+fail:
+ return (error);
+}
+
+static int
+zyd_rfwrite_cr(struct zyd_softc *sc, uint32_t val)
+{
+ int error;
+
+ zyd_write16_m(sc, ZYD_CR244, (val >> 16) & 0xff);
+ zyd_write16_m(sc, ZYD_CR243, (val >> 8) & 0xff);
+ zyd_write16_m(sc, ZYD_CR242, (val >> 0) & 0xff);
+fail:
+ return (error);
+}
+
+static int
+zyd_lock_phy(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_MISC, &tmp);
+ tmp &= ~ZYD_UNLOCK_PHY_REGS;
+ zyd_write32_m(sc, ZYD_MAC_MISC, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_unlock_phy(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_MISC, &tmp);
+ tmp |= ZYD_UNLOCK_PHY_REGS;
+ zyd_write32_m(sc, ZYD_MAC_MISC, tmp);
+fail:
+ return (error);
+}
+
+/*
+ * RFMD RF methods.
+ */
+static int
+zyd_rfmd_init(struct zyd_rf *rf)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY;
+ static const uint32_t rfini[] = ZYD_RFMD_RF;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < nitems(phyini); i++) {
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+ }
+
+ /* init RFMD radio */
+ for (i = 0; i < nitems(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+fail:
+ return (error);
+}
+
+static int
+zyd_rfmd_switch_radio(struct zyd_rf *rf, int on)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+
+ zyd_write16_m(sc, ZYD_CR10, on ? 0x89 : 0x15);
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x81);
+fail:
+ return (error);
+}
+
+static int
+zyd_rfmd_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_RFMD_CHANTABLE;
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+
+fail:
+ return (error);
+}
+
+/*
+ * AL2230 RF methods.
+ */
+static int
+zyd_al2230_init(struct zyd_rf *rf)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY;
+ static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT;
+ static const struct zyd_phy_pair phypll[] = {
+ { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f },
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 }
+ };
+ static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1;
+ static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2;
+ static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < nitems(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) {
+ for (i = 0; i < nitems(phy2230s); i++)
+ zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val);
+ }
+
+ /* init AL2230 radio */
+ for (i = 0; i < nitems(rfini1); i++) {
+ error = zyd_rfwrite(sc, rfini1[i]);
+ if (error != 0)
+ goto fail;
+ }
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0)
+ error = zyd_rfwrite(sc, 0x000824);
+ else
+ error = zyd_rfwrite(sc, 0x0005a4);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < nitems(rfini2); i++) {
+ error = zyd_rfwrite(sc, rfini2[i]);
+ if (error != 0)
+ goto fail;
+ }
+
+ for (i = 0; i < nitems(phypll); i++)
+ zyd_write16_m(sc, phypll[i].reg, phypll[i].val);
+
+ for (i = 0; i < nitems(rfini3); i++) {
+ error = zyd_rfwrite(sc, rfini3[i]);
+ if (error != 0)
+ goto fail;
+ }
+fail:
+ return (error);
+}
+
+static int
+zyd_al2230_fini(struct zyd_rf *rf)
+{
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1;
+
+ for (i = 0; i < nitems(phy); i++)
+ zyd_write16_m(sc, phy[i].reg, phy[i].val);
+
+ if (sc->sc_newphy != 0)
+ zyd_write16_m(sc, ZYD_CR9, 0xe1);
+
+ zyd_write16_m(sc, ZYD_CR203, 0x6);
+fail:
+ return (error);
+}
+
+static int
+zyd_al2230_init_b(struct zyd_rf *rf)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1;
+ static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2;
+ static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3;
+ static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B;
+ static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1;
+ static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2;
+ static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3;
+ static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE;
+ int i, error;
+
+ for (i = 0; i < nitems(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < nitems(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) {
+ for (i = 0; i < nitems(phy2230s); i++)
+ zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val);
+ }
+
+ for (i = 0; i < 3; i++) {
+ error = zyd_rfwrite_cr(sc, zyd_al2230_chtable[0][i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < nitems(rfini_part1); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part1[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0)
+ error = zyd_rfwrite(sc, 0x241000);
+ else
+ error = zyd_rfwrite(sc, 0x25a000);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < nitems(rfini_part2); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part2[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < nitems(phy2); i++)
+ zyd_write16_m(sc, phy2[i].reg, phy2[i].val);
+
+ for (i = 0; i < nitems(rfini_part3); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part3[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < nitems(phy3); i++)
+ zyd_write16_m(sc, phy3[i].reg, phy3[i].val);
+
+ error = zyd_al2230_fini(rf);
+fail:
+ return (error);
+}
+
+static int
+zyd_al2230_switch_radio(struct zyd_rf *rf, int on)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ int error, on251 = (sc->sc_macrev == ZYD_ZD1211) ? 0x3f : 0x7f;
+
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04);
+ zyd_write16_m(sc, ZYD_CR251, on ? on251 : 0x2f);
+fail:
+ return (error);
+}
+
+static int
+zyd_al2230_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = {
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 },
+ };
+ static const struct {
+ uint32_t r1, r2, r3;
+ } rfprog[] = ZYD_AL2230_CHANTABLE;
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r3);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < nitems(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+fail:
+ return (error);
+}
+
+static int
+zyd_al2230_set_channel_b(struct zyd_rf *rf, uint8_t chan)
+{
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1;
+ static const struct {
+ uint32_t r1, r2, r3;
+ } rfprog[] = ZYD_AL2230_CHANTABLE_B;
+
+ for (i = 0; i < nitems(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r3);
+ if (error != 0)
+ goto fail;
+ error = zyd_al2230_fini(rf);
+fail:
+ return (error);
+}
+
+#define ZYD_AL2230_PHY_BANDEDGE6 \
+{ \
+ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \
+ { ZYD_CR47, 0x1e } \
+}
+
+static int
+zyd_al2230_bandedge6(struct zyd_rf *rf, struct ieee80211_channel *c)
+{
+ int error = 0, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6;
+ int chan = ieee80211_chan2ieee(ic, c);
+
+ if (chan == 1 || chan == 11)
+ r[0].val = 0x12;
+
+ for (i = 0; i < nitems(r); i++)
+ zyd_write16_m(sc, r[i].reg, r[i].val);
+fail:
+ return (error);
+}
+
+/*
+ * AL7230B RF methods.
+ */
+static int
+zyd_al7230B_init(struct zyd_rf *rf)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1;
+ static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2;
+ static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3;
+ static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1;
+ static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2;
+ int i, error;
+
+ /* for AL7230B, PHY and RF need to be initialized in "phases" */
+
+ /* init RF-dependent PHY registers, part one */
+ for (i = 0; i < nitems(phyini_1); i++)
+ zyd_write16_m(sc, phyini_1[i].reg, phyini_1[i].val);
+
+ /* init AL7230B radio, part one */
+ for (i = 0; i < nitems(rfini_1); i++) {
+ if ((error = zyd_rfwrite(sc, rfini_1[i])) != 0)
+ return (error);
+ }
+ /* init RF-dependent PHY registers, part two */
+ for (i = 0; i < nitems(phyini_2); i++)
+ zyd_write16_m(sc, phyini_2[i].reg, phyini_2[i].val);
+
+ /* init AL7230B radio, part two */
+ for (i = 0; i < nitems(rfini_2); i++) {
+ if ((error = zyd_rfwrite(sc, rfini_2[i])) != 0)
+ return (error);
+ }
+ /* init RF-dependent PHY registers, part three */
+ for (i = 0; i < nitems(phyini_3); i++)
+ zyd_write16_m(sc, phyini_3[i].reg, phyini_3[i].val);
+fail:
+ return (error);
+}
+
+static int
+zyd_al7230B_switch_radio(struct zyd_rf *rf, int on)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04);
+ zyd_write16_m(sc, ZYD_CR251, on ? 0x3f : 0x2f);
+fail:
+ return (error);
+}
+
+static int
+zyd_al7230B_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_AL7230B_CHANTABLE;
+ static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL;
+ int i, error;
+
+ zyd_write16_m(sc, ZYD_CR240, 0x57);
+ zyd_write16_m(sc, ZYD_CR251, 0x2f);
+
+ for (i = 0; i < nitems(rfsc); i++) {
+ if ((error = zyd_rfwrite(sc, rfsc[i])) != 0)
+ return (error);
+ }
+
+ zyd_write16_m(sc, ZYD_CR128, 0x14);
+ zyd_write16_m(sc, ZYD_CR129, 0x12);
+ zyd_write16_m(sc, ZYD_CR130, 0x10);
+ zyd_write16_m(sc, ZYD_CR38, 0x38);
+ zyd_write16_m(sc, ZYD_CR136, 0xdf);
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, 0x3c9000);
+ if (error != 0)
+ goto fail;
+
+ zyd_write16_m(sc, ZYD_CR251, 0x3f);
+ zyd_write16_m(sc, ZYD_CR203, 0x06);
+ zyd_write16_m(sc, ZYD_CR240, 0x08);
+fail:
+ return (error);
+}
+
+/*
+ * AL2210 RF methods.
+ */
+static int
+zyd_al2210_init(struct zyd_rf *rf)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY;
+ static const uint32_t rfini[] = ZYD_AL2210_RF;
+ uint32_t tmp;
+ int i, error;
+
+ zyd_write32_m(sc, ZYD_CR18, 2);
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < nitems(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ /* init AL2210 radio */
+ for (i = 0; i < nitems(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_write32_m(sc, ZYD_CR18, 3);
+fail:
+ return (error);
+}
+
+static int
+zyd_al2210_switch_radio(struct zyd_rf *rf, int on)
+{
+ /* vendor driver does nothing for this RF chip */
+
+ return (0);
+}
+
+static int
+zyd_al2210_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE;
+ uint32_t tmp;
+
+ zyd_write32_m(sc, ZYD_CR18, 2);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+
+ /* actually set the channel */
+ error = zyd_rfwrite(sc, rfprog[chan - 1]);
+ if (error != 0)
+ goto fail;
+
+ zyd_write32_m(sc, ZYD_CR18, 3);
+fail:
+ return (error);
+}
+
+/*
+ * GCT RF methods.
+ */
+static int
+zyd_gct_init(struct zyd_rf *rf)
+{
+#define ZYD_GCT_INTR_REG 0x85c1
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY;
+ static const uint32_t rfini[] = ZYD_GCT_RF;
+ static const uint16_t vco[11][7] = ZYD_GCT_VCO;
+ int i, idx = -1, error;
+ uint16_t data;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < nitems(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ /* init cgt radio */
+ for (i = 0; i < nitems(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+
+ error = zyd_gct_mode(rf);
+ if (error != 0)
+ return (error);
+
+ for (i = 0; i < (int)(nitems(vco) - 1); i++) {
+ error = zyd_gct_set_channel_synth(rf, 1, 0);
+ if (error != 0)
+ goto fail;
+ error = zyd_gct_write(rf, vco[i][0]);
+ if (error != 0)
+ goto fail;
+ zyd_write16_m(sc, ZYD_GCT_INTR_REG, 0xf);
+ zyd_read16_m(sc, ZYD_GCT_INTR_REG, &data);
+ if ((data & 0xf) == 0) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1) {
+ error = zyd_gct_set_channel_synth(rf, 1, 1);
+ if (error != 0)
+ goto fail;
+ error = zyd_gct_write(rf, 0x6662);
+ if (error != 0)
+ goto fail;
+ }
+
+ rf->idx = idx;
+ zyd_write16_m(sc, ZYD_CR203, 0x6);
+fail:
+ return (error);
+#undef ZYD_GCT_INTR_REG
+}
+
+static int
+zyd_gct_mode(struct zyd_rf *rf)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const uint32_t mode[] = {
+ 0x25f98, 0x25f9a, 0x25f94, 0x27fd4
+ };
+ int i, error;
+
+ for (i = 0; i < nitems(mode); i++) {
+ if ((error = zyd_rfwrite(sc, mode[i])) != 0)
+ break;
+ }
+ return (error);
+}
+
+static int
+zyd_gct_set_channel_synth(struct zyd_rf *rf, int chan, int acal)
+{
+ int error, idx = chan - 1;
+ struct zyd_softc *sc = rf->rf_sc;
+ static uint32_t acal_synth[] = ZYD_GCT_CHANNEL_ACAL;
+ static uint32_t std_synth[] = ZYD_GCT_CHANNEL_STD;
+ static uint32_t div_synth[] = ZYD_GCT_CHANNEL_DIV;
+
+ error = zyd_rfwrite(sc,
+ (acal == 1) ? acal_synth[idx] : std_synth[idx]);
+ if (error != 0)
+ return (error);
+ return zyd_rfwrite(sc, div_synth[idx]);
+}
+
+static int
+zyd_gct_write(struct zyd_rf *rf, uint16_t value)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+
+ return zyd_rfwrite(sc, 0x300000 | 0x40000 | value);
+}
+
+static int
+zyd_gct_switch_radio(struct zyd_rf *rf, int on)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+
+ error = zyd_rfwrite(sc, on ? 0x25f94 : 0x25f90);
+ if (error != 0)
+ return (error);
+
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04);
+ zyd_write16_m(sc, ZYD_CR251,
+ on ? ((sc->sc_macrev == ZYD_ZD1211B) ? 0x7f : 0x3f) : 0x2f);
+fail:
+ return (error);
+}
+
+static int
+zyd_gct_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair cmd[] = {
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 },
+ { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 },
+ };
+ static const uint16_t vco[11][7] = ZYD_GCT_VCO;
+
+ error = zyd_gct_set_channel_synth(rf, chan, 0);
+ if (error != 0)
+ goto fail;
+ error = zyd_gct_write(rf, (rf->idx == -1) ? 0x6662 :
+ vco[rf->idx][((chan - 1) / 2)]);
+ if (error != 0)
+ goto fail;
+ error = zyd_gct_mode(rf);
+ if (error != 0)
+ return (error);
+ for (i = 0; i < nitems(cmd); i++)
+ zyd_write16_m(sc, cmd[i].reg, cmd[i].val);
+ error = zyd_gct_txgain(rf, chan);
+ if (error != 0)
+ return (error);
+ zyd_write16_m(sc, ZYD_CR203, 0x6);
+fail:
+ return (error);
+}
+
+static int
+zyd_gct_txgain(struct zyd_rf *rf, uint8_t chan)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static uint32_t txgain[] = ZYD_GCT_TXGAIN;
+ uint8_t idx = sc->sc_pwrint[chan - 1];
+
+ if (idx >= nitems(txgain)) {
+ device_printf(sc->sc_dev, "could not set TX gain (%d %#x)\n",
+ chan, idx);
+ return 0;
+ }
+
+ return zyd_rfwrite(sc, 0x700000 | txgain[idx]);
+}
+
+/*
+ * Maxim2 RF methods.
+ */
+static int
+zyd_maxim2_init(struct zyd_rf *rf)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM2_RF;
+ uint16_t tmp;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < nitems(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* init maxim2 radio */
+ for (i = 0; i < nitems(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+}
+
+static int
+zyd_maxim2_switch_radio(struct zyd_rf *rf, int on)
+{
+
+ /* vendor driver does nothing for this RF chip */
+ return (0);
+}
+
+static int
+zyd_maxim2_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM2_RF;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_MAXIM2_CHANTABLE;
+ uint16_t tmp;
+ int i, error;
+
+ /*
+ * Do the same as we do when initializing it, except for the channel
+ * values coming from the two channel tables.
+ */
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < nitems(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* first two values taken from the chantables */
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+
+ /* init maxim2 radio - skipping the two first values */
+ for (i = 2; i < nitems(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+}
+
+static int
+zyd_rf_attach(struct zyd_softc *sc, uint8_t type)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+
+ rf->rf_sc = sc;
+ rf->update_pwr = 1;
+
+ switch (type) {
+ case ZYD_RF_RFMD:
+ rf->init = zyd_rfmd_init;
+ rf->switch_radio = zyd_rfmd_switch_radio;
+ rf->set_channel = zyd_rfmd_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL2230:
+ case ZYD_RF_AL2230S:
+ if (sc->sc_macrev == ZYD_ZD1211B) {
+ rf->init = zyd_al2230_init_b;
+ rf->set_channel = zyd_al2230_set_channel_b;
+ } else {
+ rf->init = zyd_al2230_init;
+ rf->set_channel = zyd_al2230_set_channel;
+ }
+ rf->switch_radio = zyd_al2230_switch_radio;
+ rf->bandedge6 = zyd_al2230_bandedge6;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL7230B:
+ rf->init = zyd_al7230B_init;
+ rf->switch_radio = zyd_al7230B_switch_radio;
+ rf->set_channel = zyd_al7230B_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL2210:
+ rf->init = zyd_al2210_init;
+ rf->switch_radio = zyd_al2210_switch_radio;
+ rf->set_channel = zyd_al2210_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_MAXIM_NEW:
+ case ZYD_RF_GCT:
+ rf->init = zyd_gct_init;
+ rf->switch_radio = zyd_gct_switch_radio;
+ rf->set_channel = zyd_gct_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ rf->update_pwr = 0;
+ break;
+ case ZYD_RF_MAXIM_NEW2:
+ rf->init = zyd_maxim2_init;
+ rf->switch_radio = zyd_maxim2_switch_radio;
+ rf->set_channel = zyd_maxim2_set_channel;
+ rf->width = 18; /* 18-bit RF values */
+ break;
+ default:
+ device_printf(sc->sc_dev,
+ "sorry, radio \"%s\" is not supported yet\n",
+ zyd_rf_name(type));
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static const char *
+zyd_rf_name(uint8_t type)
+{
+ static const char * const zyd_rfs[] = {
+ "unknown", "unknown", "UW2451", "UCHIP", "AL2230",
+ "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT",
+ "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2",
+ "PHILIPS"
+ };
+
+ return zyd_rfs[(type > 15) ? 0 : type];
+}
+
+static int
+zyd_hw_init(struct zyd_softc *sc)
+{
+ int error;
+ const struct zyd_phy_pair *phyp;
+ struct zyd_rf *rf = &sc->sc_rf;
+ uint16_t val;
+
+ /* specify that the plug and play is finished */
+ zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1);
+ zyd_read16_m(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_fwbase);
+ DPRINTF(sc, ZYD_DEBUG_FW, "firmware base address=0x%04x\n",
+ sc->sc_fwbase);
+
+ /* retrieve firmware revision number */
+ zyd_read16_m(sc, sc->sc_fwbase + ZYD_FW_FIRMWARE_REV, &sc->sc_fwrev);
+ zyd_write32_m(sc, ZYD_CR_GPI_EN, 0);
+ zyd_write32_m(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f);
+ /* set mandatory rates - XXX assumes 802.11b/g */
+ zyd_write32_m(sc, ZYD_MAC_MAN_RATE, 0x150f);
+
+ /* disable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0);
+
+ if ((error = zyd_read_pod(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ goto fail;
+ }
+
+ /* PHY init (resetting) */
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ phyp = (sc->sc_macrev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy;
+ for (; phyp->reg != 0; phyp++)
+ zyd_write16_m(sc, phyp->reg, phyp->val);
+ if (sc->sc_macrev == ZYD_ZD1211 && sc->sc_fix_cr157 != 0) {
+ zyd_read16_m(sc, ZYD_EEPROM_PHY_REG, &val);
+ zyd_write32_m(sc, ZYD_CR157, val >> 8);
+ }
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ /* HMAC init */
+ zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000020);
+ zyd_write32_m(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808);
+ zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_GHTBL, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_GHTBH, 0x80000000);
+ zyd_write32_m(sc, ZYD_MAC_MISC, 0x000000a4);
+ zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f);
+ zyd_write32_m(sc, ZYD_MAC_BCNCFG, 0x00f00401);
+ zyd_write32_m(sc, ZYD_MAC_PHY_DELAY2, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000080);
+ zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100);
+ zyd_write32_m(sc, ZYD_CR_RX_PE_DELAY, 0x00000070);
+ zyd_write32_m(sc, ZYD_CR_PS_CTRL, 0x10000000);
+ zyd_write32_m(sc, ZYD_MAC_RTSCTSRATE, 0x02030203);
+ zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1);
+ zyd_write32_m(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114);
+ zyd_write32_m(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032);
+ zyd_write32_m(sc, ZYD_MAC_CAM_MODE, 0x3);
+
+ if (sc->sc_macrev == ZYD_ZD1211) {
+ zyd_write32_m(sc, ZYD_MAC_RETRY, 0x00000002);
+ zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640);
+ } else {
+ zyd_write32_m(sc, ZYD_MACB_MAX_RETRY, 0x02020202);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f);
+ zyd_write32_m(sc, ZYD_MACB_AIFS_CTL1, 0x00280028);
+ zyd_write32_m(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C);
+ zyd_write32_m(sc, ZYD_MACB_TXOP, 0x01800824);
+ zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff);
+ }
+
+ /* init beacon interval to 100ms */
+ if ((error = zyd_set_beacon_interval(sc, 100)) != 0)
+ goto fail;
+
+ if ((error = zyd_rf_attach(sc, sc->sc_rfrev)) != 0) {
+ device_printf(sc->sc_dev, "could not attach RF, rev 0x%x\n",
+ sc->sc_rfrev);
+ goto fail;
+ }
+
+ /* RF chip init */
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ error = (*rf->init)(rf);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "radio initialization failed, error %d\n", error);
+ goto fail;
+ }
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ if ((error = zyd_read_eeprom(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ goto fail;
+ }
+
+fail: return (error);
+}
+
+static int
+zyd_read_pod(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_EEPROM_POD, &tmp);
+ sc->sc_rfrev = tmp & 0x0f;
+ sc->sc_ledtype = (tmp >> 4) & 0x01;
+ sc->sc_al2230s = (tmp >> 7) & 0x01;
+ sc->sc_cckgain = (tmp >> 8) & 0x01;
+ sc->sc_fix_cr157 = (tmp >> 13) & 0x01;
+ sc->sc_parev = (tmp >> 16) & 0x0f;
+ sc->sc_bandedge6 = (tmp >> 21) & 0x01;
+ sc->sc_newphy = (tmp >> 31) & 0x01;
+ sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1;
+fail:
+ return (error);
+}
+
+static int
+zyd_read_eeprom(struct zyd_softc *sc)
+{
+ uint16_t val;
+ int error, i;
+
+ /* read Tx power calibration tables */
+ for (i = 0; i < 7; i++) {
+ zyd_read16_m(sc, ZYD_EEPROM_PWR_CAL + i, &val);
+ sc->sc_pwrcal[i * 2] = val >> 8;
+ sc->sc_pwrcal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_PWR_INT + i, &val);
+ sc->sc_pwrint[i * 2] = val >> 8;
+ sc->sc_pwrint[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_36M_CAL + i, &val);
+ sc->sc_ofdm36_cal[i * 2] = val >> 8;
+ sc->sc_ofdm36_cal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_48M_CAL + i, &val);
+ sc->sc_ofdm48_cal[i * 2] = val >> 8;
+ sc->sc_ofdm48_cal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_54M_CAL + i, &val);
+ sc->sc_ofdm54_cal[i * 2] = val >> 8;
+ sc->sc_ofdm54_cal[i * 2 + 1] = val & 0xff;
+ }
+fail:
+ return (error);
+}
+
+static int
+zyd_get_macaddr(struct zyd_softc *sc)
+{
+ struct usb_device_request req;
+ usb_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = ZYD_READFWDATAREQ;
+ USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, IEEE80211_ADDR_LEN);
+
+ error = zyd_do_request(sc, &req, sc->sc_ic.ic_macaddr);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usbd_errstr(error));
+ }
+
+ return (error);
+}
+
+static int
+zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr)
+{
+ int error;
+ uint32_t tmp;
+
+ tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0];
+ zyd_write32_m(sc, ZYD_MAC_MACADRL, tmp);
+ tmp = addr[5] << 8 | addr[4];
+ zyd_write32_m(sc, ZYD_MAC_MACADRH, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_set_bssid(struct zyd_softc *sc, const uint8_t *addr)
+{
+ int error;
+ uint32_t tmp;
+
+ tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0];
+ zyd_write32_m(sc, ZYD_MAC_BSSADRL, tmp);
+ tmp = addr[5] << 8 | addr[4];
+ zyd_write32_m(sc, ZYD_MAC_BSSADRH, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_switch_radio(struct zyd_softc *sc, int on)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+ int error;
+
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ error = (*rf->switch_radio)(rf, on);
+ if (error != 0)
+ goto fail;
+ error = zyd_unlock_phy(sc);
+fail:
+ return (error);
+}
+
+static int
+zyd_set_led(struct zyd_softc *sc, int which, int on)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_TX_PE_CONTROL, &tmp);
+ tmp &= ~which;
+ if (on)
+ tmp |= which;
+ zyd_write32_m(sc, ZYD_MAC_TX_PE_CONTROL, tmp);
+fail:
+ return (error);
+}
+
+static void
+zyd_set_multi(struct zyd_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t low, high;
+ int error;
+
+ if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0)
+ return;
+
+ low = 0x00000000;
+ high = 0x80000000;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_allmulti > 0 ||
+ ic->ic_promisc > 0) {
+ low = 0xffffffff;
+ high = 0xffffffff;
+ } else {
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ uint8_t v;
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ ifp = vap->iv_ifp;
+ if_maddr_rlock(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ v = ((uint8_t *)LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr))[5] >> 2;
+ if (v < 32)
+ low |= 1 << v;
+ else
+ high |= 1 << (v - 32);
+ }
+ if_maddr_runlock(ifp);
+ }
+ }
+
+ /* reprogram multicast global hash table */
+ zyd_write32_m(sc, ZYD_MAC_GHTBL, low);
+ zyd_write32_m(sc, ZYD_MAC_GHTBH, high);
+fail:
+ if (error != 0)
+ device_printf(sc->sc_dev,
+ "could not set multicast hash table\n");
+}
+
+static void
+zyd_update_mcast(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_softc;
+
+ ZYD_LOCK(sc);
+ zyd_set_multi(sc);
+ ZYD_UNLOCK(sc);
+}
+
+static int
+zyd_set_rxfilter(struct zyd_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t rxfilter;
+
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ rxfilter = ZYD_FILTER_BSS;
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_HOSTAP:
+ rxfilter = ZYD_FILTER_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ rxfilter = ZYD_FILTER_MONITOR;
+ break;
+ default:
+ /* should not get there */
+ return (EINVAL);
+ }
+ return zyd_write32(sc, ZYD_MAC_RXFILTER, rxfilter);
+}
+
+static void
+zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c)
+{
+ int error;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct zyd_rf *rf = &sc->sc_rf;
+ uint32_t tmp;
+ int chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY) {
+ /* XXX should NEVER happen */
+ device_printf(sc->sc_dev,
+ "%s: invalid channel %x\n", __func__, chan);
+ return;
+ }
+
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ error = (*rf->set_channel)(rf, chan);
+ if (error != 0)
+ goto fail;
+
+ if (rf->update_pwr) {
+ /* update Tx power */
+ zyd_write16_m(sc, ZYD_CR31, sc->sc_pwrint[chan - 1]);
+
+ if (sc->sc_macrev == ZYD_ZD1211B) {
+ zyd_write16_m(sc, ZYD_CR67,
+ sc->sc_ofdm36_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR66,
+ sc->sc_ofdm48_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR65,
+ sc->sc_ofdm54_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR68, sc->sc_pwrcal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR69, 0x28);
+ zyd_write16_m(sc, ZYD_CR69, 0x2a);
+ }
+ }
+ if (sc->sc_cckgain) {
+ /* set CCK baseband gain from EEPROM */
+ if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0)
+ zyd_write16_m(sc, ZYD_CR47, tmp & 0xff);
+ }
+ if (sc->sc_bandedge6 && rf->bandedge6 != NULL) {
+ error = (*rf->bandedge6)(rf, c);
+ if (error != 0)
+ goto fail;
+ }
+ zyd_write32_m(sc, ZYD_CR_CONFIG_PHILIPS, 0);
+
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq =
+ htole16(c->ic_freq);
+ sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags =
+ htole16(c->ic_flags);
+fail:
+ return;
+}
+
+static int
+zyd_set_beacon_interval(struct zyd_softc *sc, int bintval)
+{
+ int error;
+ uint32_t val;
+
+ zyd_read32_m(sc, ZYD_CR_ATIM_WND_PERIOD, &val);
+ sc->sc_atim_wnd = val;
+ zyd_read32_m(sc, ZYD_CR_PRE_TBTT, &val);
+ sc->sc_pre_tbtt = val;
+ sc->sc_bcn_int = bintval;
+
+ if (sc->sc_bcn_int <= 5)
+ sc->sc_bcn_int = 5;
+ if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int)
+ sc->sc_pre_tbtt = sc->sc_bcn_int - 1;
+ if (sc->sc_atim_wnd >= sc->sc_pre_tbtt)
+ sc->sc_atim_wnd = sc->sc_pre_tbtt - 1;
+
+ zyd_write32_m(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd);
+ zyd_write32_m(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt);
+ zyd_write32_m(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int);
+fail:
+ return (error);
+}
+
+static void
+zyd_rx_data(struct usb_xfer *xfer, int offset, uint16_t len)
+{
+ struct zyd_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct zyd_plcphdr plcp;
+ struct zyd_rx_stat stat;
+ struct usb_page_cache *pc;
+ struct mbuf *m;
+ int rlen, rssi;
+
+ if (len < ZYD_MIN_FRAGSZ) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n",
+ device_get_nameunit(sc->sc_dev), len);
+ counter_u64_add(ic->ic_ierrors, 1);
+ return;
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, offset, &plcp, sizeof(plcp));
+ usbd_copy_out(pc, offset + len - sizeof(stat), &stat, sizeof(stat));
+
+ if (stat.flags & ZYD_RX_ERROR) {
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: RX status indicated error (%x)\n",
+ device_get_nameunit(sc->sc_dev), stat.flags);
+ counter_u64_add(ic->ic_ierrors, 1);
+ return;
+ }
+
+ /* compute actual frame length */
+ rlen = len - sizeof(struct zyd_plcphdr) -
+ sizeof(struct zyd_rx_stat) - IEEE80211_CRC_LEN;
+
+ /* allocate a mbuf to store the frame */
+ if (rlen > (int)MCLBYTES) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too long (length=%d)\n",
+ device_get_nameunit(sc->sc_dev), rlen);
+ counter_u64_add(ic->ic_ierrors, 1);
+ return;
+ } else if (rlen > (int)MHLEN)
+ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ else
+ m = m_gethdr(M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n",
+ device_get_nameunit(sc->sc_dev));
+ counter_u64_add(ic->ic_ierrors, 1);
+ return;
+ }
+ m->m_pkthdr.len = m->m_len = rlen;
+ usbd_copy_out(pc, offset + sizeof(plcp), mtod(m, uint8_t *), rlen);
+
+ if (ieee80211_radiotap_active(ic)) {
+ struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = 0;
+ if (stat.flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32))
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+ /* XXX toss, no way to express errors */
+ if (stat.flags & ZYD_RX_DECRYPTERR)
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+ tap->wr_rate = ieee80211_plcp2rate(plcp.signal,
+ (stat.flags & ZYD_RX_OFDM) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ tap->wr_antsignal = stat.rssi + -95;
+ tap->wr_antnoise = -95; /* XXX */
+ }
+ rssi = (stat.rssi > 63) ? 127 : 2 * stat.rssi;
+
+ sc->sc_rx_data[sc->sc_rx_count].rssi = rssi;
+ sc->sc_rx_data[sc->sc_rx_count].m = m;
+ sc->sc_rx_count++;
+}
+
+static void
+zyd_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct zyd_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct zyd_rx_desc desc;
+ struct mbuf *m;
+ struct usb_page_cache *pc;
+ uint32_t offset;
+ uint8_t rssi;
+ int8_t nf;
+ int i;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ sc->sc_rx_count = 0;
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_out(pc, actlen - sizeof(desc), &desc, sizeof(desc));
+
+ offset = 0;
+ if (UGETW(desc.tag) == ZYD_TAG_MULTIFRAME) {
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: received multi-frame transfer\n", __func__);
+
+ for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) {
+ uint16_t len16 = UGETW(desc.len[i]);
+
+ if (len16 == 0 || len16 > actlen)
+ break;
+
+ zyd_rx_data(xfer, offset, len16);
+
+ /* next frame is aligned on a 32-bit boundary */
+ len16 = (len16 + 3) & ~3;
+ offset += len16;
+ if (len16 > actlen)
+ break;
+ actlen -= len16;
+ }
+ } else {
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: received single-frame transfer\n", __func__);
+
+ zyd_rx_data(xfer, 0, actlen);
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+ usbd_transfer_submit(xfer);
+
+ /*
+ * At the end of a USB callback it is always safe to unlock
+ * the private mutex of a device! That is why we do the
+ * "ieee80211_input" here, and not some lines up!
+ */
+ ZYD_UNLOCK(sc);
+ for (i = 0; i < sc->sc_rx_count; i++) {
+ rssi = sc->sc_rx_data[i].rssi;
+ m = sc->sc_rx_data[i].m;
+ sc->sc_rx_data[i].m = NULL;
+
+ nf = -95; /* XXX */
+
+ ni = ieee80211_find_rxnode(ic,
+ mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void)ieee80211_input(ni, m, rssi, nf);
+ ieee80211_free_node(ni);
+ } else
+ (void)ieee80211_input_all(ic, m, rssi, nf);
+ }
+ ZYD_LOCK(sc);
+ zyd_start(sc);
+ break;
+
+ default: /* Error */
+ DPRINTF(sc, ZYD_DEBUG_ANY, "frame error: %s\n", usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static uint8_t
+zyd_plcp_signal(struct zyd_softc *sc, int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12:
+ return (0xb);
+ case 18:
+ return (0xf);
+ case 24:
+ return (0xa);
+ case 36:
+ return (0xe);
+ case 48:
+ return (0x9);
+ case 72:
+ return (0xd);
+ case 96:
+ return (0x8);
+ case 108:
+ return (0xc);
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2:
+ return (0x0);
+ case 4:
+ return (0x1);
+ case 11:
+ return (0x2);
+ case 22:
+ return (0x3);
+ }
+
+ device_printf(sc->sc_dev, "unsupported rate %d\n", rate);
+ return (0x0);
+}
+
+static void
+zyd_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct zyd_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211vap *vap;
+ struct zyd_tx_data *data;
+ struct mbuf *m;
+ struct usb_page_cache *pc;
+ int actlen;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF(sc, ZYD_DEBUG_ANY, "transfer complete, %u bytes\n",
+ actlen);
+
+ /* free resources */
+ data = usbd_xfer_get_priv(xfer);
+ zyd_tx_free(data, 0);
+ usbd_xfer_set_priv(xfer, NULL);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&sc->tx_q);
+ if (data) {
+ STAILQ_REMOVE_HEAD(&sc->tx_q, next);
+ m = data->m;
+
+ if (m->m_pkthdr.len > (int)ZYD_MAX_TXBUFSZ) {
+ DPRINTF(sc, ZYD_DEBUG_ANY, "data overflow, %u bytes\n",
+ m->m_pkthdr.len);
+ m->m_pkthdr.len = ZYD_MAX_TXBUFSZ;
+ }
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &data->desc, ZYD_TX_DESC_SIZE);
+ usbd_m_copy_in(pc, ZYD_TX_DESC_SIZE, m, 0,
+ m->m_pkthdr.len);
+
+ vap = data->ni->ni_vap;
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = data->rate;
+
+ ieee80211_radiotap_tx(vap, m);
+ }
+
+ usbd_xfer_set_frame_len(xfer, 0, ZYD_TX_DESC_SIZE + m->m_pkthdr.len);
+ usbd_xfer_set_priv(xfer, data);
+ usbd_transfer_submit(xfer);
+ }
+ zyd_start(sc);
+ break;
+
+ default: /* Error */
+ DPRINTF(sc, ZYD_DEBUG_ANY, "transfer error, %s\n",
+ usbd_errstr(error));
+
+ counter_u64_add(sc->sc_ic.ic_oerrors, 1);
+ data = usbd_xfer_get_priv(xfer);
+ usbd_xfer_set_priv(xfer, NULL);
+ if (data != NULL)
+ zyd_tx_free(data, error);
+
+ if (error != USB_ERR_CANCELLED) {
+ if (error == USB_ERR_TIMEOUT)
+ device_printf(sc->sc_dev, "device timeout\n");
+
+ /*
+ * Try to clear stall first, also if other
+ * errors occur, hence clearing stall
+ * introduces a 50 ms delay:
+ */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static int
+zyd_tx_start(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct zyd_tx_desc *desc;
+ struct zyd_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ int rate, totlen;
+ static const uint8_t ratediv[] = ZYD_TX_RATEDIV;
+ uint8_t phy;
+ uint16_t pktlen;
+ uint32_t bits;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT ||
+ (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+ rate = tp->mgmtrate;
+ } else {
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ /* for data frames */
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else {
+ (void) ieee80211_ratectl_rate(ni, NULL, 0);
+ rate = ni->ni_txrate;
+ }
+ }
+
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ return (ENOBUFS);
+ }
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ data->ni = ni;
+ data->m = m0;
+ data->rate = rate;
+
+ /* fill Tx descriptor */
+ desc = &data->desc;
+ phy = zyd_plcp_signal(sc, rate);
+ desc->phy = phy;
+ if (ZYD_RATE_IS_OFDM(rate)) {
+ desc->phy |= ZYD_TX_PHY_OFDM;
+ if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
+ desc->phy |= ZYD_TX_PHY_5GHZ;
+ } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->phy |= ZYD_TX_PHY_SHPREAMBLE;
+
+ totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN;
+ desc->len = htole16(totlen);
+
+ desc->flags = ZYD_TX_FLAG_BACKOFF;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /* multicast frames are not sent at OFDM rates in 802.11b/g */
+ if (totlen > vap->iv_rtsthreshold) {
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ } else if (ZYD_RATE_IS_OFDM(rate) &&
+ (ic->ic_flags & IEEE80211_F_USEPROT)) {
+ if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF;
+ else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ }
+ } else
+ desc->flags |= ZYD_TX_FLAG_MULTICAST;
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
+ desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
+
+ /* actual transmit length (XXX why +10?) */
+ pktlen = ZYD_TX_DESC_SIZE + 10;
+ if (sc->sc_macrev == ZYD_ZD1211)
+ pktlen += totlen;
+ desc->pktlen = htole16(pktlen);
+
+ bits = (rate == 11) ? (totlen * 16) + 10 :
+ ((rate == 22) ? (totlen * 8) + 10 : (totlen * 8));
+ desc->plcp_length = htole16(bits / ratediv[phy]);
+ desc->plcp_service = 0;
+ if (rate == 22 && (bits % 11) > 0 && (bits % 11) <= 3)
+ desc->plcp_service |= ZYD_PLCP_LENGEXT;
+ desc->nextlen = 0;
+
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rate;
+
+ ieee80211_radiotap_tx(vap, m0);
+ }
+
+ DPRINTF(sc, ZYD_DEBUG_XMIT,
+ "%s: sending data frame len=%zu rate=%u\n",
+ device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len,
+ rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usbd_transfer_start(sc->sc_xfer[ZYD_BULK_WR]);
+
+ return (0);
+}
+
+static int
+zyd_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct zyd_softc *sc = ic->ic_softc;
+ int error;
+
+ ZYD_LOCK(sc);
+ if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0) {
+ ZYD_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ ZYD_UNLOCK(sc);
+ return (error);
+ }
+ zyd_start(sc);
+ ZYD_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+zyd_start(struct zyd_softc *sc)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ ZYD_LOCK_ASSERT(sc, MA_OWNED);
+
+ while (sc->tx_nfree > 0 && (m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ if (zyd_tx_start(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ m_freem(m);
+ if_inc_counter(ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ break;
+ }
+ }
+}
+
+static int
+zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct zyd_softc *sc = ic->ic_softc;
+
+ ZYD_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if (!(sc->sc_flags & ZYD_FLAG_RUNNING)) {
+ ZYD_UNLOCK(sc);
+ m_freem(m);
+ return (ENETDOWN);
+ }
+ if (sc->tx_nfree == 0) {
+ ZYD_UNLOCK(sc);
+ m_freem(m);
+ return (ENOBUFS); /* XXX */
+ }
+
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ * XXX raw path
+ */
+ if (zyd_tx_start(sc, m, ni) != 0) {
+ ZYD_UNLOCK(sc);
+ m_freem(m);
+ return (EIO);
+ }
+ ZYD_UNLOCK(sc);
+ return (0);
+}
+
+static void
+zyd_parent(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_softc;
+ int startall = 0;
+
+ ZYD_LOCK(sc);
+ if (sc->sc_flags & ZYD_FLAG_DETACHED) {
+ ZYD_UNLOCK(sc);
+ return;
+ }
+ if (ic->ic_nrunning > 0) {
+ if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0) {
+ zyd_init_locked(sc);
+ startall = 1;
+ } else
+ zyd_set_multi(sc);
+ } else if (sc->sc_flags & ZYD_FLAG_RUNNING)
+ zyd_stop(sc);
+ ZYD_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+}
+
+static void
+zyd_init_locked(struct zyd_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct usb_config_descriptor *cd;
+ int error;
+ uint32_t val;
+
+ ZYD_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) {
+ error = zyd_loadfirmware(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not load firmware (error=%d)\n", error);
+ goto fail;
+ }
+
+ /* reset device */
+ cd = usbd_get_config_descriptor(sc->sc_udev);
+ error = usbd_req_set_config(sc->sc_udev, &sc->sc_mtx,
+ cd->bConfigurationValue);
+ if (error)
+ device_printf(sc->sc_dev, "reset failed, continuing\n");
+
+ error = zyd_hw_init(sc);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "hardware initialization failed\n");
+ goto fail;
+ }
+
+ device_printf(sc->sc_dev,
+ "HMAC ZD1211%s, FW %02x.%02x, RF %s S%x, PA%x LED %x "
+ "BE%x NP%x Gain%x F%x\n",
+ (sc->sc_macrev == ZYD_ZD1211) ? "": "B",
+ sc->sc_fwrev >> 8, sc->sc_fwrev & 0xff,
+ zyd_rf_name(sc->sc_rfrev), sc->sc_al2230s, sc->sc_parev,
+ sc->sc_ledtype, sc->sc_bandedge6, sc->sc_newphy,
+ sc->sc_cckgain, sc->sc_fix_cr157);
+
+ /* read regulatory domain (currently unused) */
+ zyd_read32_m(sc, ZYD_EEPROM_SUBID, &val);
+ sc->sc_regdomain = val >> 16;
+ DPRINTF(sc, ZYD_DEBUG_INIT, "regulatory domain %x\n",
+ sc->sc_regdomain);
+
+ /* we'll do software WEP decryption for now */
+ DPRINTF(sc, ZYD_DEBUG_INIT, "%s: setting encryption type\n",
+ __func__);
+ zyd_write32_m(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER);
+
+ sc->sc_flags |= ZYD_FLAG_INITONCE;
+ }
+
+ if (sc->sc_flags & ZYD_FLAG_RUNNING)
+ zyd_stop(sc);
+
+ DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %6D\n",
+ vap ? vap->iv_myaddr : ic->ic_macaddr, ":");
+ error = zyd_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr);
+ if (error != 0)
+ return;
+
+ /* set basic rates */
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x0003);
+ else if (ic->ic_curmode == IEEE80211_MODE_11A)
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x1500);
+ else /* assumes 802.11b/g */
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0xff0f);
+
+ /* promiscuous mode */
+ zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0);
+ /* multicast setup */
+ zyd_set_multi(sc);
+ /* set RX filter */
+ error = zyd_set_rxfilter(sc);
+ if (error != 0)
+ goto fail;
+
+ /* switch radio transmitter ON */
+ error = zyd_switch_radio(sc, 1);
+ if (error != 0)
+ goto fail;
+ /* set default BSS channel */
+ zyd_set_chan(sc, ic->ic_curchan);
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ zyd_setup_tx_list(sc);
+
+ /* enable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK);
+
+ sc->sc_flags |= ZYD_FLAG_RUNNING;
+ usbd_xfer_set_stall(sc->sc_xfer[ZYD_BULK_WR]);
+ usbd_transfer_start(sc->sc_xfer[ZYD_BULK_RD]);
+ usbd_transfer_start(sc->sc_xfer[ZYD_INTR_RD]);
+
+ return;
+
+fail: zyd_stop(sc);
+ return;
+}
+
+static void
+zyd_stop(struct zyd_softc *sc)
+{
+ int error;
+
+ ZYD_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~ZYD_FLAG_RUNNING;
+ zyd_drain_mbufq(sc);
+
+ /*
+ * Drain all the transfers, if not already drained:
+ */
+ ZYD_UNLOCK(sc);
+ usbd_transfer_drain(sc->sc_xfer[ZYD_BULK_WR]);
+ usbd_transfer_drain(sc->sc_xfer[ZYD_BULK_RD]);
+ ZYD_LOCK(sc);
+
+ zyd_unsetup_tx_list(sc);
+
+ /* Stop now if the device was never set up */
+ if (!(sc->sc_flags & ZYD_FLAG_INITONCE))
+ return;
+
+ /* switch radio transmitter OFF */
+ error = zyd_switch_radio(sc, 0);
+ if (error != 0)
+ goto fail;
+ /* disable Rx */
+ zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0);
+ /* disable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0);
+
+fail:
+ return;
+}
+
+static int
+zyd_loadfirmware(struct zyd_softc *sc)
+{
+ struct usb_device_request req;
+ size_t size;
+ u_char *fw;
+ uint8_t stat;
+ uint16_t addr;
+
+ if (sc->sc_flags & ZYD_FLAG_FWLOADED)
+ return (0);
+
+ if (sc->sc_macrev == ZYD_ZD1211) {
+ fw = (u_char *)zd1211_firmware;
+ size = sizeof(zd1211_firmware);
+ } else {
+ fw = (u_char *)zd1211b_firmware;
+ size = sizeof(zd1211b_firmware);
+ }
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = ZYD_DOWNLOADREQ;
+ USETW(req.wIndex, 0);
+
+ addr = ZYD_FIRMWARE_START_ADDR;
+ while (size > 0) {
+ /*
+ * When the transfer size is 4096 bytes, it is not
+ * likely to be able to transfer it.
+ * The cause is port or machine or chip?
+ */
+ const int mlen = min(size, 64);
+
+ DPRINTF(sc, ZYD_DEBUG_FW,
+ "loading firmware block: len=%d, addr=0x%x\n", mlen, addr);
+
+ USETW(req.wValue, addr);
+ USETW(req.wLength, mlen);
+ if (zyd_do_request(sc, &req, fw) != 0)
+ return (EIO);
+
+ addr += mlen / 2;
+ fw += mlen;
+ size -= mlen;
+ }
+
+ /* check whether the upload succeeded */
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = ZYD_DOWNLOADSTS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(stat));
+ if (zyd_do_request(sc, &req, &stat) != 0)
+ return (EIO);
+
+ sc->sc_flags |= ZYD_FLAG_FWLOADED;
+
+ return (stat & 0x80) ? (EIO) : (0);
+}
+
+static void
+zyd_scan_start(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_softc;
+
+ ZYD_LOCK(sc);
+ /* want broadcast address while scanning */
+ zyd_set_bssid(sc, ieee80211broadcastaddr);
+ ZYD_UNLOCK(sc);
+}
+
+static void
+zyd_scan_end(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_softc;
+
+ ZYD_LOCK(sc);
+ /* restore previous bssid */
+ zyd_set_bssid(sc, sc->sc_bssid);
+ ZYD_UNLOCK(sc);
+}
+
+static void
+zyd_getradiocaps(struct ieee80211com *ic,
+ int maxchans, int *nchans, struct ieee80211_channel chans[])
+{
+ uint8_t bands[IEEE80211_MODE_BYTES];
+
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ ieee80211_add_channel_list_2ghz(chans, maxchans, nchans,
+ zyd_chan_2ghz, nitems(zyd_chan_2ghz), bands, 0);
+}
+
+static void
+zyd_set_channel(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_softc;
+
+ ZYD_LOCK(sc);
+ zyd_set_chan(sc, ic->ic_curchan);
+ ZYD_UNLOCK(sc);
+}
+
+static device_method_t zyd_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, zyd_match),
+ DEVMETHOD(device_attach, zyd_attach),
+ DEVMETHOD(device_detach, zyd_detach),
+ DEVMETHOD_END
+};
+
+static driver_t zyd_driver = {
+ .name = "zyd",
+ .methods = zyd_methods,
+ .size = sizeof(struct zyd_softc)
+};
+
+static devclass_t zyd_devclass;
+
+DRIVER_MODULE(zyd, uhub, zyd_driver, zyd_devclass, NULL, 0);
+MODULE_DEPEND(zyd, usb, 1, 1, 1);
+MODULE_DEPEND(zyd, wlan, 1, 1, 1);
+MODULE_VERSION(zyd, 1);
+USB_PNP_HOST_INFO(zyd_devs);
diff --git a/freebsd/sys/dev/usb/wlan/if_zydfw.h b/freebsd/sys/dev/usb/wlan/if_zydfw.h
new file mode 100644
index 00000000..46f5c2ab
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_zydfw.h
@@ -0,0 +1,1144 @@
+/*
+ * Copyright (C) 2001, 2002, 2003,2004 ZyDAS Technology Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that the following conditions are met:
+ * 1. Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistribution 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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$ */
+
+uint8_t zd1211_firmware[] = {
+ 0x08, 0x91, 0xFF, 0xED, 0x09, 0x93, 0x1E, 0xEE,
+ 0xD1, 0x94, 0x11, 0xEE, 0x88, 0xD4, 0xD1, 0x96,
+ 0xD1, 0x98, 0x5C, 0x99, 0x5C, 0x99, 0x4C, 0x99,
+ 0x04, 0x9D, 0xD1, 0x98, 0xD1, 0x9A, 0x03, 0xEE,
+ 0xF4, 0x94, 0xD3, 0xD4, 0x41, 0x2A, 0x40, 0x4A,
+ 0x45, 0xBE, 0x88, 0x92, 0x41, 0x24, 0x40, 0x44,
+ 0x53, 0xBE, 0x40, 0xF0, 0x93, 0xEE, 0x41, 0xEE,
+ 0x98, 0x9A, 0xD4, 0xF7, 0x02, 0x00, 0x1F, 0xEC,
+ 0x00, 0x00, 0xB2, 0xF8, 0x4D, 0x00, 0xA1, 0xEC,
+ 0x00, 0x00, 0xA6, 0xF7, 0x21, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xD8,
+ 0xA0, 0x90, 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8,
+ 0x40, 0xF0, 0xB4, 0xF0, 0xA0, 0x90, 0x98, 0x9A,
+ 0xA0, 0xD8, 0x40, 0xF0, 0x64, 0xEF, 0xA0, 0x90,
+ 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, 0xF6, 0xF0,
+ 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0,
+ 0xF7, 0xF6, 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8,
+ 0x40, 0xF0, 0xF8, 0xF5, 0xA0, 0x90, 0x98, 0x9A,
+ 0xA0, 0xD8, 0x40, 0xF0, 0xF1, 0xF0, 0xA0, 0x90,
+ 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0,
+ 0x97, 0xF7, 0xA0, 0x90, 0x98, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00,
+ 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x42, 0x02,
+ 0xC1, 0x92, 0x03, 0x96, 0x1B, 0xD7, 0x2A, 0x86,
+ 0x1A, 0xD5, 0x2B, 0x86, 0x09, 0xA3, 0x00, 0x80,
+ 0x19, 0xD3, 0x2C, 0x86, 0x00, 0xEE, 0x0A, 0x65,
+ 0xC0, 0x7A, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xFE, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, 0xC5, 0xD4,
+ 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x01, 0xD4,
+ 0x42, 0x02, 0xC1, 0x96, 0x0A, 0x65, 0xC0, 0x7A,
+ 0x02, 0x99, 0xC4, 0x92, 0x41, 0xA2, 0xC4, 0xD2,
+ 0xC5, 0x98, 0x1C, 0xD9, 0x2A, 0x86, 0x01, 0x98,
+ 0x1C, 0xD9, 0x2B, 0x86, 0x1B, 0xD7, 0x2C, 0x86,
+ 0x00, 0xEE, 0x09, 0xB3, 0xFE, 0xFF, 0xC2, 0xD2,
+ 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x41, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0xE5, 0xEE, 0x11, 0x93, 0xD8, 0xF7, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xAE, 0xEE, 0x40, 0xF1,
+ 0x40, 0x92, 0x19, 0xD3, 0xD8, 0xF7, 0xC5, 0x92,
+ 0x41, 0x92, 0x19, 0xD3, 0x00, 0x83, 0x40, 0x92,
+ 0x19, 0xD3, 0x00, 0x83, 0x0F, 0x9F, 0x95, 0xF8,
+ 0x0F, 0x9F, 0x99, 0xEE, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x99, 0xEE, 0x40, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3,
+ 0x91, 0xEC, 0x40, 0xF0, 0x5F, 0xF2, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0F, 0x9F,
+ 0x99, 0xEE, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92,
+ 0x19, 0xD3, 0x12, 0x95, 0x19, 0xD3, 0x10, 0x95,
+ 0x19, 0xD3, 0x02, 0x80, 0x19, 0xD3, 0x03, 0x82,
+ 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, 0x91, 0xEC,
+ 0x40, 0xF0, 0x5F, 0xF2, 0x40, 0xF0, 0xDE, 0xF3,
+ 0x11, 0x93, 0x04, 0xEC, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xE3, 0xEE, 0x40, 0x92, 0x19, 0xD3,
+ 0x04, 0xEC, 0x40, 0xF0, 0x38, 0xF2, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x11, 0x93, 0x44, 0x96, 0x09, 0xB3, 0xFF, 0xFD,
+ 0x19, 0xD3, 0x44, 0x96, 0x40, 0xF0, 0x90, 0xF7,
+ 0x6E, 0x92, 0x19, 0xD3, 0x05, 0x84, 0x40, 0xF0,
+ 0xC4, 0xEE, 0x4B, 0x62, 0x0A, 0x95, 0x2E, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x2B, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x00, 0xEE, 0xD1, 0xD4, 0x0B, 0x97,
+ 0x2F, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x34, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x39, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x3E, 0xEE, 0xD1, 0xD4, 0x0B, 0x97,
+ 0x43, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x48, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x4D, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x4E, 0xEE, 0xC1, 0xD4, 0x0A, 0x65,
+ 0x00, 0x44, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2,
+ 0xC2, 0xD2, 0x43, 0xF1, 0x09, 0x93, 0x01, 0x3F,
+ 0x19, 0xD3, 0xC0, 0x85, 0x11, 0x93, 0x44, 0x96,
+ 0x09, 0xB3, 0xFF, 0xFC, 0x19, 0xD3, 0x44, 0x96,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, 0x03, 0x96,
+ 0x41, 0x02, 0x03, 0x99, 0xC4, 0x94, 0x42, 0x04,
+ 0xC1, 0x04, 0xC2, 0x94, 0xC3, 0xD4, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0x13, 0x97,
+ 0x95, 0xEC, 0x1B, 0xD7, 0x02, 0x80, 0x11, 0x93,
+ 0x99, 0xEC, 0x19, 0xD3, 0x7C, 0x96, 0x0B, 0x97,
+ 0xA0, 0x00, 0x1B, 0xD7, 0x6E, 0xEC, 0x0A, 0x65,
+ 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xFF, 0xBF, 0x11, 0xA3, 0x9A, 0xEC, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x14, 0x99, 0x03, 0x80, 0x0C, 0xB3, 0x00, 0x10,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x97, 0xF0,
+ 0x11, 0x93, 0x9F, 0xEC, 0x41, 0x02, 0x19, 0xD3,
+ 0x9F, 0xEC, 0x11, 0x93, 0xD6, 0xF7, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x84, 0xEF, 0x0A, 0x65,
+ 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0x00, 0x04, 0xC2, 0xD2, 0x0F, 0x9F, 0xB1, 0xF0,
+ 0x11, 0x93, 0x94, 0xEC, 0x02, 0xD2, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xD0, 0xEF, 0x41, 0x92,
+ 0x19, 0xD3, 0x94, 0xEC, 0x19, 0xD3, 0x9F, 0xEC,
+ 0x12, 0x95, 0x02, 0x80, 0x1A, 0xD5, 0x95, 0xEC,
+ 0x13, 0x97, 0x7C, 0x96, 0x1B, 0xD7, 0x99, 0xEC,
+ 0x0A, 0x65, 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0x00, 0x40, 0x19, 0xD3, 0x9A, 0xEC,
+ 0x09, 0x63, 0x00, 0x40, 0xC2, 0xD2, 0x02, 0x94,
+ 0x1A, 0xD5, 0x7C, 0x96, 0x0C, 0xB3, 0x00, 0x08,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xB0, 0xEF,
+ 0x0C, 0xB3, 0xFF, 0x07, 0x0F, 0x9F, 0xB4, 0xEF,
+ 0x11, 0x93, 0x06, 0x80, 0x09, 0xB3, 0xFF, 0x07,
+ 0x09, 0x03, 0x00, 0xA0, 0x19, 0xD3, 0x97, 0xEC,
+ 0x40, 0x98, 0x0B, 0x97, 0x9C, 0xEC, 0x04, 0x95,
+ 0x03, 0x05, 0x14, 0x03, 0x97, 0xEC, 0x46, 0x02,
+ 0xC1, 0x92, 0xC2, 0xD2, 0x41, 0x08, 0x42, 0x48,
+ 0x02, 0x9E, 0x0F, 0x9F, 0xBB, 0xEF, 0x11, 0x93,
+ 0x97, 0xEC, 0xC1, 0x92, 0xC5, 0xD2, 0x5F, 0xB2,
+ 0x19, 0xD3, 0x9B, 0xEC, 0x0F, 0x9F, 0xD3, 0xEF,
+ 0x13, 0x97, 0x98, 0xEC, 0xC5, 0xD6, 0x11, 0x93,
+ 0x03, 0x80, 0x09, 0xB3, 0x00, 0x08, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xE9, 0xEF, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x10,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x40, 0x98, 0x1C, 0xD9,
+ 0x9B, 0xEC, 0x12, 0x95, 0x9B, 0xEC, 0x40, 0x44,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x86, 0xF0, 0x0A, 0xB3,
+ 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x07, 0xF0, 0x0A, 0xB3, 0x07, 0x00, 0x09, 0x05,
+ 0xA9, 0xEC, 0xC2, 0x94, 0x01, 0xD4, 0x09, 0x03,
+ 0xA1, 0xEC, 0xC1, 0x92, 0x19, 0xD3, 0x9B, 0xEC,
+ 0xC5, 0x94, 0x0A, 0xB5, 0x00, 0xFF, 0x01, 0xA5,
+ 0xC5, 0xD4, 0x0F, 0x9F, 0x13, 0xF0, 0x0A, 0x05,
+ 0xFF, 0xFF, 0x0A, 0x03, 0xB1, 0xEC, 0xC1, 0x92,
+ 0x01, 0xD2, 0x1A, 0xD5, 0x9B, 0xEC, 0xC5, 0x96,
+ 0x0B, 0x07, 0xFF, 0xFF, 0xC5, 0xD6, 0x11, 0x93,
+ 0x97, 0xEC, 0xC5, 0x98, 0xC1, 0xD8, 0x11, 0x93,
+ 0x97, 0xEC, 0x09, 0x05, 0x0B, 0x00, 0x03, 0xD4,
+ 0xC2, 0x96, 0x06, 0xD6, 0x7B, 0x95, 0x7A, 0x95,
+ 0x4C, 0x02, 0xC1, 0x92, 0x59, 0x93, 0x59, 0x93,
+ 0x01, 0xA5, 0x01, 0x98, 0x0C, 0xF5, 0x7B, 0x93,
+ 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xB3,
+ 0xFF, 0x00, 0x04, 0xD2, 0x5C, 0x93, 0x59, 0x93,
+ 0x04, 0x94, 0x01, 0xA5, 0x03, 0x96, 0xC3, 0xD4,
+ 0x11, 0x93, 0x97, 0xEC, 0x4C, 0x02, 0x05, 0xD2,
+ 0xC1, 0x92, 0x09, 0xB3, 0x00, 0xFF, 0x7C, 0x95,
+ 0x7A, 0x95, 0x02, 0xA3, 0x05, 0x98, 0xC4, 0xD2,
+ 0x12, 0x95, 0x97, 0xEC, 0x45, 0x04, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2,
+ 0x12, 0x95, 0x9B, 0xEC, 0x0A, 0xB3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5B, 0xF0,
+ 0x12, 0x95, 0x97, 0xEC, 0x4A, 0x04, 0x02, 0x99,
+ 0xC4, 0x92, 0x01, 0x98, 0x0C, 0xF3, 0x7B, 0x93,
+ 0x41, 0x02, 0x0F, 0x9F, 0x7C, 0xF0, 0x43, 0x44,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x7D, 0xF0, 0x11, 0x93,
+ 0x97, 0xEC, 0x42, 0x02, 0x0A, 0x05, 0xFF, 0xFF,
+ 0xC1, 0xD4, 0x11, 0x93, 0x97, 0xEC, 0x4A, 0x02,
+ 0x12, 0x95, 0x60, 0x96, 0xC1, 0xD4, 0x12, 0x95,
+ 0x97, 0xEC, 0x4B, 0x04, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0x1F, 0xFF, 0xC2, 0xD2, 0x12, 0x95,
+ 0x97, 0xEC, 0x4B, 0x04, 0x11, 0x93, 0x62, 0x96,
+ 0x41, 0x93, 0x59, 0x93, 0x02, 0x99, 0xC4, 0xA2,
+ 0xC2, 0xD2, 0xC5, 0x92, 0x19, 0xD3, 0x98, 0xEC,
+ 0x0A, 0x95, 0x0C, 0x02, 0x1A, 0xD5, 0x02, 0x80,
+ 0x0F, 0x9F, 0xB1, 0xF0, 0x09, 0x63, 0xFE, 0x7F,
+ 0x01, 0x97, 0xC3, 0x94, 0x0A, 0xA5, 0x00, 0x04,
+ 0xC1, 0xD4, 0x11, 0x93, 0x9F, 0xEC, 0x09, 0xA3,
+ 0x00, 0x01, 0x19, 0xD3, 0x9F, 0xEC, 0x40, 0xF0,
+ 0x39, 0xEF, 0x0F, 0x9F, 0xB1, 0xF0, 0x11, 0x93,
+ 0x94, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xA6, 0xF0, 0x40, 0xF0, 0x39, 0xEF, 0x11, 0x93,
+ 0x95, 0xEC, 0x44, 0xB2, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xB1, 0xF0, 0x48, 0x98, 0x1C, 0xD9,
+ 0x02, 0x80, 0x11, 0x93, 0x91, 0xEC, 0x41, 0x22,
+ 0x0A, 0x95, 0xB1, 0xF0, 0x88, 0xD4, 0x88, 0xDC,
+ 0x91, 0x9A, 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0x04, 0x82, 0x48, 0xB2, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xC8, 0xF0, 0x0A, 0x65, 0xFD, 0x7D,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFF, 0xFE,
+ 0xC2, 0xD2, 0x41, 0x92, 0x19, 0xD3, 0xBF, 0xEC,
+ 0x11, 0x93, 0x04, 0x82, 0x43, 0xB2, 0x12, 0x95,
+ 0x03, 0x82, 0x02, 0xB3, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xEF, 0xF0, 0x0A, 0xB3, 0x00, 0xFF,
+ 0x48, 0xA2, 0x19, 0xD3, 0x03, 0x82, 0x40, 0xF0,
+ 0xEB, 0xF3, 0x11, 0x93, 0xBF, 0xEC, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93,
+ 0x07, 0x82, 0x11, 0x43, 0x03, 0xEC, 0x02, 0x0E,
+ 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, 0x03, 0x82,
+ 0x09, 0xA3, 0x00, 0x01, 0x19, 0xD3, 0x03, 0x82,
+ 0x40, 0x96, 0x1B, 0xD7, 0xBF, 0xEC, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x11, 0x93, 0x20, 0xBC, 0xC8, 0xD2,
+ 0x40, 0xF0, 0x48, 0xF1, 0x41, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94,
+ 0x41, 0x02, 0xC1, 0x92, 0x01, 0x97, 0xC3, 0x96,
+ 0xC2, 0xD6, 0x0A, 0x45, 0x00, 0x95, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x45, 0xF1, 0xC1, 0x92, 0x41, 0xB2,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x45, 0xF1,
+ 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x45, 0xF1, 0x41, 0x98, 0x1C, 0xD9,
+ 0xC0, 0xEC, 0x12, 0x95, 0x02, 0x80, 0x01, 0xD4,
+ 0x40, 0xF0, 0x56, 0xF2, 0x0B, 0x67, 0xFD, 0x7D,
+ 0x03, 0x99, 0xC4, 0x92, 0x0C, 0x99, 0x96, 0x03,
+ 0x1C, 0xD9, 0x06, 0x82, 0x41, 0x98, 0x1C, 0xD9,
+ 0x02, 0x82, 0x42, 0x98, 0x1C, 0xD9, 0x05, 0x82,
+ 0x0C, 0x69, 0x80, 0x7F, 0x1C, 0xD9, 0x00, 0xB0,
+ 0x09, 0xA3, 0x00, 0x01, 0xC3, 0xD2, 0x01, 0x94,
+ 0x0A, 0xB3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x43, 0xF1, 0x42, 0xA4, 0x1A, 0xD5,
+ 0x02, 0x80, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x05, 0x92, 0xC5, 0xD2, 0x60, 0xB2, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x55, 0xF1, 0x40, 0xF0,
+ 0x35, 0xF7, 0xC5, 0x94, 0x0A, 0xB3, 0x10, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5E, 0xF1,
+ 0x40, 0xF0, 0x23, 0xF6, 0xC5, 0x96, 0x0B, 0xB3,
+ 0x40, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x67, 0xF1, 0x40, 0xF0, 0x5D, 0xF5, 0xC5, 0x94,
+ 0x0A, 0xB3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xC8, 0xF1, 0x13, 0x97, 0x21, 0xBC,
+ 0x01, 0xD6, 0x0B, 0xB3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x79, 0xF1, 0x40, 0xF0,
+ 0x62, 0xFB, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x82, 0xF1,
+ 0x40, 0xF0, 0x6C, 0xFB, 0x01, 0x96, 0x0B, 0xB3,
+ 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xA2, 0xF1, 0x40, 0xF0, 0xB0, 0xFA, 0x41, 0x92,
+ 0x19, 0xD3, 0xD5, 0xF7, 0x11, 0x93, 0x03, 0xEC,
+ 0x09, 0x43, 0x40, 0x00, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x98, 0xF1, 0x40, 0x94, 0x1A, 0xD5, 0xD5, 0xF7,
+ 0x11, 0x93, 0x00, 0xEC, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xAB, 0xF1, 0x40, 0xF0, 0x38, 0xF2,
+ 0x0F, 0x9F, 0xAB, 0xF1, 0x01, 0x96, 0x0B, 0xB3,
+ 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xAB, 0xF1, 0x40, 0xF0, 0x7C, 0xFB, 0x01, 0x94,
+ 0x0A, 0xB3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xB4, 0xF1, 0x40, 0xF0, 0x87, 0xFB,
+ 0x11, 0x93, 0x10, 0xEC, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xBF, 0xF1, 0x44, 0x96, 0x1B, 0xD7,
+ 0x0B, 0xBC, 0x0F, 0x9F, 0xC5, 0xF1, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xC5, 0xF1, 0x19, 0xD3,
+ 0x0B, 0xBC, 0x40, 0x92, 0x19, 0xD3, 0x10, 0xEC,
+ 0xC5, 0x94, 0x0A, 0xB3, 0x80, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, 0x13, 0x97,
+ 0x28, 0xBC, 0x01, 0xD6, 0x0B, 0xB3, 0x40, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xDA, 0xF1,
+ 0x40, 0xF0, 0x18, 0xF7, 0x01, 0x94, 0x0A, 0xB3,
+ 0x02, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xED, 0xF1, 0x40, 0xF0, 0xC4, 0xEE, 0x40, 0xF0,
+ 0x8F, 0xFB, 0x40, 0xF0, 0x1B, 0xF2, 0x40, 0x96,
+ 0x1B, 0xD7, 0x00, 0xEC, 0x41, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x09, 0xF2,
+ 0x40, 0xF0, 0x9E, 0xFB, 0x09, 0x63, 0x00, 0x44,
+ 0x01, 0x97, 0xC3, 0x94, 0x48, 0xA4, 0xC1, 0xD4,
+ 0x00, 0xEE, 0x40, 0x92, 0x19, 0xD3, 0x12, 0x95,
+ 0x19, 0xD3, 0x10, 0x95, 0x19, 0xD3, 0x02, 0x80,
+ 0x19, 0xD3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2,
+ 0x40, 0xF0, 0xAE, 0xFB, 0x0A, 0x65, 0x00, 0x44,
+ 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2,
+ 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, 0xEA, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65,
+ 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEB, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF,
+ 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3,
+ 0x02, 0x80, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x93, 0x00, 0x09,
+ 0x19, 0xD3, 0x02, 0x80, 0x40, 0xF0, 0x56, 0xF2,
+ 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0xC8, 0xD2,
+ 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0,
+ 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0x3B, 0xF5, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x85, 0xF2, 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97,
+ 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0x92, 0xF2, 0x40, 0xF0, 0x94, 0xF2, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x92, 0xF2, 0xC8, 0xD2,
+ 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0,
+ 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0xF1, 0xBD, 0x19, 0xD3, 0xB6, 0xEC, 0x11, 0x93,
+ 0xB4, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xAC, 0xF2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xC3, 0x94, 0x0A, 0x07, 0x07, 0x00, 0xC1, 0xD6,
+ 0x0A, 0x05, 0x00, 0xA0, 0x1A, 0xD5, 0x96, 0xEC,
+ 0x11, 0x93, 0xB6, 0xEC, 0x19, 0xD3, 0x01, 0x80,
+ 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92,
+ 0x41, 0xA2, 0xC2, 0xD2, 0x40, 0x92, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x13, 0x97, 0xB4, 0xEC, 0x40, 0x46,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x2C, 0xF3, 0x12, 0x95,
+ 0x96, 0xEC, 0x0A, 0x03, 0x07, 0x00, 0xC1, 0x92,
+ 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0x09, 0x05,
+ 0x01, 0x00, 0x48, 0x02, 0xC1, 0x92, 0xC2, 0xD2,
+ 0x11, 0x93, 0x96, 0xEC, 0x4E, 0x02, 0xC1, 0x94,
+ 0xC5, 0xD6, 0xC5, 0x92, 0x11, 0x07, 0x96, 0xEC,
+ 0x0B, 0x03, 0x0F, 0x00, 0xC1, 0x98, 0x46, 0x06,
+ 0x7A, 0x93, 0x79, 0x93, 0x5C, 0x95, 0x5A, 0x95,
+ 0x02, 0xA3, 0xC3, 0xD2, 0x04, 0x95, 0xC5, 0x96,
+ 0x41, 0x06, 0xC5, 0xD6, 0x42, 0x46, 0x02, 0x9E,
+ 0x0F, 0x9F, 0xD5, 0xF2, 0x11, 0x93, 0x96, 0xEC,
+ 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xC1, 0x92,
+ 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0xC1, 0x92,
+ 0x09, 0xB5, 0x1F, 0x00, 0x43, 0x44, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x02, 0xF3, 0x40, 0x44, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x03, 0xF3, 0x0A, 0x05, 0xFF, 0xFF,
+ 0x0F, 0x9F, 0x03, 0xF3, 0x43, 0x94, 0x11, 0x93,
+ 0x96, 0xEC, 0x42, 0x02, 0xC1, 0xD4, 0x11, 0x93,
+ 0x96, 0xEC, 0x49, 0x02, 0xC1, 0x92, 0x19, 0xD3,
+ 0xB4, 0xEC, 0x09, 0x05, 0xF2, 0xFF, 0x1A, 0xD5,
+ 0x92, 0xEC, 0x09, 0x43, 0xD0, 0x07, 0x02, 0x9E,
+ 0x0F, 0x9F, 0x2C, 0xF3, 0x11, 0x93, 0xDC, 0xF7,
+ 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93,
+ 0xDB, 0xF7, 0x09, 0xA3, 0x40, 0x00, 0x19, 0xD3,
+ 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, 0x01, 0x95,
+ 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, 0x40, 0x96,
+ 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, 0x92, 0xF3,
+ 0x11, 0x93, 0x92, 0xEC, 0x12, 0x95, 0xB6, 0xEC,
+ 0x02, 0x43, 0x02, 0x8E, 0x0F, 0x9F, 0x7A, 0xF3,
+ 0x02, 0x0E, 0x0F, 0x9F, 0x4D, 0xF3, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x80, 0x00,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x95, 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC,
+ 0x40, 0x96, 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F,
+ 0x92, 0xF3, 0x11, 0x93, 0x03, 0x80, 0x09, 0xB3,
+ 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x5F, 0xF3, 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x5F, 0xF3, 0x40, 0xF0,
+ 0xA6, 0xF3, 0x0F, 0x9F, 0x94, 0xF3, 0x41, 0x92,
+ 0xC8, 0xD2, 0x0A, 0x95, 0x91, 0xEC, 0xC8, 0xD4,
+ 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, 0x11, 0x93,
+ 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x72, 0xF3, 0x42, 0x96, 0x1B, 0xD7, 0xC0, 0xEC,
+ 0x0F, 0x9F, 0x94, 0xF3, 0x0A, 0x65, 0xFE, 0x7F,
+ 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2,
+ 0x0F, 0x9F, 0x94, 0xF3, 0x12, 0x45, 0x03, 0xEC,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x8C, 0xF3, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x08,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x1A, 0xD5, 0x92, 0xEC,
+ 0x11, 0x93, 0x92, 0xEC, 0x19, 0x25, 0x92, 0xEC,
+ 0x09, 0x63, 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD,
+ 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, 0xA6, 0xF3,
+ 0x40, 0x92, 0xC8, 0xD2, 0x09, 0x93, 0x91, 0xEC,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x11, 0x93, 0xD7, 0xF7, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xB6, 0xF3, 0x0A, 0x65,
+ 0xBC, 0x69, 0x02, 0x97, 0xC3, 0x92, 0x09, 0x83,
+ 0x00, 0x02, 0xC2, 0xD2, 0x11, 0x93, 0x03, 0x80,
+ 0x09, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xC9, 0xF3, 0x11, 0x93, 0xDC, 0xF7,
+ 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93,
+ 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x20, 0x19, 0xD3,
+ 0xDB, 0xF7, 0x11, 0x93, 0xB5, 0xEC, 0x19, 0xD3,
+ 0x04, 0x80, 0x12, 0x95, 0xB4, 0xEC, 0x1A, 0xD5,
+ 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xC3, 0x96, 0x1B, 0xD7, 0xB5, 0xEC, 0x40, 0x94,
+ 0x1A, 0xD5, 0xB4, 0xEC, 0x19, 0xD3, 0xF2, 0xBD,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, 0xD3,
+ 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3,
+ 0x03, 0x82, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0x01, 0x82, 0xC5, 0xD2, 0x40, 0x94, 0x01, 0xD4,
+ 0x13, 0x97, 0xB8, 0xEC, 0x02, 0xD6, 0x03, 0x95,
+ 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x05, 0x13, 0x97,
+ 0x03, 0xEC, 0x01, 0x27, 0x02, 0x99, 0xC4, 0x92,
+ 0x03, 0x03, 0xC2, 0xD2, 0x14, 0x99, 0xBA, 0xEC,
+ 0x03, 0x09, 0x1C, 0xD9, 0xBA, 0xEC, 0x12, 0x95,
+ 0x04, 0x82, 0x0A, 0xB3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x29, 0xF5, 0x01, 0x92,
+ 0x03, 0xD2, 0x0A, 0xA3, 0x02, 0x00, 0x19, 0xD3,
+ 0x04, 0x82, 0x02, 0x96, 0x0B, 0x05, 0x01, 0x00,
+ 0x1A, 0xD5, 0xB8, 0xEC, 0xC5, 0x92, 0x43, 0x42,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x37, 0xF4, 0x42, 0x44,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x93,
+ 0xBF, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x37, 0xF4, 0x0C, 0x49, 0xD3, 0x08, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x63, 0x07, 0x82,
+ 0x11, 0xA3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93,
+ 0x79, 0x93, 0x79, 0x93, 0x03, 0xD2, 0xC5, 0x94,
+ 0x0A, 0xB5, 0xFC, 0xFF, 0x04, 0xD4, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x46, 0xF4,
+ 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x4D, 0xF4, 0xC5, 0x98, 0x0C, 0x03,
+ 0xFF, 0xFF, 0x42, 0x42, 0x02, 0x8E, 0x0F, 0x9F,
+ 0x74, 0xF4, 0x0A, 0x95, 0xBB, 0xEC, 0x42, 0x92,
+ 0x19, 0xD3, 0xB9, 0xEC, 0xC5, 0x96, 0x43, 0x46,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x66, 0xF4, 0x0B, 0x07,
+ 0xFC, 0xFF, 0xC5, 0xD6, 0xD2, 0x98, 0x1C, 0xD9,
+ 0xC8, 0xBC, 0xD2, 0x96, 0x1B, 0xD7, 0xCA, 0xBC,
+ 0x09, 0x03, 0xFF, 0xFF, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x52, 0xF4, 0x19, 0xD3, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x72, 0xF4,
+ 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xC2, 0xD2,
+ 0x0F, 0x9F, 0x74, 0xF4, 0x1A, 0xD5, 0x93, 0xEC,
+ 0x03, 0x98, 0x40, 0x48, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xA1, 0xF4, 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x84, 0xF4, 0x04, 0x94,
+ 0x48, 0x44, 0x02, 0x4E, 0x0F, 0x9F, 0x8F, 0xF4,
+ 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xA1, 0xF4,
+ 0x11, 0x93, 0x04, 0x82, 0x41, 0xB2, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xA1, 0xF4, 0x41, 0x96,
+ 0x01, 0xD6, 0x0A, 0x65, 0xBD, 0x43, 0x02, 0x99,
+ 0xC4, 0x92, 0x09, 0xA3, 0x80, 0x00, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0xFA, 0xF4, 0xC5, 0x98, 0x43, 0x48, 0x02, 0x9E,
+ 0x0F, 0x9F, 0xFA, 0xF4, 0x4F, 0x96, 0x0C, 0xB3,
+ 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xAE, 0xF4, 0x47, 0x96, 0x11, 0x93, 0xB7, 0xEC,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4,
+ 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xD6, 0xF4, 0x12, 0x95, 0x00, 0x82,
+ 0x0A, 0x05, 0xFF, 0xAF, 0x05, 0xD4, 0xC8, 0xD6,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x7B, 0xF7, 0x42, 0x00,
+ 0x05, 0x96, 0xC3, 0x94, 0x01, 0xB5, 0x40, 0x44,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, 0x06, 0x98,
+ 0x50, 0x98, 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x98,
+ 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x92, 0x03, 0xD2,
+ 0x0F, 0x9F, 0xFF, 0xF4, 0x03, 0x94, 0x40, 0x44,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xE3, 0xF4, 0x0A, 0x65,
+ 0x5E, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x48, 0xA2,
+ 0xC2, 0xD2, 0x0F, 0x9F, 0xFF, 0xF4, 0x11, 0x93,
+ 0xB8, 0xEC, 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x03,
+ 0x04, 0x96, 0x13, 0x25, 0x03, 0xEC, 0xC1, 0xD4,
+ 0x11, 0x93, 0xBA, 0xEC, 0x19, 0x05, 0xBA, 0xEC,
+ 0x1B, 0xD7, 0x01, 0x82, 0x0A, 0x65, 0xFD, 0x7D,
+ 0x02, 0x99, 0xC4, 0x92, 0x43, 0xA2, 0xC2, 0xD2,
+ 0x41, 0x92, 0x01, 0xD2, 0x03, 0x94, 0x40, 0x44,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, 0x11, 0x93,
+ 0xB9, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x0B, 0xF5, 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3,
+ 0xBA, 0xEC, 0x19, 0xD3, 0xBB, 0xEC, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5,
+ 0x41, 0x98, 0x1C, 0xD9, 0xB7, 0xEC, 0x11, 0x93,
+ 0xBF, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x24, 0xF5, 0x11, 0x93, 0x00, 0x82, 0x19, 0xD3,
+ 0x02, 0x82, 0x0A, 0x65, 0xFD, 0x7D, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2,
+ 0x40, 0x98, 0x1C, 0xD9, 0xBF, 0xEC, 0x0F, 0x9F,
+ 0x2C, 0xF5, 0x01, 0x92, 0x19, 0xD3, 0xB7, 0xEC,
+ 0x01, 0x94, 0x40, 0x44, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x38, 0xF5, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2,
+ 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x12, 0x95, 0x03, 0x80,
+ 0x0A, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x57, 0xF5, 0x0A, 0xB7, 0x00, 0x08,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x5A, 0xF5,
+ 0x11, 0x93, 0x03, 0xEC, 0x41, 0x02, 0x09, 0xB3,
+ 0xFE, 0xFF, 0x12, 0x95, 0x07, 0x80, 0x01, 0x45,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x5A, 0xF5, 0x41, 0x92,
+ 0x0F, 0x9F, 0x5B, 0xF5, 0x40, 0x92, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2,
+ 0x13, 0x97, 0x6E, 0xEC, 0x0B, 0x47, 0xA0, 0x00,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x86, 0xF5, 0x09, 0x63,
+ 0x08, 0x43, 0x0A, 0x65, 0xFF, 0x5F, 0x01, 0x99,
+ 0xC4, 0xD4, 0x0A, 0x95, 0x9B, 0xEC, 0xD2, 0x96,
+ 0x1B, 0xD7, 0xFA, 0xBC, 0xD2, 0x96, 0xC4, 0xD6,
+ 0xD2, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, 0xD2, 0x96,
+ 0xC1, 0xD6, 0xC2, 0x94, 0x1A, 0xD5, 0xFA, 0xBC,
+ 0x0F, 0x9F, 0xC4, 0xF5, 0x0C, 0x69, 0xFF, 0x6F,
+ 0x1C, 0xD9, 0xF8, 0xBC, 0x0B, 0x47, 0x10, 0x95,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x9E, 0xF5, 0x0A, 0x95,
+ 0x6F, 0xEC, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99,
+ 0xC4, 0xD6, 0xD2, 0x96, 0x1B, 0xD7, 0xF8, 0xBC,
+ 0x0C, 0x69, 0xEE, 0x6A, 0xC1, 0xD8, 0xC2, 0x94,
+ 0x1A, 0xD5, 0xF8, 0xBC, 0x40, 0x92, 0xC5, 0xD2,
+ 0x11, 0x43, 0xC1, 0xEC, 0x02, 0x0E, 0x0F, 0x9F,
+ 0xC1, 0xF5, 0xC5, 0x94, 0x0A, 0x03, 0x71, 0xEC,
+ 0xC1, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, 0x11, 0x93,
+ 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xB3, 0xF5, 0x0A, 0x95, 0x6F, 0xEC, 0xC8, 0xD4,
+ 0x40, 0xF0, 0x9C, 0xF7, 0x19, 0xD3, 0xF8, 0xBC,
+ 0x41, 0x00, 0xC5, 0x96, 0x41, 0x06, 0xC5, 0xD6,
+ 0x13, 0x47, 0xC1, 0xEC, 0x02, 0x1E, 0x0F, 0x9F,
+ 0xA5, 0xF5, 0x40, 0x98, 0x1C, 0xD9, 0xFA, 0xBC,
+ 0x40, 0x92, 0x19, 0xD3, 0x6E, 0xEC, 0x19, 0xD3,
+ 0xC1, 0xEC, 0x0A, 0x65, 0x52, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3,
+ 0xBF, 0xFF, 0xC2, 0xD2, 0x41, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x43, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x06, 0x92, 0x01, 0xD2, 0x0A, 0x65,
+ 0xF0, 0x6A, 0x0B, 0x97, 0x6F, 0xEC, 0x02, 0x99,
+ 0xC4, 0x98, 0xD3, 0xD8, 0x02, 0xD6, 0x0A, 0x03,
+ 0x02, 0x00, 0x01, 0x97, 0xC3, 0x98, 0x02, 0x96,
+ 0xC3, 0xD8, 0x01, 0x96, 0xC1, 0xD6, 0x1A, 0xD5,
+ 0x6E, 0xEC, 0xC5, 0x98, 0x14, 0x99, 0x6F, 0xEC,
+ 0xC2, 0xD8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92,
+ 0xC8, 0xD2, 0x40, 0xF0, 0xD9, 0xF5, 0x41, 0x00,
+ 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x13, 0xF6, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x10, 0xF6, 0x0A, 0x65, 0xFE, 0x7F,
+ 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2,
+ 0x40, 0x92, 0x19, 0xD3, 0xC0, 0xEC, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xE9, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF,
+ 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x63, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0xAF, 0xBC, 0x47, 0xB2, 0x59, 0x95, 0x5A, 0x95,
+ 0x12, 0xA5, 0xBF, 0xBC, 0x0A, 0xB3, 0x01, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x35, 0xF6,
+ 0x41, 0x04, 0x05, 0x93, 0x40, 0x96, 0x20, 0xD6,
+ 0x62, 0x97, 0x0F, 0x9F, 0x44, 0xF6, 0x14, 0x99,
+ 0xFC, 0xBC, 0xD1, 0xD8, 0x14, 0x99, 0xFE, 0xBC,
+ 0xD1, 0xD8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xD8,
+ 0x20, 0x98, 0x03, 0x49, 0x02, 0x1E, 0x0F, 0x9F,
+ 0x3B, 0xF6, 0xC5, 0x92, 0x62, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x5D, 0xF6, 0x02, 0x8E, 0x0F, 0x9F,
+ 0x57, 0xF6, 0x61, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x81, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, 0x63, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xA4, 0xF6, 0x0F, 0x9F,
+ 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, 0x0C, 0x99,
+ 0x71, 0xEC, 0x0B, 0x05, 0xFF, 0xFF, 0x40, 0x96,
+ 0x0F, 0x9F, 0x6A, 0xF6, 0xD1, 0x96, 0xD4, 0xD6,
+ 0x20, 0x96, 0x41, 0x06, 0x20, 0xD6, 0x02, 0x47,
+ 0x02, 0x1E, 0x0F, 0x9F, 0x66, 0xF6, 0x1A, 0xD5,
+ 0xC1, 0xEC, 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x99,
+ 0xC4, 0x92, 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0xAE, 0xF6, 0x0A, 0x03, 0xFE, 0xFF, 0x61, 0x95,
+ 0x40, 0x98, 0x20, 0xD8, 0x02, 0x49, 0x02, 0x0E,
+ 0x0F, 0x9F, 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00,
+ 0x21, 0xD2, 0x20, 0x92, 0x05, 0x03, 0x42, 0x02,
+ 0xC8, 0xD2, 0x21, 0x96, 0xC3, 0x92, 0x42, 0x06,
+ 0x21, 0xD6, 0xC8, 0xD2, 0x22, 0xD4, 0x40, 0xF0,
+ 0x01, 0xF1, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08,
+ 0x20, 0xD8, 0x22, 0x94, 0x02, 0x49, 0x02, 0x1E,
+ 0x0F, 0x9F, 0x8D, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6,
+ 0x0D, 0x03, 0x03, 0x00, 0xC8, 0xD2, 0x02, 0x92,
+ 0xC8, 0xD2, 0x01, 0x96, 0xC8, 0xD6, 0x40, 0xF0,
+ 0xB1, 0xF6, 0x43, 0x00, 0x63, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x45, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x08, 0x00, 0x08, 0x94,
+ 0xC5, 0xD4, 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94,
+ 0x03, 0xD4, 0x42, 0x02, 0xC1, 0x92, 0x01, 0xD2,
+ 0x02, 0x97, 0xC5, 0x94, 0x0A, 0x83, 0xFF, 0xFF,
+ 0x11, 0xB3, 0x2C, 0x93, 0x09, 0xB3, 0xFB, 0xFF,
+ 0x19, 0xD3, 0x2C, 0x93, 0x03, 0x92, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xE4, 0xF6, 0x01, 0x94,
+ 0xD2, 0x92, 0x19, 0xD3, 0x2C, 0x93, 0x01, 0xD4,
+ 0x02, 0x94, 0x12, 0x95, 0x2C, 0x93, 0x44, 0xA4,
+ 0x1A, 0xD5, 0x2C, 0x93, 0x0A, 0xB5, 0xFB, 0xFF,
+ 0x1A, 0xD5, 0x2C, 0x93, 0x0B, 0x07, 0xFF, 0xFF,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0xCF, 0xF6,
+ 0x09, 0x63, 0xD4, 0x6C, 0x01, 0x95, 0xC2, 0x96,
+ 0xC5, 0x94, 0x02, 0xA7, 0xC1, 0xD6, 0x03, 0x92,
+ 0x54, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xF4, 0xF6,
+ 0x0A, 0x83, 0xFF, 0xFF, 0x1B, 0xB3, 0x2C, 0x93,
+ 0x45, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xD3, 0xF2, 0xBD, 0x40, 0xF0, 0x3B, 0xF5,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x08, 0xF7,
+ 0x40, 0xF0, 0x94, 0xF2, 0x0F, 0x9F, 0x16, 0xF7,
+ 0x40, 0x96, 0xC8, 0xD6, 0x09, 0x93, 0x91, 0xEC,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x0A, 0x65,
+ 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2,
+ 0xC2, 0xD2, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x0A, 0x65,
+ 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF,
+ 0xC2, 0xD2, 0x40, 0x92, 0x19, 0xD3, 0x2D, 0xBC,
+ 0x0A, 0x65, 0xD8, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x09, 0x63, 0xEA, 0x43, 0x01, 0x97, 0xC3, 0x94,
+ 0x44, 0xA4, 0xC1, 0xD4, 0x11, 0x93, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x6F, 0xF7,
+ 0x12, 0x95, 0x93, 0xEC, 0x0B, 0x67, 0x36, 0x43,
+ 0xD2, 0x98, 0x1C, 0xD9, 0xC8, 0xBC, 0xD2, 0x98,
+ 0x03, 0x93, 0xC1, 0xD8, 0x11, 0x93, 0xB9, 0xEC,
+ 0x09, 0x03, 0xFF, 0xFF, 0x19, 0xD3, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x48, 0xF7,
+ 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, 0xBA, 0xEC,
+ 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xCA, 0xD2,
+ 0xC2, 0xD2, 0x0A, 0x65, 0x5E, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEA, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3,
+ 0xFB, 0xFF, 0x0F, 0x9F, 0x78, 0xF7, 0x11, 0x93,
+ 0x03, 0xEC, 0x19, 0xD3, 0x01, 0x82, 0x0A, 0x65,
+ 0xFD, 0x7D, 0x02, 0x97, 0xC3, 0x92, 0x43, 0xA2,
+ 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x03, 0x92, 0x04, 0x96,
+ 0x0D, 0x5E, 0x50, 0x46, 0x02, 0x0E, 0x40, 0x92,
+ 0x09, 0xEE, 0x44, 0x46, 0x04, 0x0E, 0x59, 0x93,
+ 0x44, 0x26, 0x04, 0x5E, 0x46, 0xEE, 0x41, 0x93,
+ 0x41, 0x26, 0x43, 0x4E, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0xB1, 0xFE, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x03, 0x94,
+ 0x1A, 0xD5, 0xA3, 0xF7, 0x11, 0x93, 0x00, 0x90,
+ 0x88, 0x98, 0x90, 0x9A, 0x1D, 0x00, 0x1A, 0x00,
+ 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, 0x19, 0x00,
+ 0x1A, 0x00, 0x1B, 0x00, 0x16, 0x00, 0x21, 0x00,
+ 0x12, 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00,
+ 0x19, 0x00, 0x19, 0x00, 0x21, 0x00, 0x2D, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x69,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0xF2, 0xCD, 0xF7, 0x00, 0x00, 0x74, 0xF2,
+ 0xCD, 0xF7, 0x00, 0x00, 0xB9, 0xF2, 0xCA, 0xF7,
+ 0xD1, 0xF7, 0x00, 0x00, 0x97, 0xF3, 0xCD, 0xF7,
+ 0x05, 0x46, 0x01, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * current zd1211b firmware version.
+ */
+#define ZD1211B_FIRMWARE_VER 4705
+
+uint8_t zd1211b_firmware[] = {
+ 0x08, 0x91, 0xff, 0xed, 0x09, 0x93, 0x1e, 0xee, 0xd1, 0x94, 0x11,
+ 0xee, 0x88, 0xd4, 0xd1, 0x96, 0xd1, 0x98, 0x5c, 0x99, 0x5c, 0x99,
+ 0x4c, 0x99, 0x04, 0x9d, 0xd1, 0x98, 0xd1, 0x9a, 0x03, 0xee, 0xf4,
+ 0x94, 0xd3, 0xd4, 0x41, 0x2a, 0x40, 0x4a, 0x45, 0xbe, 0x88, 0x92,
+ 0x41, 0x24, 0x40, 0x44, 0x53, 0xbe, 0x40, 0xf0, 0x4e, 0xee, 0x41,
+ 0xee, 0x98, 0x9a, 0x72, 0xf7, 0x02, 0x00, 0x1f, 0xec, 0x00, 0x00,
+ 0xb2, 0xf8, 0x4d, 0x00, 0xa1, 0xec, 0x00, 0x00, 0x43, 0xf7, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xd8,
+ 0xa0, 0x90, 0x98, 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x5a,
+ 0xf0, 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x0a, 0xef,
+ 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x97, 0xf0, 0xa0,
+ 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x94, 0xf6, 0xa0, 0x90,
+ 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x95, 0xf5, 0xa0, 0x90, 0x98,
+ 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x34, 0xf7, 0xa0, 0x90,
+ 0x98, 0x9a, 0x88, 0xda, 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x40,
+ 0xf0, 0x8e, 0xee, 0x40, 0x96, 0x0a, 0x65, 0x00, 0x7d, 0x11, 0x93,
+ 0x76, 0xf7, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x57, 0xee, 0x40,
+ 0xf1, 0x1b, 0xd7, 0x76, 0xf7, 0xc5, 0x92, 0x02, 0x99, 0x41, 0x92,
+ 0xc4, 0xd2, 0x40, 0x92, 0xc4, 0xd2, 0x0f, 0x9f, 0x95, 0xf8, 0x0f,
+ 0x9f, 0x57, 0xee, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0x19, 0xd3, 0x12, 0x95, 0x19,
+ 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, 0xd3, 0x03, 0x82,
+ 0x09, 0x93, 0x65, 0xf7, 0x19, 0xd3, 0x91, 0xec, 0x40, 0xf0, 0x07,
+ 0xf2, 0x40, 0xf0, 0x75, 0xf3, 0x11, 0x93, 0x04, 0xec, 0x42, 0x42,
+ 0x02, 0x5e, 0x0f, 0x9f, 0x8c, 0xee, 0x40, 0x92, 0x19, 0xd3, 0x04,
+ 0xec, 0x40, 0xf0, 0xe0, 0xf1, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff,
+ 0xfd, 0x19, 0xd3, 0x44, 0x96, 0x40, 0xf0, 0x2d, 0xf7, 0x40, 0xf0,
+ 0x6d, 0xee, 0x4b, 0x62, 0x0a, 0x95, 0x2e, 0xee, 0xd1, 0xd4, 0x0b,
+ 0x97, 0x2b, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x00, 0xee, 0xd1, 0xd4,
+ 0x0b, 0x97, 0x2f, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x34, 0xee, 0xd1,
+ 0xd4, 0x0b, 0x97, 0x39, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x3e, 0xee,
+ 0xd1, 0xd4, 0x0b, 0x97, 0x43, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x2e,
+ 0xee, 0xd1, 0xd4, 0x0b, 0x97, 0x48, 0xee, 0xd1, 0xd6, 0x0a, 0x95,
+ 0x49, 0xee, 0xc1, 0xd4, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, 0xc3,
+ 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x43, 0xf1, 0x09, 0x93, 0x01, 0x3f,
+ 0x19, 0xd3, 0xc0, 0x85, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff,
+ 0xfc, 0x19, 0xd3, 0x44, 0x96, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x03, 0x00, 0x03, 0x96, 0x41,
+ 0x02, 0x03, 0x99, 0xc4, 0x94, 0x42, 0x04, 0xc1, 0x04, 0xc2, 0x94,
+ 0xc3, 0xd4, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0x13, 0x97, 0x95, 0xec,
+ 0x1b, 0xd7, 0x02, 0x80, 0x11, 0x93, 0x99, 0xec, 0x19, 0xd3, 0x7c,
+ 0x96, 0x0b, 0x97, 0xa0, 0x00, 0x1b, 0xd7, 0x6e, 0xec, 0x0a, 0x65,
+ 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xff, 0xbf, 0x11,
+ 0xa3, 0x9a, 0xec, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xe9,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01,
+ 0x00, 0x14, 0x99, 0x03, 0x80, 0x0c, 0xb3, 0x00, 0x10, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x3d, 0xf0, 0x11, 0x93, 0x9f, 0xec, 0x41,
+ 0x02, 0x19, 0xd3, 0x9f, 0xec, 0x11, 0x93, 0x74, 0xf7, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x2a, 0xef, 0x0a, 0x65, 0xfe, 0x7f, 0x02,
+ 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x04, 0xc2, 0xd2, 0x0f, 0x9f,
+ 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x02, 0xd2, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x76, 0xef, 0x41, 0x92, 0x19, 0xd3, 0x94, 0xec,
+ 0x19, 0xd3, 0x9f, 0xec, 0x12, 0x95, 0x02, 0x80, 0x1a, 0xd5, 0x95,
+ 0xec, 0x13, 0x97, 0x7c, 0x96, 0x1b, 0xd7, 0x99, 0xec, 0x0a, 0x65,
+ 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0x00, 0x40, 0x19,
+ 0xd3, 0x9a, 0xec, 0x09, 0x63, 0x00, 0x40, 0xc2, 0xd2, 0x02, 0x94,
+ 0x1a, 0xd5, 0x7c, 0x96, 0x0c, 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x56, 0xef, 0x0c, 0xb3, 0xff, 0x07, 0x0f, 0x9f,
+ 0x5a, 0xef, 0x11, 0x93, 0x06, 0x80, 0x09, 0xb3, 0xff, 0x07, 0x09,
+ 0x03, 0x00, 0xa0, 0x19, 0xd3, 0x97, 0xec, 0x40, 0x98, 0x0b, 0x97,
+ 0x9c, 0xec, 0x04, 0x95, 0x03, 0x05, 0x14, 0x03, 0x97, 0xec, 0x46,
+ 0x02, 0xc1, 0x92, 0xc2, 0xd2, 0x41, 0x08, 0x42, 0x48, 0x02, 0x9e,
+ 0x0f, 0x9f, 0x61, 0xef, 0x11, 0x93, 0x97, 0xec, 0xc1, 0x92, 0xc5,
+ 0xd2, 0x5f, 0xb2, 0x19, 0xd3, 0x9b, 0xec, 0x0f, 0x9f, 0x79, 0xef,
+ 0x13, 0x97, 0x98, 0xec, 0xc5, 0xd6, 0x11, 0x93, 0x03, 0x80, 0x09,
+ 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x8f, 0xef,
+ 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11,
+ 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x10, 0x19, 0xd3, 0x79, 0xf7,
+ 0x40, 0x98, 0x1c, 0xd9, 0x9b, 0xec, 0x12, 0x95, 0x9b, 0xec, 0x40,
+ 0x44, 0x02, 0x4e, 0x0f, 0x9f, 0x2c, 0xf0, 0x0a, 0xb3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xad, 0xef, 0x0a, 0xb3, 0x07,
+ 0x00, 0x09, 0x05, 0xa9, 0xec, 0xc2, 0x94, 0x01, 0xd4, 0x09, 0x03,
+ 0xa1, 0xec, 0xc1, 0x92, 0x19, 0xd3, 0x9b, 0xec, 0xc5, 0x94, 0x0a,
+ 0xb5, 0x00, 0xff, 0x01, 0xa5, 0xc5, 0xd4, 0x0f, 0x9f, 0xb9, 0xef,
+ 0x0a, 0x05, 0xff, 0xff, 0x0a, 0x03, 0xb1, 0xec, 0xc1, 0x92, 0x01,
+ 0xd2, 0x1a, 0xd5, 0x9b, 0xec, 0xc5, 0x96, 0x0b, 0x07, 0xff, 0xff,
+ 0xc5, 0xd6, 0x11, 0x93, 0x97, 0xec, 0xc5, 0x98, 0xc1, 0xd8, 0x11,
+ 0x93, 0x97, 0xec, 0x09, 0x05, 0x0b, 0x00, 0x03, 0xd4, 0xc2, 0x96,
+ 0x06, 0xd6, 0x7b, 0x95, 0x7a, 0x95, 0x4c, 0x02, 0xc1, 0x92, 0x59,
+ 0x93, 0x59, 0x93, 0x01, 0xa5, 0x01, 0x98, 0x0c, 0xf5, 0x7b, 0x93,
+ 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xb3, 0xff, 0x00, 0x04,
+ 0xd2, 0x5c, 0x93, 0x59, 0x93, 0x04, 0x94, 0x01, 0xa5, 0x03, 0x96,
+ 0xc3, 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4c, 0x02, 0x05, 0xd2, 0xc1,
+ 0x92, 0x09, 0xb3, 0x00, 0xff, 0x7c, 0x95, 0x7a, 0x95, 0x02, 0xa3,
+ 0x05, 0x98, 0xc4, 0xd2, 0x12, 0x95, 0x97, 0xec, 0x45, 0x04, 0x02,
+ 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x01, 0xc2, 0xd2, 0x12, 0x95,
+ 0x9b, 0xec, 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x01, 0xf0, 0x12, 0x95, 0x97, 0xec, 0x4a, 0x04, 0x02, 0x99,
+ 0xc4, 0x92, 0x01, 0x98, 0x0c, 0xf3, 0x7b, 0x93, 0x41, 0x02, 0x0f,
+ 0x9f, 0x22, 0xf0, 0x43, 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0x23, 0xf0,
+ 0x11, 0x93, 0x97, 0xec, 0x42, 0x02, 0x0a, 0x05, 0xff, 0xff, 0xc1,
+ 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4a, 0x02, 0x12, 0x95, 0x60, 0x96,
+ 0xc1, 0xd4, 0x12, 0x95, 0x97, 0xec, 0x4b, 0x04, 0x02, 0x97, 0xc3,
+ 0x92, 0x09, 0xb3, 0x1f, 0xff, 0xc2, 0xd2, 0x12, 0x95, 0x97, 0xec,
+ 0x4b, 0x04, 0x11, 0x93, 0x62, 0x96, 0x41, 0x93, 0x59, 0x93, 0x02,
+ 0x99, 0xc4, 0xa2, 0xc2, 0xd2, 0xc5, 0x92, 0x19, 0xd3, 0x98, 0xec,
+ 0x0a, 0x95, 0x0c, 0x02, 0x1a, 0xd5, 0x02, 0x80, 0x0f, 0x9f, 0x57,
+ 0xf0, 0x09, 0x63, 0xfe, 0x7f, 0x01, 0x97, 0xc3, 0x94, 0x0a, 0xa5,
+ 0x00, 0x04, 0xc1, 0xd4, 0x11, 0x93, 0x9f, 0xec, 0x09, 0xa3, 0x00,
+ 0x01, 0x19, 0xd3, 0x9f, 0xec, 0x40, 0xf0, 0xdf, 0xee, 0x0f, 0x9f,
+ 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f,
+ 0x9f, 0x4c, 0xf0, 0x40, 0xf0, 0xdf, 0xee, 0x11, 0x93, 0x95, 0xec,
+ 0x44, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x57, 0xf0, 0x48,
+ 0x98, 0x1c, 0xd9, 0x02, 0x80, 0x11, 0x93, 0x91, 0xec, 0x41, 0x22,
+ 0x0a, 0x95, 0x57, 0xf0, 0x88, 0xd4, 0x88, 0xdc, 0x91, 0x9a, 0x47,
+ 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00,
+ 0x11, 0x93, 0x04, 0x82, 0x48, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x6e, 0xf0, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92,
+ 0x09, 0xb3, 0xff, 0xfe, 0xc2, 0xd2, 0x41, 0x92, 0x19, 0xd3, 0xbf,
+ 0xec, 0x11, 0x93, 0x04, 0x82, 0x43, 0xb2, 0x12, 0x95, 0x03, 0x82,
+ 0x02, 0xb3, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf0, 0x0a,
+ 0xb3, 0x00, 0xff, 0x48, 0xa2, 0x19, 0xd3, 0x03, 0x82, 0x40, 0xf0,
+ 0x82, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f,
+ 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x07, 0x82, 0x11, 0x43, 0x03, 0xec,
+ 0x02, 0x0e, 0x0f, 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x03, 0x82, 0x09,
+ 0xa3, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, 0x40, 0x96, 0x1b, 0xd7,
+ 0xbf, 0xec, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x11, 0x93, 0x20, 0xbc, 0xc8, 0xd2, 0x40, 0xf0, 0xe9, 0xf0,
+ 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08,
+ 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x05, 0x00, 0x05, 0x94, 0x41, 0x02,
+ 0xc1, 0x92, 0x01, 0x97, 0xc3, 0x96, 0xc2, 0xd6, 0x0a, 0x45, 0x00,
+ 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0xc1, 0x92, 0x41, 0xb2,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xe6, 0xf0, 0x11, 0x93, 0xc0,
+ 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0x41, 0x98,
+ 0x1c, 0xd9, 0xc0, 0xec, 0x12, 0x95, 0x02, 0x80, 0x01, 0xd4, 0x40,
+ 0xf0, 0xfe, 0xf1, 0x0b, 0x67, 0xfd, 0x7d, 0x03, 0x99, 0xc4, 0x92,
+ 0x0c, 0x99, 0x96, 0x03, 0x1c, 0xd9, 0x06, 0x82, 0x41, 0x98, 0x1c,
+ 0xd9, 0x02, 0x82, 0x42, 0x98, 0x1c, 0xd9, 0x05, 0x82, 0x0c, 0x69,
+ 0x80, 0x7f, 0x1c, 0xd9, 0x00, 0xb0, 0x09, 0xa3, 0x00, 0x01, 0xc3,
+ 0xd2, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0xe4, 0xf0, 0x42, 0xa4, 0x1a, 0xd5, 0x02, 0x80, 0x42,
+ 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b,
+ 0x01, 0x00, 0x05, 0x92, 0xc5, 0xd2, 0x60, 0xb2, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0xf6, 0xf0, 0x40, 0xf0, 0xd2, 0xf6, 0xc5, 0x94,
+ 0x0a, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xff,
+ 0xf0, 0x40, 0xf0, 0xc0, 0xf5, 0xc5, 0x96, 0x0b, 0xb3, 0x40, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x08, 0xf1, 0x40, 0xf0, 0xfa,
+ 0xf4, 0xc5, 0x94, 0x0a, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0x70, 0xf1, 0x13, 0x97, 0x21, 0xbc, 0x01, 0xd6, 0x0b,
+ 0xb3, 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1a, 0xf1,
+ 0x40, 0xf0, 0x62, 0xfb, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40,
+ 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf1, 0x40, 0xf0, 0x6c, 0xfb,
+ 0x01, 0x96, 0x0b, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x4c, 0xf1, 0x40, 0xf0, 0xb0, 0xfa, 0x41, 0x92, 0x19, 0xd3,
+ 0x73, 0xf7, 0x11, 0x93, 0x03, 0xec, 0x09, 0x43, 0x40, 0x00, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x39, 0xf1, 0x40, 0x94, 0x1a, 0xd5, 0x73, 0xf7,
+ 0x11, 0x93, 0x00, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55,
+ 0xf1, 0x11, 0x93, 0xc1, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x55, 0xf1, 0x40, 0xf0, 0xe0, 0xf1, 0x41, 0x96, 0x1b, 0xd7, 0xc1,
+ 0xec, 0x0f, 0x9f, 0x55, 0xf1, 0x01, 0x94, 0x0a, 0xb3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, 0xf1, 0x40, 0xf0, 0x7c,
+ 0xfb, 0x01, 0x96, 0x0b, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0x5e, 0xf1, 0x40, 0xf0, 0x87, 0xfb, 0x11, 0x93, 0x10,
+ 0xec, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x67, 0xf1, 0x44, 0x92,
+ 0x0f, 0x9f, 0x6b, 0xf1, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x6d,
+ 0xf1, 0x19, 0xd3, 0x0b, 0xbc, 0x40, 0x94, 0x1a, 0xd5, 0x10, 0xec,
+ 0xc5, 0x96, 0x0b, 0xb3, 0x80, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0xba, 0xf1, 0x11, 0x93, 0x28, 0xbc, 0x01, 0xd2, 0x09, 0xb3,
+ 0x40, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x82, 0xf1, 0x40,
+ 0xf0, 0xb5, 0xf6, 0x01, 0x94, 0x0a, 0xb3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf1, 0x40, 0xf0, 0x6d, 0xee, 0x40,
+ 0xf0, 0x8f, 0xfb, 0x40, 0xf0, 0xc3, 0xf1, 0x40, 0x96, 0x1b, 0xd7,
+ 0x00, 0xec, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, 0x0a,
+ 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xb1, 0xf1,
+ 0x40, 0xf0, 0x9e, 0xfb, 0x09, 0x63, 0x00, 0x44, 0x01, 0x97, 0xc3,
+ 0x94, 0x48, 0xa4, 0xc1, 0xd4, 0x00, 0xee, 0x40, 0x92, 0x19, 0xd3,
+ 0x12, 0x95, 0x19, 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19,
+ 0xd3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94,
+ 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xba,
+ 0xf1, 0x40, 0xf0, 0xae, 0xfb, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97,
+ 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3,
+ 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xeb,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09,
+ 0x63, 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xe8, 0x43,
+ 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a,
+ 0x65, 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff,
+ 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09,
+ 0xb3, 0xfb, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x02,
+ 0x80, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00,
+ 0x09, 0x93, 0x00, 0x09, 0x19, 0xd3, 0x02, 0x80, 0x40, 0xf0, 0xfe,
+ 0xf1, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0xc8, 0xd2, 0x09, 0x93,
+ 0x91, 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0,
+ 0xd8, 0xf4, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x2d, 0xf2, 0x0a,
+ 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2,
+ 0x0f, 0x9f, 0x3a, 0xf2, 0x40, 0xf0, 0x3c, 0xf2, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x3a, 0xf2, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec,
+ 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xf1, 0xbd,
+ 0x19, 0xd3, 0xb6, 0xec, 0x11, 0x93, 0xb4, 0xec, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x54, 0xf2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xc3, 0x94, 0x0a, 0x07, 0x07, 0x00, 0xc1, 0xd6, 0x0a, 0x05, 0x00,
+ 0xa0, 0x1a, 0xd5, 0x96, 0xec, 0x11, 0x93, 0xb6, 0xec, 0x19, 0xd3,
+ 0x01, 0x80, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x41,
+ 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x13, 0x97, 0xb4, 0xec, 0x40,
+ 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xc3, 0xf2, 0x12, 0x95, 0x96, 0xec,
+ 0x0a, 0x03, 0x07, 0x00, 0xc1, 0x92, 0xc2, 0xd2, 0x11, 0x93, 0x96,
+ 0xec, 0x09, 0x05, 0x01, 0x00, 0x48, 0x02, 0xc1, 0x92, 0xc2, 0xd2,
+ 0x11, 0x93, 0x96, 0xec, 0x4e, 0x02, 0xc1, 0x94, 0xc5, 0xd6, 0xc5,
+ 0x92, 0x11, 0x07, 0x96, 0xec, 0x0b, 0x03, 0x0f, 0x00, 0xc1, 0x98,
+ 0x46, 0x06, 0x7a, 0x93, 0x79, 0x93, 0x5c, 0x95, 0x5a, 0x95, 0x02,
+ 0xa3, 0xc3, 0xd2, 0x04, 0x95, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6,
+ 0x42, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0x7d, 0xf2, 0x11, 0x93, 0x96,
+ 0xec, 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xc1, 0x92, 0xc2, 0xd2,
+ 0x11, 0x93, 0x96, 0xec, 0xc1, 0x92, 0x09, 0xb5, 0x1f, 0x00, 0x43,
+ 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0xaa, 0xf2, 0x40, 0x44, 0x02, 0x4e,
+ 0x0f, 0x9f, 0xab, 0xf2, 0x0a, 0x05, 0xff, 0xff, 0x0f, 0x9f, 0xab,
+ 0xf2, 0x43, 0x94, 0x11, 0x93, 0x96, 0xec, 0x42, 0x02, 0xc1, 0xd4,
+ 0x13, 0x97, 0x96, 0xec, 0x03, 0x93, 0xd1, 0x94, 0x7a, 0x95, 0x7a,
+ 0x95, 0xc1, 0x92, 0x59, 0x93, 0x59, 0x93, 0x01, 0x05, 0x49, 0x06,
+ 0xc3, 0x92, 0x7f, 0xb2, 0x01, 0x05, 0x1a, 0xd5, 0xb4, 0xec, 0x0a,
+ 0x05, 0xf2, 0xff, 0x1a, 0xd5, 0x92, 0xec, 0x11, 0x93, 0x92, 0xec,
+ 0x12, 0x95, 0xb6, 0xec, 0x02, 0x43, 0x02, 0x8e, 0x0f, 0x9f, 0x11,
+ 0xf3, 0x02, 0x0e, 0x0f, 0x9f, 0xe4, 0xf2, 0x11, 0x93, 0x7a, 0xf7,
+ 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09,
+ 0xa3, 0x80, 0x00, 0x19, 0xd3, 0x79, 0xf7, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x95, 0xc2, 0x94, 0x1a, 0xd5, 0xb5, 0xec, 0x40, 0x96, 0x1b,
+ 0xd7, 0xb4, 0xec, 0x0f, 0x9f, 0x29, 0xf3, 0x11, 0x93, 0x03, 0x80,
+ 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xf6,
+ 0xf2, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0xf6, 0xf2, 0x40, 0xf0, 0x3d, 0xf3, 0x0f, 0x9f, 0x2b, 0xf3, 0x41,
+ 0x92, 0xc8, 0xd2, 0x0a, 0x95, 0x91, 0xec, 0xc8, 0xd4, 0x40, 0xf0,
+ 0xd0, 0xee, 0x42, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0x09, 0xf3, 0x42, 0x96, 0x1b, 0xd7, 0xc0, 0xec,
+ 0x0f, 0x9f, 0x2b, 0xf3, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3,
+ 0x92, 0x42, 0xa2, 0xc2, 0xd2, 0x0f, 0x9f, 0x2b, 0xf3, 0x12, 0x45,
+ 0x03, 0xec, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf3, 0x11, 0x93, 0x7a,
+ 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7,
+ 0x09, 0xa3, 0x00, 0x08, 0x19, 0xd3, 0x79, 0xf7, 0x1a, 0xd5, 0x92,
+ 0xec, 0x11, 0x93, 0x92, 0xec, 0x19, 0x25, 0x92, 0xec, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x41, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0x3d, 0xf3,
+ 0x40, 0x92, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, 0xc8, 0xd2, 0x40,
+ 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x75, 0xf7, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0x4d, 0xf3, 0x0a, 0x65, 0xbc, 0x69, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0x83, 0x00, 0x02, 0xc2, 0xd2, 0x11, 0x93, 0x03,
+ 0x80, 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x60, 0xf3, 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a,
+ 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x20, 0x19, 0xd3,
+ 0x79, 0xf7, 0x11, 0x93, 0xb5, 0xec, 0x19, 0xd3, 0x04, 0x80, 0x12,
+ 0x95, 0xb4, 0xec, 0x1a, 0xd5, 0x05, 0x80, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x97, 0xc3, 0x96, 0x1b, 0xd7, 0xb5, 0xec, 0x40, 0x94, 0x1a,
+ 0xd5, 0xb4, 0xec, 0x19, 0xd3, 0xf2, 0xbd, 0x88, 0x98, 0x90, 0x9a,
+ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19,
+ 0xd3, 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01,
+ 0x00, 0x11, 0x93, 0x01, 0x82, 0xc5, 0xd2, 0x40, 0x94, 0x01, 0xd4,
+ 0x13, 0x97, 0xb8, 0xec, 0x02, 0xd6, 0x03, 0x95, 0x0c, 0x99, 0xbb,
+ 0xec, 0x04, 0x05, 0x13, 0x97, 0x03, 0xec, 0x01, 0x27, 0x02, 0x99,
+ 0xc4, 0x92, 0x03, 0x03, 0xc2, 0xd2, 0x14, 0x99, 0xba, 0xec, 0x03,
+ 0x09, 0x1c, 0xd9, 0xba, 0xec, 0x12, 0x95, 0x04, 0x82, 0x0a, 0xb3,
+ 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xc6, 0xf4, 0x01,
+ 0x92, 0x03, 0xd2, 0x0a, 0xa3, 0x02, 0x00, 0x19, 0xd3, 0x04, 0x82,
+ 0x02, 0x96, 0x0b, 0x05, 0x01, 0x00, 0x1a, 0xd5, 0xb8, 0xec, 0xc5,
+ 0x92, 0x43, 0x42, 0x02, 0x9e, 0x0f, 0x9f, 0xce, 0xf3, 0x42, 0x44,
+ 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x40,
+ 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xce, 0xf3, 0x0c, 0x49, 0xd3, 0x08,
+ 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x63, 0x07, 0x82, 0x11,
+ 0xa3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, 0x79, 0x93, 0x79, 0x93,
+ 0x03, 0xd2, 0xc5, 0x94, 0x0a, 0xb5, 0xfc, 0xff, 0x04, 0xd4, 0x03,
+ 0x96, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xdd, 0xf3, 0x11, 0x93,
+ 0xb8, 0xec, 0x41, 0x42, 0x02, 0x8e, 0x0f, 0x9f, 0xe4, 0xf3, 0xc5,
+ 0x98, 0x0c, 0x03, 0xff, 0xff, 0x42, 0x42, 0x02, 0x8e, 0x0f, 0x9f,
+ 0x0b, 0xf4, 0x0a, 0x95, 0xbb, 0xec, 0x42, 0x92, 0x19, 0xd3, 0xb9,
+ 0xec, 0xc5, 0x96, 0x43, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0xfd, 0xf3,
+ 0x0b, 0x07, 0xfc, 0xff, 0xc5, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xc8,
+ 0xbc, 0xd2, 0x96, 0x1b, 0xd7, 0xca, 0xbc, 0x09, 0x03, 0xff, 0xff,
+ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe9, 0xf3, 0x19, 0xd3, 0xb9,
+ 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x09, 0xf4, 0x0a, 0x05,
+ 0xfe, 0xff, 0xca, 0xd2, 0xc2, 0xd2, 0x0f, 0x9f, 0x0b, 0xf4, 0x1a,
+ 0xd5, 0x93, 0xec, 0x03, 0x98, 0x40, 0x48, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x38, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x9e, 0x0f,
+ 0x9f, 0x1b, 0xf4, 0x04, 0x94, 0x48, 0x44, 0x02, 0x4e, 0x0f, 0x9f,
+ 0x26, 0xf4, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x38, 0xf4, 0x11,
+ 0x93, 0x04, 0x82, 0x41, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f,
+ 0x38, 0xf4, 0x41, 0x96, 0x01, 0xd6, 0x0a, 0x65, 0xbd, 0x43, 0x02,
+ 0x99, 0xc4, 0x92, 0x09, 0xa3, 0x80, 0x00, 0xc2, 0xd2, 0x0a, 0x65,
+ 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2,
+ 0xd2, 0x0f, 0x9f, 0x97, 0xf4, 0xc5, 0x98, 0x43, 0x48, 0x02, 0x9e,
+ 0x0f, 0x9f, 0x97, 0xf4, 0x4f, 0x96, 0x0c, 0xb3, 0x01, 0x00, 0x40,
+ 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x45, 0xf4, 0x47, 0x96, 0x11, 0x93,
+ 0xb7, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x73, 0xf4, 0x11,
+ 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x73, 0xf4,
+ 0x12, 0x95, 0x00, 0x82, 0x0a, 0x05, 0xff, 0xaf, 0x05, 0xd4, 0xc8,
+ 0xd6, 0xc8, 0xd2, 0x40, 0xf0, 0x18, 0xf7, 0x42, 0x00, 0x05, 0x96,
+ 0xc3, 0x94, 0x01, 0xb5, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x68,
+ 0xf4, 0x11, 0x93, 0xba, 0xec, 0x4d, 0x42, 0x02, 0x8e, 0x0f, 0x9f,
+ 0x73, 0xf4, 0x06, 0x98, 0x50, 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40,
+ 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, 0x92, 0x03, 0xd2, 0x0f, 0x9f,
+ 0x9c, 0xf4, 0x03, 0x94, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x80,
+ 0xf4, 0x0a, 0x65, 0x5e, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x48, 0xa2,
+ 0xc2, 0xd2, 0x0f, 0x9f, 0x9c, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x0c,
+ 0x99, 0xbb, 0xec, 0x04, 0x03, 0x04, 0x96, 0x13, 0x25, 0x03, 0xec,
+ 0xc1, 0xd4, 0x11, 0x93, 0xba, 0xec, 0x19, 0x05, 0xba, 0xec, 0x1b,
+ 0xd7, 0x01, 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x99, 0xc4, 0x92,
+ 0x43, 0xa2, 0xc2, 0xd2, 0x41, 0x92, 0x01, 0xd2, 0x03, 0x94, 0x40,
+ 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x11, 0x93, 0xb9, 0xec,
+ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa8, 0xf4, 0x19, 0xd3, 0xb8,
+ 0xec, 0x19, 0xd3, 0xba, 0xec, 0x19, 0xd3, 0xbb, 0xec, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x41, 0x98, 0x1c,
+ 0xd9, 0xb7, 0xec, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e,
+ 0x0f, 0x9f, 0xc1, 0xf4, 0x11, 0x93, 0x00, 0x82, 0x19, 0xd3, 0x02,
+ 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3,
+ 0x00, 0x01, 0xc2, 0xd2, 0x40, 0x98, 0x1c, 0xd9, 0xbf, 0xec, 0x0f,
+ 0x9f, 0xc9, 0xf4, 0x01, 0x92, 0x19, 0xd3, 0xb7, 0xec, 0x01, 0x94,
+ 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xd5, 0xf4, 0x0a, 0x65, 0xea,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0xc2, 0xd2,
+ 0x47, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x12, 0x95, 0x03, 0x80, 0x0a, 0xb3, 0x00, 0x40, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0xf4, 0xf4, 0x0a, 0xb7, 0x00, 0x08, 0x40,
+ 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xf7, 0xf4, 0x11, 0x93, 0x03, 0xec,
+ 0x41, 0x02, 0x09, 0xb3, 0xfe, 0xff, 0x12, 0x95, 0x07, 0x80, 0x01,
+ 0x45, 0x02, 0x8e, 0x0f, 0x9f, 0xf7, 0xf4, 0x41, 0x92, 0x0f, 0x9f,
+ 0xf8, 0xf4, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x41,
+ 0x20, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x13, 0x97, 0x6e,
+ 0xec, 0x0b, 0x47, 0xa0, 0x00, 0x02, 0x5e, 0x0f, 0x9f, 0x23, 0xf5,
+ 0x09, 0x63, 0x08, 0x43, 0x0a, 0x65, 0xff, 0x5f, 0x01, 0x99, 0xc4,
+ 0xd4, 0x0a, 0x95, 0x9b, 0xec, 0xd2, 0x96, 0x1b, 0xd7, 0xfa, 0xbc,
+ 0xd2, 0x96, 0xc4, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0xd2,
+ 0x96, 0xc1, 0xd6, 0xc2, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x0f, 0x9f,
+ 0x61, 0xf5, 0x0c, 0x69, 0xff, 0x6f, 0x1c, 0xd9, 0xf8, 0xbc, 0x0b,
+ 0x47, 0x10, 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0x3b, 0xf5, 0x0a, 0x95,
+ 0x6f, 0xec, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, 0xc4, 0xd6, 0xd2,
+ 0x96, 0x1b, 0xd7, 0xf8, 0xbc, 0x0c, 0x69, 0xee, 0x6a, 0xc1, 0xd8,
+ 0xc2, 0x94, 0x1a, 0xd5, 0xf8, 0xbc, 0x40, 0x92, 0xc5, 0xd2, 0x11,
+ 0x43, 0xc2, 0xec, 0x02, 0x0e, 0x0f, 0x9f, 0x5e, 0xf5, 0xc5, 0x94,
+ 0x0a, 0x03, 0x71, 0xec, 0xc1, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x11,
+ 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x50, 0xf5,
+ 0x0a, 0x95, 0x6f, 0xec, 0xc8, 0xd4, 0x40, 0xf0, 0x39, 0xf7, 0x19,
+ 0xd3, 0xf8, 0xbc, 0x41, 0x00, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6,
+ 0x13, 0x47, 0xc2, 0xec, 0x02, 0x1e, 0x0f, 0x9f, 0x42, 0xf5, 0x40,
+ 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0x40, 0x92, 0x19, 0xd3, 0x6e, 0xec,
+ 0x19, 0xd3, 0xc2, 0xec, 0x0a, 0x65, 0x52, 0x43, 0x02, 0x97, 0xc3,
+ 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x99,
+ 0xc4, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x41, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x43, 0x20, 0x08, 0x0b, 0x01, 0x00,
+ 0x06, 0x92, 0x01, 0xd2, 0x0a, 0x65, 0xf0, 0x6a, 0x0b, 0x97, 0x6f,
+ 0xec, 0x02, 0x99, 0xc4, 0x98, 0xd3, 0xd8, 0x02, 0xd6, 0x0a, 0x03,
+ 0x02, 0x00, 0x01, 0x97, 0xc3, 0x98, 0x02, 0x96, 0xc3, 0xd8, 0x01,
+ 0x96, 0xc1, 0xd6, 0x1a, 0xd5, 0x6e, 0xec, 0xc5, 0x98, 0x14, 0x99,
+ 0x6f, 0xec, 0xc2, 0xd8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88,
+ 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0xc8, 0xd2, 0x40, 0xf0,
+ 0x76, 0xf5, 0x41, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0xb0, 0xf5, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0xad, 0xf5, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x42,
+ 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0xc0, 0xec, 0x0a, 0x65,
+ 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2,
+ 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3,
+ 0xbf, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x63,
+ 0x20, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xaf, 0xbc, 0x47, 0xb2,
+ 0x59, 0x95, 0x5a, 0x95, 0x12, 0xa5, 0xbf, 0xbc, 0x0a, 0xb3, 0x01,
+ 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xd2, 0xf5, 0x41, 0x04,
+ 0x05, 0x93, 0x40, 0x96, 0x20, 0xd6, 0x62, 0x97, 0x0f, 0x9f, 0xe1,
+ 0xf5, 0x14, 0x99, 0xfc, 0xbc, 0xd1, 0xd8, 0x14, 0x99, 0xfe, 0xbc,
+ 0xd1, 0xd8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x20, 0x98, 0x03,
+ 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0xd8, 0xf5, 0xc5, 0x92, 0x62, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0xfa, 0xf5, 0x02, 0x8e, 0x0f, 0x9f, 0xf4,
+ 0xf5, 0x61, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1e, 0xf6, 0x0f, 0x9f,
+ 0x4b, 0xf6, 0x63, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x41, 0xf6, 0x0f,
+ 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x0c, 0x99, 0x71, 0xec,
+ 0x0b, 0x05, 0xff, 0xff, 0x40, 0x96, 0x0f, 0x9f, 0x07, 0xf6, 0xd1,
+ 0x96, 0xd4, 0xd6, 0x20, 0x96, 0x41, 0x06, 0x20, 0xd6, 0x02, 0x47,
+ 0x02, 0x1e, 0x0f, 0x9f, 0x03, 0xf6, 0x1a, 0xd5, 0xc2, 0xec, 0x0a,
+ 0x65, 0xeb, 0x43, 0x02, 0x99, 0xc4, 0x92, 0x09, 0xa3, 0xc0, 0x00,
+ 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09,
+ 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x0f, 0x9f, 0x4b, 0xf6, 0x0a, 0x03,
+ 0xfe, 0xff, 0x61, 0x95, 0x40, 0x98, 0x20, 0xd8, 0x02, 0x49, 0x02,
+ 0x0e, 0x0f, 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x21, 0xd2,
+ 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, 0xc8, 0xd2, 0x21, 0x96, 0xc3,
+ 0x92, 0x42, 0x06, 0x21, 0xd6, 0xc8, 0xd2, 0x22, 0xd4, 0x40, 0xf0,
+ 0xa2, 0xf0, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x22,
+ 0x94, 0x02, 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0x2a, 0xf6, 0x0f, 0x9f,
+ 0x4b, 0xf6, 0x0d, 0x03, 0x03, 0x00, 0xc8, 0xd2, 0x02, 0x92, 0xc8,
+ 0xd2, 0x01, 0x96, 0xc8, 0xd6, 0x40, 0xf0, 0x4e, 0xf6, 0x43, 0x00,
+ 0x63, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x45, 0x20, 0x08,
+ 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x08, 0x00, 0x08, 0x94, 0xc5, 0xd4,
+ 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, 0x03, 0xd4, 0x42, 0x02, 0xc1,
+ 0x92, 0x01, 0xd2, 0x02, 0x97, 0xc5, 0x94, 0x0a, 0x83, 0xff, 0xff,
+ 0x11, 0xb3, 0x2c, 0x93, 0x09, 0xb3, 0xfb, 0xff, 0x19, 0xd3, 0x2c,
+ 0x93, 0x03, 0x92, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x81, 0xf6,
+ 0x01, 0x94, 0xd2, 0x92, 0x19, 0xd3, 0x2c, 0x93, 0x01, 0xd4, 0x02,
+ 0x94, 0x12, 0x95, 0x2c, 0x93, 0x44, 0xa4, 0x1a, 0xd5, 0x2c, 0x93,
+ 0x0a, 0xb5, 0xfb, 0xff, 0x1a, 0xd5, 0x2c, 0x93, 0x0b, 0x07, 0xff,
+ 0xff, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x6c, 0xf6, 0x09, 0x63,
+ 0xd4, 0x6c, 0x01, 0x95, 0xc2, 0x96, 0xc5, 0x94, 0x02, 0xa7, 0xc1,
+ 0xd6, 0x03, 0x92, 0x54, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x91, 0xf6,
+ 0x0a, 0x83, 0xff, 0xff, 0x1b, 0xb3, 0x2c, 0x93, 0x45, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63,
+ 0x00, 0x40, 0x19, 0xd3, 0xf2, 0xbd, 0x40, 0xf0, 0xd8, 0xf4, 0x40,
+ 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa5, 0xf6, 0x40, 0xf0, 0x3c, 0xf2,
+ 0x0f, 0x9f, 0xb3, 0xf6, 0x40, 0x96, 0xc8, 0xd6, 0x09, 0x93, 0x91,
+ 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x0a, 0x65, 0xfe, 0x7f,
+ 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65,
+ 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2,
+ 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3,
+ 0xfb, 0xff, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0x2d, 0xbc, 0x0a,
+ 0x65, 0xd8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff,
+ 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x09, 0x63, 0xea, 0x43, 0x01, 0x97, 0xc3, 0x94, 0x44, 0xa4,
+ 0xc1, 0xd4, 0x11, 0x93, 0xb9, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x0c, 0xf7, 0x12, 0x95, 0x93, 0xec, 0x0b, 0x67, 0x36, 0x43,
+ 0xd2, 0x98, 0x1c, 0xd9, 0xc8, 0xbc, 0xd2, 0x98, 0x03, 0x93, 0xc1,
+ 0xd8, 0x11, 0x93, 0xb9, 0xec, 0x09, 0x03, 0xff, 0xff, 0x19, 0xd3,
+ 0xb9, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe5, 0xf6, 0x19,
+ 0xd3, 0xb8, 0xec, 0x19, 0xd3, 0xba, 0xec, 0x0a, 0x05, 0xfe, 0xff,
+ 0xca, 0xd2, 0xca, 0xd2, 0xc2, 0xd2, 0x0a, 0x65, 0x5e, 0x43, 0x02,
+ 0x97, 0xc3, 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43,
+ 0x02, 0x99, 0xc4, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0x0f, 0x9f, 0x15,
+ 0xf7, 0x11, 0x93, 0x03, 0xec, 0x19, 0xd3, 0x01, 0x82, 0x0a, 0x65,
+ 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x43, 0xa2, 0xc2, 0xd2, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x92,
+ 0x04, 0x96, 0x0d, 0x5e, 0x50, 0x46, 0x02, 0x0e, 0x40, 0x92, 0x09,
+ 0xee, 0x44, 0x46, 0x04, 0x0e, 0x59, 0x93, 0x44, 0x26, 0x04, 0x5e,
+ 0x46, 0xee, 0x41, 0x93, 0x41, 0x26, 0x43, 0x4e, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0xb1, 0xfe,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x94,
+ 0x1a, 0xd5, 0x40, 0xf7, 0x11, 0x93, 0x00, 0x90, 0x88, 0x98, 0x90,
+ 0x9a, 0x1d, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x03, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00,
+ 0x21, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf2, 0x6b, 0xf7, 0x00, 0x00,
+ 0x1c, 0xf2, 0x6b, 0xf7, 0x00, 0x00, 0x61, 0xf2, 0x68, 0xf7, 0x6f,
+ 0xf7, 0x00, 0x00, 0x2e, 0xf3, 0x6b, 0xf7, 0x25, 0x47, 0x01, 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, 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, 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,
+ 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, 0x00
+};
diff --git a/freebsd/sys/dev/usb/wlan/if_zydreg.h b/freebsd/sys/dev/usb/wlan/if_zydreg.h
new file mode 100644
index 00000000..724b8c57
--- /dev/null
+++ b/freebsd/sys/dev/usb/wlan/if_zydreg.h
@@ -0,0 +1,1319 @@
+/* $OpenBSD: if_zydreg.h,v 1.19 2006/11/30 19:28:07 damien Exp $ */
+/* $NetBSD: if_zydreg.h,v 1.2 2007/06/16 11:18:45 kiyohara Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * ZyDAS ZD1211/ZD1211B USB WLAN driver.
+ */
+
+#define ZYD_CR_GPI_EN 0x9418
+#define ZYD_CR_RADIO_PD 0x942c
+#define ZYD_CR_RF2948_PD 0x942c
+#define ZYD_CR_EN_PS_MANUAL_AGC 0x943c
+#define ZYD_CR_CONFIG_PHILIPS 0x9440
+#define ZYD_CR_I2C_WRITE 0x9444
+#define ZYD_CR_SA2400_SER_RP 0x9448
+#define ZYD_CR_RADIO_PE 0x9458
+#define ZYD_CR_RST_BUS_MASTER 0x945c
+#define ZYD_CR_RFCFG 0x9464
+#define ZYD_CR_HSTSCHG 0x946c
+#define ZYD_CR_PHY_ON 0x9474
+#define ZYD_CR_RX_DELAY 0x9478
+#define ZYD_CR_RX_PE_DELAY 0x947c
+#define ZYD_CR_GPIO_1 0x9490
+#define ZYD_CR_GPIO_2 0x9494
+#define ZYD_CR_EnZYD_CRyBufMux 0x94a8
+#define ZYD_CR_PS_CTRL 0x9500
+#define ZYD_CR_ADDA_PWR_DWN 0x9504
+#define ZYD_CR_ADDA_MBIAS_WT 0x9508
+#define ZYD_CR_INTERRUPT 0x9510
+#define ZYD_CR_MAC_PS_STATE 0x950c
+#define ZYD_CR_ATIM_WND_PERIOD 0x951c
+#define ZYD_CR_BCN_INTERVAL 0x9520
+#define ZYD_CR_PRE_TBTT 0x9524
+
+/*
+ * MAC registers.
+ */
+#define ZYD_MAC_MACADRL 0x9610 /* MAC address (low) */
+#define ZYD_MAC_MACADRH 0x9614 /* MAC address (high) */
+#define ZYD_MAC_BSSADRL 0x9618 /* BSS address (low) */
+#define ZYD_MAC_BSSADRH 0x961c /* BSS address (high) */
+#define ZYD_MAC_BCNCFG 0x9620 /* BCN configuration */
+#define ZYD_MAC_GHTBL 0x9624 /* Group hash table (low) */
+#define ZYD_MAC_GHTBH 0x9628 /* Group hash table (high) */
+#define ZYD_MAC_RX_TIMEOUT 0x962c /* Rx timeout value */
+#define ZYD_MAC_BAS_RATE 0x9630 /* Basic rate setting */
+#define ZYD_MAC_MAN_RATE 0x9634 /* Mandatory rate setting */
+#define ZYD_MAC_RTSCTSRATE 0x9638 /* RTS CTS rate */
+#define ZYD_MAC_BACKOFF_PROTECT 0x963c /* Backoff protection */
+#define ZYD_MAC_RX_THRESHOLD 0x9640 /* Rx threshold */
+#define ZYD_MAC_TX_PE_CONTROL 0x9644 /* Tx_PE control */
+#define ZYD_MAC_AFTER_PNP 0x9648 /* After PnP */
+#define ZYD_MAC_RX_PE_DELAY 0x964c /* Rx_pe delay */
+#define ZYD_MAC_RX_ADDR2_L 0x9650 /* RX address2 (low) */
+#define ZYD_MAC_RX_ADDR2_H 0x9654 /* RX address2 (high) */
+#define ZYD_MAC_SIFS_ACK_TIME 0x9658 /* Dynamic SIFS ack time */
+#define ZYD_MAC_PHY_DELAY 0x9660 /* PHY delay */
+#define ZYD_MAC_PHY_DELAY2 0x966c /* PHY delay */
+#define ZYD_MAC_BCNFIFO 0x9670 /* Beacon FIFO I/O port */
+#define ZYD_MAC_SNIFFER 0x9674 /* Sniffer on/off */
+#define ZYD_MAC_ENCRYPTION_TYPE 0x9678 /* Encryption type */
+#define ZYD_MAC_RETRY 0x967c /* Retry time */
+#define ZYD_MAC_MISC 0x9680 /* Misc */
+#define ZYD_MAC_STMACHINESTAT 0x9684 /* State machine status */
+#define ZYD_MAC_TX_UNDERRUN_CNT 0x9688 /* TX underrun counter */
+#define ZYD_MAC_RXFILTER 0x968c /* Send to host settings */
+#define ZYD_MAC_ACK_EXT 0x9690 /* Acknowledge extension */
+#define ZYD_MAC_BCNFIFOST 0x9694 /* BCN FIFO set and status */
+#define ZYD_MAC_DIFS_EIFS_SIFS 0x9698 /* DIFS, EIFS & SIFS settings */
+#define ZYD_MAC_RX_TIMEOUT_CNT 0x969c /* RX timeout count */
+#define ZYD_MAC_RX_TOTAL_FRAME 0x96a0 /* RX total frame count */
+#define ZYD_MAC_RX_CRC32_CNT 0x96a4 /* RX CRC32 frame count */
+#define ZYD_MAC_RX_CRC16_CNT 0x96a8 /* RX CRC16 frame count */
+#define ZYD_MAC_RX_UDEC 0x96ac /* RX unicast decr. error count */
+#define ZYD_MAC_RX_OVERRUN_CNT 0x96b0 /* RX FIFO overrun count */
+#define ZYD_MAC_RX_MDEC 0x96bc /* RX multicast decr. err. cnt. */
+#define ZYD_MAC_NAV_TCR 0x96c4 /* NAV timer count read */
+#define ZYD_MAC_BACKOFF_ST_RD 0x96c8 /* Backoff status read */
+#define ZYD_MAC_DM_RETRY_CNT_RD 0x96cc /* DM retry count read */
+#define ZYD_MAC_RX_ACR 0x96d0 /* RX arbitration count read */
+#define ZYD_MAC_TX_CCR 0x96d4 /* Tx complete count read */
+#define ZYD_MAC_TCB_ADDR 0x96e8 /* Current PCI process TCP addr */
+#define ZYD_MAC_RCB_ADDR 0x96ec /* Next RCB address */
+#define ZYD_MAC_CONT_WIN_LIMIT 0x96f0 /* Contention window limit */
+#define ZYD_MAC_TX_PKT 0x96f4 /* Tx total packet count read */
+#define ZYD_MAC_DL_CTRL 0x96f8 /* Download control */
+#define ZYD_MAC_CAM_MODE 0x9700 /* CAM: Continuous Access Mode */
+#define ZYD_MACB_TXPWR_CTL1 0x9b00
+#define ZYD_MACB_TXPWR_CTL2 0x9b04
+#define ZYD_MACB_TXPWR_CTL3 0x9b08
+#define ZYD_MACB_TXPWR_CTL4 0x9b0c
+#define ZYD_MACB_AIFS_CTL1 0x9b10
+#define ZYD_MACB_AIFS_CTL2 0x9b14
+#define ZYD_MACB_TXOP 0x9b20
+#define ZYD_MACB_MAX_RETRY 0x9b28
+
+/*
+ * Miscellaneous registers.
+ */
+#define ZYD_FIRMWARE_START_ADDR 0xee00
+#define ZYD_FIRMWARE_BASE_ADDR 0xee1d /* Firmware base address */
+
+/*
+ * EEPROM registers.
+ */
+#define ZYD_EEPROM_START_HEAD 0xf800 /* EEPROM start */
+#define ZYD_EEPROM_SUBID 0xf817
+#define ZYD_EEPROM_POD 0xf819
+#define ZYD_EEPROM_MAC_ADDR_P1 0xf81b /* Part 1 of the MAC address */
+#define ZYD_EEPROM_MAC_ADDR_P2 0xf81d /* Part 2 of the MAC address */
+#define ZYD_EEPROM_PWR_CAL 0xf81f /* Calibration */
+#define ZYD_EEPROM_PWR_INT 0xf827 /* Calibration */
+#define ZYD_EEPROM_ALLOWEDCHAN 0xf82f /* Allowed CH mask, 1 bit each */
+#define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */
+#define ZYD_EEPROM_PHY_REG 0xf83c /* PHY registers */
+#define ZYD_EEPROM_36M_CAL 0xf83f /* Calibration */
+#define ZYD_EEPROM_11A_INT 0xf847 /* Interpolation */
+#define ZYD_EEPROM_48M_CAL 0xf84f /* Calibration */
+#define ZYD_EEPROM_48M_INT 0xf857 /* Interpolation */
+#define ZYD_EEPROM_54M_CAL 0xf85f /* Calibration */
+#define ZYD_EEPROM_54M_INT 0xf867 /* Interpolation */
+
+/*
+ * Firmware registers offsets (relative to fwbase).
+ */
+#define ZYD_FW_FIRMWARE_REV 0x0000 /* Firmware version */
+#define ZYD_FW_USB_SPEED 0x0001 /* USB speed (!=0 if highspeed) */
+#define ZYD_FW_FIX_TX_RATE 0x0002 /* Fixed TX rate */
+#define ZYD_FW_LINK_STATUS 0x0003
+#define ZYD_FW_SOFT_RESET 0x0004
+#define ZYD_FW_FLASH_CHK 0x0005
+
+/* possible flags for register ZYD_FW_LINK_STATUS */
+#define ZYD_LED1 (1 << 8)
+#define ZYD_LED2 (1 << 9)
+
+/*
+ * RF IDs.
+ */
+#define ZYD_RF_UW2451 0x2 /* not supported yet */
+#define ZYD_RF_UCHIP 0x3 /* not supported yet */
+#define ZYD_RF_AL2230 0x4
+#define ZYD_RF_AL7230B 0x5
+#define ZYD_RF_THETA 0x6 /* not supported yet */
+#define ZYD_RF_AL2210 0x7
+#define ZYD_RF_MAXIM_NEW 0x8
+#define ZYD_RF_GCT 0x9
+#define ZYD_RF_AL2230S 0xa /* not supported yet */
+#define ZYD_RF_RALINK 0xb /* not supported yet */
+#define ZYD_RF_INTERSIL 0xc /* not supported yet */
+#define ZYD_RF_RFMD 0xd
+#define ZYD_RF_MAXIM_NEW2 0xe
+#define ZYD_RF_PHILIPS 0xf /* not supported yet */
+
+/*
+ * PHY registers (8 bits, not documented).
+ */
+#define ZYD_CR0 0x9000
+#define ZYD_CR1 0x9004
+#define ZYD_CR2 0x9008
+#define ZYD_CR3 0x900c
+#define ZYD_CR5 0x9010
+#define ZYD_CR6 0x9014
+#define ZYD_CR7 0x9018
+#define ZYD_CR8 0x901c
+#define ZYD_CR4 0x9020
+#define ZYD_CR9 0x9024
+#define ZYD_CR10 0x9028
+#define ZYD_CR11 0x902c
+#define ZYD_CR12 0x9030
+#define ZYD_CR13 0x9034
+#define ZYD_CR14 0x9038
+#define ZYD_CR15 0x903c
+#define ZYD_CR16 0x9040
+#define ZYD_CR17 0x9044
+#define ZYD_CR18 0x9048
+#define ZYD_CR19 0x904c
+#define ZYD_CR20 0x9050
+#define ZYD_CR21 0x9054
+#define ZYD_CR22 0x9058
+#define ZYD_CR23 0x905c
+#define ZYD_CR24 0x9060
+#define ZYD_CR25 0x9064
+#define ZYD_CR26 0x9068
+#define ZYD_CR27 0x906c
+#define ZYD_CR28 0x9070
+#define ZYD_CR29 0x9074
+#define ZYD_CR30 0x9078
+#define ZYD_CR31 0x907c
+#define ZYD_CR32 0x9080
+#define ZYD_CR33 0x9084
+#define ZYD_CR34 0x9088
+#define ZYD_CR35 0x908c
+#define ZYD_CR36 0x9090
+#define ZYD_CR37 0x9094
+#define ZYD_CR38 0x9098
+#define ZYD_CR39 0x909c
+#define ZYD_CR40 0x90a0
+#define ZYD_CR41 0x90a4
+#define ZYD_CR42 0x90a8
+#define ZYD_CR43 0x90ac
+#define ZYD_CR44 0x90b0
+#define ZYD_CR45 0x90b4
+#define ZYD_CR46 0x90b8
+#define ZYD_CR47 0x90bc
+#define ZYD_CR48 0x90c0
+#define ZYD_CR49 0x90c4
+#define ZYD_CR50 0x90c8
+#define ZYD_CR51 0x90cc
+#define ZYD_CR52 0x90d0
+#define ZYD_CR53 0x90d4
+#define ZYD_CR54 0x90d8
+#define ZYD_CR55 0x90dc
+#define ZYD_CR56 0x90e0
+#define ZYD_CR57 0x90e4
+#define ZYD_CR58 0x90e8
+#define ZYD_CR59 0x90ec
+#define ZYD_CR60 0x90f0
+#define ZYD_CR61 0x90f4
+#define ZYD_CR62 0x90f8
+#define ZYD_CR63 0x90fc
+#define ZYD_CR64 0x9100
+#define ZYD_CR65 0x9104
+#define ZYD_CR66 0x9108
+#define ZYD_CR67 0x910c
+#define ZYD_CR68 0x9110
+#define ZYD_CR69 0x9114
+#define ZYD_CR70 0x9118
+#define ZYD_CR71 0x911c
+#define ZYD_CR72 0x9120
+#define ZYD_CR73 0x9124
+#define ZYD_CR74 0x9128
+#define ZYD_CR75 0x912c
+#define ZYD_CR76 0x9130
+#define ZYD_CR77 0x9134
+#define ZYD_CR78 0x9138
+#define ZYD_CR79 0x913c
+#define ZYD_CR80 0x9140
+#define ZYD_CR81 0x9144
+#define ZYD_CR82 0x9148
+#define ZYD_CR83 0x914c
+#define ZYD_CR84 0x9150
+#define ZYD_CR85 0x9154
+#define ZYD_CR86 0x9158
+#define ZYD_CR87 0x915c
+#define ZYD_CR88 0x9160
+#define ZYD_CR89 0x9164
+#define ZYD_CR90 0x9168
+#define ZYD_CR91 0x916c
+#define ZYD_CR92 0x9170
+#define ZYD_CR93 0x9174
+#define ZYD_CR94 0x9178
+#define ZYD_CR95 0x917c
+#define ZYD_CR96 0x9180
+#define ZYD_CR97 0x9184
+#define ZYD_CR98 0x9188
+#define ZYD_CR99 0x918c
+#define ZYD_CR100 0x9190
+#define ZYD_CR101 0x9194
+#define ZYD_CR102 0x9198
+#define ZYD_CR103 0x919c
+#define ZYD_CR104 0x91a0
+#define ZYD_CR105 0x91a4
+#define ZYD_CR106 0x91a8
+#define ZYD_CR107 0x91ac
+#define ZYD_CR108 0x91b0
+#define ZYD_CR109 0x91b4
+#define ZYD_CR110 0x91b8
+#define ZYD_CR111 0x91bc
+#define ZYD_CR112 0x91c0
+#define ZYD_CR113 0x91c4
+#define ZYD_CR114 0x91c8
+#define ZYD_CR115 0x91cc
+#define ZYD_CR116 0x91d0
+#define ZYD_CR117 0x91d4
+#define ZYD_CR118 0x91d8
+#define ZYD_CR119 0x91dc
+#define ZYD_CR120 0x91e0
+#define ZYD_CR121 0x91e4
+#define ZYD_CR122 0x91e8
+#define ZYD_CR123 0x91ec
+#define ZYD_CR124 0x91f0
+#define ZYD_CR125 0x91f4
+#define ZYD_CR126 0x91f8
+#define ZYD_CR127 0x91fc
+#define ZYD_CR128 0x9200
+#define ZYD_CR129 0x9204
+#define ZYD_CR130 0x9208
+#define ZYD_CR131 0x920c
+#define ZYD_CR132 0x9210
+#define ZYD_CR133 0x9214
+#define ZYD_CR134 0x9218
+#define ZYD_CR135 0x921c
+#define ZYD_CR136 0x9220
+#define ZYD_CR137 0x9224
+#define ZYD_CR138 0x9228
+#define ZYD_CR139 0x922c
+#define ZYD_CR140 0x9230
+#define ZYD_CR141 0x9234
+#define ZYD_CR142 0x9238
+#define ZYD_CR143 0x923c
+#define ZYD_CR144 0x9240
+#define ZYD_CR145 0x9244
+#define ZYD_CR146 0x9248
+#define ZYD_CR147 0x924c
+#define ZYD_CR148 0x9250
+#define ZYD_CR149 0x9254
+#define ZYD_CR150 0x9258
+#define ZYD_CR151 0x925c
+#define ZYD_CR152 0x9260
+#define ZYD_CR153 0x9264
+#define ZYD_CR154 0x9268
+#define ZYD_CR155 0x926c
+#define ZYD_CR156 0x9270
+#define ZYD_CR157 0x9274
+#define ZYD_CR158 0x9278
+#define ZYD_CR159 0x927c
+#define ZYD_CR160 0x9280
+#define ZYD_CR161 0x9284
+#define ZYD_CR162 0x9288
+#define ZYD_CR163 0x928c
+#define ZYD_CR164 0x9290
+#define ZYD_CR165 0x9294
+#define ZYD_CR166 0x9298
+#define ZYD_CR167 0x929c
+#define ZYD_CR168 0x92a0
+#define ZYD_CR169 0x92a4
+#define ZYD_CR170 0x92a8
+#define ZYD_CR171 0x92ac
+#define ZYD_CR172 0x92b0
+#define ZYD_CR173 0x92b4
+#define ZYD_CR174 0x92b8
+#define ZYD_CR175 0x92bc
+#define ZYD_CR176 0x92c0
+#define ZYD_CR177 0x92c4
+#define ZYD_CR178 0x92c8
+#define ZYD_CR179 0x92cc
+#define ZYD_CR180 0x92d0
+#define ZYD_CR181 0x92d4
+#define ZYD_CR182 0x92d8
+#define ZYD_CR183 0x92dc
+#define ZYD_CR184 0x92e0
+#define ZYD_CR185 0x92e4
+#define ZYD_CR186 0x92e8
+#define ZYD_CR187 0x92ec
+#define ZYD_CR188 0x92f0
+#define ZYD_CR189 0x92f4
+#define ZYD_CR190 0x92f8
+#define ZYD_CR191 0x92fc
+#define ZYD_CR192 0x9300
+#define ZYD_CR193 0x9304
+#define ZYD_CR194 0x9308
+#define ZYD_CR195 0x930c
+#define ZYD_CR196 0x9310
+#define ZYD_CR197 0x9314
+#define ZYD_CR198 0x9318
+#define ZYD_CR199 0x931c
+#define ZYD_CR200 0x9320
+#define ZYD_CR201 0x9324
+#define ZYD_CR202 0x9328
+#define ZYD_CR203 0x932c
+#define ZYD_CR204 0x9330
+#define ZYD_CR205 0x9334
+#define ZYD_CR206 0x9338
+#define ZYD_CR207 0x933c
+#define ZYD_CR208 0x9340
+#define ZYD_CR209 0x9344
+#define ZYD_CR210 0x9348
+#define ZYD_CR211 0x934c
+#define ZYD_CR212 0x9350
+#define ZYD_CR213 0x9354
+#define ZYD_CR214 0x9358
+#define ZYD_CR215 0x935c
+#define ZYD_CR216 0x9360
+#define ZYD_CR217 0x9364
+#define ZYD_CR218 0x9368
+#define ZYD_CR219 0x936c
+#define ZYD_CR220 0x9370
+#define ZYD_CR221 0x9374
+#define ZYD_CR222 0x9378
+#define ZYD_CR223 0x937c
+#define ZYD_CR224 0x9380
+#define ZYD_CR225 0x9384
+#define ZYD_CR226 0x9388
+#define ZYD_CR227 0x938c
+#define ZYD_CR228 0x9390
+#define ZYD_CR229 0x9394
+#define ZYD_CR230 0x9398
+#define ZYD_CR231 0x939c
+#define ZYD_CR232 0x93a0
+#define ZYD_CR233 0x93a4
+#define ZYD_CR234 0x93a8
+#define ZYD_CR235 0x93ac
+#define ZYD_CR236 0x93b0
+#define ZYD_CR240 0x93c0
+#define ZYD_CR241 0x93c4
+#define ZYD_CR242 0x93c8
+#define ZYD_CR243 0x93cc
+#define ZYD_CR244 0x93d0
+#define ZYD_CR245 0x93d4
+#define ZYD_CR251 0x93ec
+#define ZYD_CR252 0x93f0
+#define ZYD_CR253 0x93f4
+#define ZYD_CR254 0x93f8
+#define ZYD_CR255 0x93fc
+
+/* nitems(ZYD_*_CHANTABLE) */
+static const uint8_t zyd_chan_2ghz[] =
+ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+
+/* copied nearly verbatim from the Linux driver rewrite */
+#define ZYD_DEF_PHY \
+{ \
+ { ZYD_CR0, 0x0a }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \
+ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xa0 }, \
+ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0x7f }, \
+ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \
+ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \
+ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0c }, \
+ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x19 }, \
+ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \
+ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \
+ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \
+ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \
+ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \
+ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \
+ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \
+ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \
+ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \
+ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \
+ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \
+ { ZYD_CR79, 0x68 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \
+ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x00 }, { ZYD_CR84, 0x00 }, \
+ { ZYD_CR85, 0x02 }, { ZYD_CR86, 0x00 }, { ZYD_CR87, 0x00 }, \
+ { ZYD_CR88, 0xff }, { ZYD_CR89, 0xfc }, { ZYD_CR90, 0x00 }, \
+ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x08 }, \
+ { ZYD_CR94, 0x00 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0xff }, \
+ { ZYD_CR97, 0xe7 }, { ZYD_CR98, 0x00 }, { ZYD_CR99, 0x00 }, \
+ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0xae }, { ZYD_CR102, 0x02 }, \
+ { ZYD_CR103, 0x00 }, { ZYD_CR104, 0x03 }, { ZYD_CR105, 0x65 }, \
+ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \
+ { ZYD_CR109, 0xaa }, { ZYD_CR110, 0xaa }, { ZYD_CR111, 0x25 }, \
+ { ZYD_CR112, 0x25 }, { ZYD_CR113, 0x00 }, { ZYD_CR119, 0x1e }, \
+ { ZYD_CR125, 0x90 }, { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, \
+ { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \
+ { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \
+ { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \
+ { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x1E }, { ZYD_CR83, 0x24 }, \
+ { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \
+ { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \
+ { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \
+ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x50 }, \
+ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR101, 0x13 }, \
+ { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, \
+ { ZYD_CR105, 0x12 }, { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, \
+ { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \
+ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \
+ { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \
+ { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \
+ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0C }, \
+ { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, { ZYD_CR138, 0xa0 }, \
+ { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, { ZYD_CR141, 0x82 }, \
+ { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \
+ { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, { ZYD_CR149, 0x50 }, \
+ { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, { ZYD_CR160, 0xfe }, \
+ { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, { ZYD_CR163, 0xfa }, \
+ { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, { ZYD_CR166, 0xbe }, \
+ { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, { ZYD_CR169, 0xba }, \
+ { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, { ZYD_CR204, 0x7d }, \
+ { ZYD_CR203, 0x30 }, { 0, 0} \
+}
+
+#define ZYD_DEF_PHYB \
+{ \
+ { ZYD_CR0, 0x14 }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \
+ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xe0 }, \
+ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0xf0 }, \
+ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \
+ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \
+ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x10 }, { ZYD_CR21, 0x0e }, \
+ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x10 }, \
+ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \
+ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \
+ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \
+ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \
+ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \
+ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x33 }, { ZYD_CR46, 0xff }, \
+ { ZYD_CR47, 0x1E }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \
+ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \
+ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \
+ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \
+ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \
+ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \
+ { ZYD_CR79, 0xf0 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \
+ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, \
+ { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0c }, { ZYD_CR87, 0x12 }, \
+ { ZYD_CR88, 0x0c }, { ZYD_CR89, 0x00 }, { ZYD_CR90, 0x58 }, \
+ { ZYD_CR91, 0x04 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x00 }, \
+ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x20 }, { ZYD_CR96, 0x50 }, \
+ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR99, 0x00 }, \
+ { ZYD_CR100, 0x01 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, \
+ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \
+ { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, \
+ { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, \
+ { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfc }, \
+ { ZYD_CR118, 0xfa }, { ZYD_CR119, 0x1e }, { ZYD_CR125, 0x90 }, \
+ { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, { ZYD_CR128, 0x14 }, \
+ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0c }, \
+ { ZYD_CR136, 0xdf }, { ZYD_CR137, 0xa0 }, { ZYD_CR138, 0xa8 }, \
+ { ZYD_CR139, 0xb4 }, { ZYD_CR140, 0x98 }, { ZYD_CR141, 0x82 }, \
+ { ZYD_CR142, 0x53 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \
+ { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x40 }, { ZYD_CR149, 0x40 }, \
+ { ZYD_CR150, 0x14 }, { ZYD_CR151, 0x18 }, { ZYD_CR159, 0x70 }, \
+ { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \
+ { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \
+ { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \
+ { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \
+ { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }, \
+ { 0, 0 } \
+}
+
+#define ZYD_RFMD_PHY \
+{ \
+ { ZYD_CR2, 0x1e }, { ZYD_CR9, 0x20 }, { ZYD_CR10, 0x89 }, \
+ { ZYD_CR11, 0x00 }, { ZYD_CR15, 0xd0 }, { ZYD_CR17, 0x68 }, \
+ { ZYD_CR19, 0x4a }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0e }, \
+ { ZYD_CR23, 0x48 }, { ZYD_CR24, 0x14 }, { ZYD_CR26, 0x90 }, \
+ { ZYD_CR27, 0x30 }, { ZYD_CR29, 0x20 }, { ZYD_CR31, 0xb2 }, \
+ { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 }, \
+ { ZYD_CR34, 0x0f }, { ZYD_CR35, 0xf0 }, { ZYD_CR41, 0x2a }, \
+ { ZYD_CR46, 0x7f }, { ZYD_CR47, 0x1e }, { ZYD_CR51, 0xc5 }, \
+ { ZYD_CR52, 0xc5 }, { ZYD_CR53, 0xc5 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR82, 0x00 }, \
+ { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, \
+ { ZYD_CR86, 0x10 }, { ZYD_CR87, 0x2a }, { ZYD_CR88, 0x10 }, \
+ { ZYD_CR89, 0x24 }, { ZYD_CR90, 0x18 }, { ZYD_CR91, 0x00 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR93, 0x00 }, { ZYD_CR94, 0x01 }, \
+ { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x40 }, { ZYD_CR97, 0x37 }, \
+ { ZYD_CR98, 0x05 }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \
+ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, \
+ { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, { ZYD_CR106, 0x1a }, \
+ { ZYD_CR107, 0x24 }, { ZYD_CR108, 0x0a }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x2f }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x40 }, \
+ { ZYD_CR116, 0x40 }, { ZYD_CR117, 0xf0 }, { ZYD_CR118, 0xf0 }, \
+ { ZYD_CR119, 0x16 }, { ZYD_CR122, 0x00 }, { ZYD_CR127, 0x03 }, \
+ { ZYD_CR131, 0x08 }, { ZYD_CR138, 0x28 }, { ZYD_CR148, 0x44 }, \
+ { ZYD_CR150, 0x10 }, { ZYD_CR169, 0xbb }, { ZYD_CR170, 0xbb } \
+}
+
+#define ZYD_RFMD_RF \
+{ \
+ 0x000007, 0x07dd43, 0x080959, 0x0e6666, 0x116a57, 0x17dd43, \
+ 0x1819f9, 0x1e6666, 0x214554, 0x25e7fa, 0x27fffa, 0x294128, \
+ 0x2c0000, 0x300000, 0x340000, 0x381e0f, 0x6c180f \
+}
+
+#define ZYD_RFMD_CHANTABLE \
+{ \
+ { 0x181979, 0x1e6666 }, \
+ { 0x181989, 0x1e6666 }, \
+ { 0x181999, 0x1e6666 }, \
+ { 0x1819a9, 0x1e6666 }, \
+ { 0x1819b9, 0x1e6666 }, \
+ { 0x1819c9, 0x1e6666 }, \
+ { 0x1819d9, 0x1e6666 }, \
+ { 0x1819e9, 0x1e6666 }, \
+ { 0x1819f9, 0x1e6666 }, \
+ { 0x181a09, 0x1e6666 }, \
+ { 0x181a19, 0x1e6666 }, \
+ { 0x181a29, 0x1e6666 }, \
+ { 0x181a39, 0x1e6666 }, \
+ { 0x181a60, 0x1c0000 } \
+}
+
+#define ZYD_AL2230_PHY \
+{ \
+ { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \
+ { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \
+ { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, \
+ { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x2b }, \
+ { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, { ZYD_CR10, 0x89 }, \
+ { ZYD_CR17, 0x28 }, { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, \
+ { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, \
+ { ZYD_CR46, 0x96 }, { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, \
+ { ZYD_CR89, 0x04 }, { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, \
+ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfc }, \
+ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, \
+ { ZYD_CR122, 0xe0 }, { ZYD_CR137, 0x88 }, { ZYD_CR252, 0xff }, \
+ { ZYD_CR253, 0xff }, { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, \
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } \
+}
+
+#define ZYD_AL2230_PHY_B \
+{ \
+ { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2B }, \
+ { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, { ZYD_CR33, 0x28 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x99 }, { ZYD_CR47, 0x1e }, \
+ { ZYD_CR48, 0x06 }, { ZYD_CR49, 0xf9 }, { ZYD_CR51, 0x01 }, \
+ { ZYD_CR52, 0x80 }, { ZYD_CR53, 0x7e }, { ZYD_CR65, 0x00 }, \
+ { ZYD_CR66, 0x00 }, { ZYD_CR67, 0x00 }, { ZYD_CR68, 0x00 }, \
+ { ZYD_CR69, 0x28 }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \
+ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \
+ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, \
+ { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfa }, \
+ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x6c }, \
+ { ZYD_CR122, 0xfc }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \
+ { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR137, 0x50 }, \
+ { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR150, 0x0d }, \
+ { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 } \
+}
+
+#define ZYD_AL2230_PHY_PART1 \
+{ \
+ { ZYD_CR240, 0x57 }, { ZYD_CR9, 0xe0 } \
+}
+
+#define ZYD_AL2230_PHY_PART2 \
+{ \
+ { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x7f }, \
+}
+
+#define ZYD_AL2230_PHY_PART3 \
+{ \
+ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \
+}
+
+#define ZYD_AL2230S_PHY_INIT \
+{ \
+ { ZYD_CR47, 0x1e }, { ZYD_CR106, 0x22 }, { ZYD_CR107, 0x2a }, \
+ { ZYD_CR109, 0x13 }, { ZYD_CR118, 0xf8 }, { ZYD_CR119, 0x12 }, \
+ { ZYD_CR122, 0xe0 }, { ZYD_CR128, 0x10 }, { ZYD_CR129, 0x0e }, \
+ { ZYD_CR130, 0x10 } \
+}
+
+#define ZYD_AL2230_PHY_FINI_PART1 \
+{ \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, \
+ { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 }, \
+}
+
+#define ZYD_AL2230_RF_PART1 \
+{ \
+ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3 \
+}
+
+#define ZYD_AL2230_RF_PART2 \
+{ \
+ 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \
+ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f \
+}
+
+#define ZYD_AL2230_RF_PART3 \
+{ \
+ 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f \
+}
+
+#define ZYD_AL2230_RF_B \
+{ \
+ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \
+ 0x0005a4, 0x0f4dc5, 0x0805b6, 0x0146c7, 0x000688, 0x0403b9, \
+ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00580f \
+}
+
+#define ZYD_AL2230_RF_B_PART1 \
+{ \
+ 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000 \
+}
+
+#define ZYD_AL2230_RF_B_PART2 \
+{ \
+ 0x25a000, 0xa3b2f0, 0x6da010, 0xe36280, 0x116000, 0x9dc020, \
+ 0x5ddb00, 0xd99000, 0x3ffbd0, 0xb00000, 0xf01a00 \
+}
+
+#define ZYD_AL2230_RF_B_PART3 \
+{ \
+ 0xf01b00, 0xf01e00, 0xf01a00 \
+}
+
+#define ZYD_AL2230_CHANTABLE \
+{ \
+ { 0x03f790, 0x033331, 0x00000d }, \
+ { 0x03f790, 0x0b3331, 0x00000d }, \
+ { 0x03e790, 0x033331, 0x00000d }, \
+ { 0x03e790, 0x0b3331, 0x00000d }, \
+ { 0x03f7a0, 0x033331, 0x00000d }, \
+ { 0x03f7a0, 0x0b3331, 0x00000d }, \
+ { 0x03e7a0, 0x033331, 0x00000d }, \
+ { 0x03e7a0, 0x0b3331, 0x00000d }, \
+ { 0x03f7b0, 0x033331, 0x00000d }, \
+ { 0x03f7b0, 0x0b3331, 0x00000d }, \
+ { 0x03e7b0, 0x033331, 0x00000d }, \
+ { 0x03e7b0, 0x0b3331, 0x00000d }, \
+ { 0x03f7c0, 0x033331, 0x00000d }, \
+ { 0x03e7c0, 0x066661, 0x00000d } \
+}
+
+#define ZYD_AL2230_CHANTABLE_B \
+{ \
+ { 0x09efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x09efc0, 0x8cccd0, 0xb00000 }, \
+ { 0x09e7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x09e7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x05efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x05efc0, 0x8cccd0, 0xb00000 }, \
+ { 0x05e7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x05e7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x0defc0, 0x8cccc0, 0xb00000 }, \
+ { 0x0defc0, 0x8cccd0, 0xb00000 }, \
+ { 0x0de7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x0de7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x03efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x03e7c0, 0x866660, 0xb00000 } \
+}
+
+#define ZYD_AL7230B_PHY_1 \
+{ \
+ { ZYD_CR240, 0x57 }, { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, \
+ { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, \
+ { ZYD_CR29, 0x00 }, { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x22 }, \
+ { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, \
+ { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, \
+ { ZYD_CR122, 0xfc }, { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x28 }, \
+ { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, \
+ { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x96 }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \
+ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x02 }, \
+ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR106, 0x22 }, \
+ { ZYD_CR107, 0x3f }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x1f }, \
+ { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, { ZYD_CR113, 0x27 }, \
+ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, { ZYD_CR116, 0x3f }, \
+ { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfc }, { ZYD_CR119, 0x10 }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR137, 0x88 }, \
+ { ZYD_CR138, 0xa8 }, { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 }, \
+ { ZYD_CR251, 0x2f } \
+}
+
+#define ZYD_AL7230B_PHY_2 \
+{ \
+ { ZYD_CR251, 0x3f }, { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, \
+ { ZYD_CR130, 0x10 }, { ZYD_CR38, 0x38 }, { ZYD_CR136, 0xdf } \
+}
+
+#define ZYD_AL7230B_PHY_3 \
+{ \
+ { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 } \
+}
+
+#define ZYD_AL7230B_RF_1 \
+{ \
+ 0x09ec04, 0x8cccc8, 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, \
+ 0x6cf56a, 0xe04073, 0x193d76, 0x9dd844, 0x500007, 0xd8c010, \
+ 0x3c9000, 0xbfffff, 0x700000, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_RF_2 \
+{ \
+ 0xf15d59, 0xf15d5c, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_RF_SETCHANNEL \
+{ \
+ 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, 0x6cf56a, 0xe04073, \
+ 0x193d76, 0x9dd844, 0x500007, 0xd8c010, 0x3c9000, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_CHANTABLE \
+{ \
+ { 0x09ec00, 0x8cccc8 }, \
+ { 0x09ec00, 0x8cccd8 }, \
+ { 0x09ec00, 0x8cccc0 }, \
+ { 0x09ec00, 0x8cccd0 }, \
+ { 0x05ec00, 0x8cccc8 }, \
+ { 0x05ec00, 0x8cccd8 }, \
+ { 0x05ec00, 0x8cccc0 }, \
+ { 0x05ec00, 0x8cccd0 }, \
+ { 0x0dec00, 0x8cccc8 }, \
+ { 0x0dec00, 0x8cccd8 }, \
+ { 0x0dec00, 0x8cccc0 }, \
+ { 0x0dec00, 0x8cccd0 }, \
+ { 0x03ec00, 0x8cccc8 }, \
+ { 0x03ec00, 0x866660 } \
+}
+
+#define ZYD_AL2210_PHY \
+{ \
+ { ZYD_CR9, 0xe0 }, { ZYD_CR10, 0x91 }, { ZYD_CR12, 0x90 }, \
+ { ZYD_CR15, 0xd0 }, { ZYD_CR16, 0x40 }, { ZYD_CR17, 0x58 }, \
+ { ZYD_CR18, 0x04 }, { ZYD_CR23, 0x66 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR26, 0x90 }, { ZYD_CR31, 0x80 }, { ZYD_CR34, 0x06 }, \
+ { ZYD_CR35, 0x3e }, { ZYD_CR38, 0x38 }, { ZYD_CR46, 0x90 }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR64, 0x64 }, { ZYD_CR79, 0xb5 }, \
+ { ZYD_CR80, 0x38 }, { ZYD_CR81, 0x30 }, { ZYD_CR113, 0xc0 }, \
+ { ZYD_CR127, 0x03 } \
+}
+
+#define ZYD_AL2210_RF \
+{ \
+ 0x2396c0, 0x00fcb1, 0x358132, 0x0108b3, 0xc77804, 0x456415, \
+ 0xff2226, 0x806667, 0x7860f8, 0xbb01c9, 0x00000a, 0x00000b \
+}
+
+#define ZYD_AL2210_CHANTABLE \
+{ \
+ 0x0196c0, 0x019710, 0x019760, 0x0197b0, 0x019800, 0x019850, \
+ 0x0198a0, 0x0198f0, 0x019940, 0x019990, 0x0199e0, 0x019a30, \
+ 0x019a80, 0x019b40 \
+}
+
+#define ZYD_GCT_PHY \
+{ \
+ { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x28 }, \
+ { ZYD_CR23, 0x38 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR27, 0x15 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \
+ { ZYD_CR33, 0x28 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x43 }, \
+ { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x92 }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x04 }, { ZYD_CR49, 0xfa }, \
+ { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, \
+ { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, { ZYD_CR91, 0x00 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, { ZYD_CR99, 0x28 }, \
+ { ZYD_CR100, 0x02 }, { ZYD_CR101, 0x09 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x1c }, { ZYD_CR107, 0x1c }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x1f }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x23 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xf0 }, \
+ { ZYD_CR119, 0x1a }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x1f }, \
+ { ZYD_CR122, 0xf0 }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \
+ { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \
+ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR137, 0x50 }, \
+ { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR146, 0x20 }, \
+ { ZYD_CR252, 0xff }, { ZYD_CR253, 0xff } \
+}
+
+#define ZYD_GCT_RF \
+{ \
+ 0x40002b, 0x519e4f, 0x6f81ad, 0x73fffe, 0x25f9c, 0x100047, \
+ 0x200999, 0x307602, 0x346063, \
+}
+
+#define ZYD_GCT_VCO \
+{ \
+ { 0x664d, 0x604d, 0x6675, 0x6475, 0x6655, 0x6455, 0x6665 }, \
+ { 0x666d, 0x606d, 0x664d, 0x644d, 0x6675, 0x6475, 0x6655 }, \
+ { 0x665d, 0x605d, 0x666d, 0x646d, 0x664d, 0x644d, 0x6675 }, \
+ { 0x667d, 0x607d, 0x665d, 0x645d, 0x666d, 0x646d, 0x664d }, \
+ { 0x6643, 0x6043, 0x667d, 0x647d, 0x665d, 0x645d, 0x666d }, \
+ { 0x6663, 0x6063, 0x6643, 0x6443, 0x667d, 0x647d, 0x665d }, \
+ { 0x6653, 0x6053, 0x6663, 0x6463, 0x6643, 0x6443, 0x667d }, \
+ { 0x6673, 0x6073, 0x6653, 0x6453, 0x6663, 0x6463, 0x6643 }, \
+ { 0x664b, 0x604b, 0x6673, 0x6473, 0x6653, 0x6453, 0x6663 }, \
+ { 0x666b, 0x606b, 0x664b, 0x644b, 0x6673, 0x6473, 0x6653 }, \
+ { 0x665b, 0x605b, 0x666b, 0x646b, 0x664b, 0x644b, 0x6673 } \
+}
+
+#define ZYD_GCT_TXGAIN \
+{ \
+ 0x0e313, 0x0fb13, 0x0e093, 0x0f893, 0x0ea93, 0x1f093, 0x1f493, \
+ 0x1f693, 0x1f393, 0x1f35b, 0x1e6db, 0x1ff3f, 0x1ffff, 0x361d7, \
+ 0x37fbf, 0x3ff8b, 0x3ff33, 0x3fb3f, 0x3ffff \
+}
+
+#define ZYD_GCT_CHANNEL_ACAL \
+{ \
+ 0x106847, 0x106847, 0x106867, 0x106867, 0x106867, 0x106867, \
+ 0x106857, 0x106857, 0x106857, 0x106857, 0x106877, 0x106877, \
+ 0x106877, 0x10684f \
+}
+
+#define ZYD_GCT_CHANNEL_STD \
+{ \
+ 0x100047, 0x100047, 0x100067, 0x100067, 0x100067, 0x100067, \
+ 0x100057, 0x100057, 0x100057, 0x100057, 0x100077, 0x100077, \
+ 0x100077, 0x10004f \
+}
+
+#define ZYD_GCT_CHANNEL_DIV \
+{ \
+ 0x200999, 0x20099b, 0x200998, 0x20099a, 0x200999, 0x20099b, \
+ 0x200998, 0x20099a, 0x200999, 0x20099b, 0x200998, 0x20099a, \
+ 0x200999, 0x200ccc \
+}
+
+#define ZYD_MAXIM2_PHY \
+{ \
+ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \
+ { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \
+ { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \
+ { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe } \
+}
+
+#define ZYD_MAXIM2_RF \
+{ \
+ 0x33334, 0x10a03, 0x00400, 0x00ca1, 0x10072, 0x18645, 0x04006, \
+ 0x000a7, 0x08258, 0x03fc9, 0x0040a, 0x0000b, 0x0026c \
+}
+
+#define ZYD_MAXIM2_CHANTABLE_F \
+{ \
+ 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, \
+ 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x26664 \
+}
+
+#define ZYD_MAXIM2_CHANTABLE \
+{ \
+ { 0x33334, 0x10a03 }, \
+ { 0x08884, 0x20a13 }, \
+ { 0x1ddd4, 0x30a13 }, \
+ { 0x33334, 0x10a13 }, \
+ { 0x08884, 0x20a23 }, \
+ { 0x1ddd4, 0x30a23 }, \
+ { 0x33334, 0x10a23 }, \
+ { 0x08884, 0x20a33 }, \
+ { 0x1ddd4, 0x30a33 }, \
+ { 0x33334, 0x10a33 }, \
+ { 0x08884, 0x20a43 }, \
+ { 0x1ddd4, 0x30a43 }, \
+ { 0x33334, 0x10a43 }, \
+ { 0x26664, 0x20a53 } \
+}
+
+#define ZYD_TX_RATEDIV \
+{ \
+ 0x1, 0x2, 0xb, 0xb, 0x1, 0x1, 0x1, 0x1, 0x30, 0x18, 0xc, 0x6, \
+ 0x36, 0x24, 0x12, 0x9 \
+}
+
+/*
+ * Control pipe requests.
+ */
+#define ZYD_DOWNLOADREQ 0x30
+#define ZYD_DOWNLOADSTS 0x31
+#define ZYD_READFWDATAREQ 0x32
+
+/* possible values for register ZYD_CR_INTERRUPT */
+#define ZYD_HWINT_MASK 0x004f0000
+
+/* possible values for register ZYD_MAC_MISC */
+#define ZYD_UNLOCK_PHY_REGS 0x80
+
+/* possible values for register ZYD_MAC_ENCRYPTION_TYPE */
+#define ZYD_ENC_SNIFFER 8
+
+/* flags for register ZYD_MAC_RXFILTER */
+#define ZYD_FILTER_ASS_REQ (1 << 0)
+#define ZYD_FILTER_ASS_RSP (1 << 1)
+#define ZYD_FILTER_REASS_REQ (1 << 2)
+#define ZYD_FILTER_REASS_RSP (1 << 3)
+#define ZYD_FILTER_PRB_REQ (1 << 4)
+#define ZYD_FILTER_PRB_RSP (1 << 5)
+#define ZYD_FILTER_BCN (1 << 8)
+#define ZYD_FILTER_ATIM (1 << 9)
+#define ZYD_FILTER_DEASS (1 << 10)
+#define ZYD_FILTER_AUTH (1 << 11)
+#define ZYD_FILTER_DEAUTH (1 << 12)
+#define ZYD_FILTER_PS_POLL (1 << 26)
+#define ZYD_FILTER_RTS (1 << 27)
+#define ZYD_FILTER_CTS (1 << 28)
+#define ZYD_FILTER_ACK (1 << 29)
+#define ZYD_FILTER_CFE (1 << 30)
+#define ZYD_FILTER_CFE_A (1U << 31)
+
+/* helpers for register ZYD_MAC_RXFILTER */
+#define ZYD_FILTER_MONITOR 0xffffffff
+#define ZYD_FILTER_BSS \
+ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_ASS_RSP | \
+ ZYD_FILTER_REASS_REQ | ZYD_FILTER_REASS_RSP | \
+ ZYD_FILTER_PRB_REQ | ZYD_FILTER_PRB_RSP | \
+ (0x3 << 6) | \
+ ZYD_FILTER_BCN | ZYD_FILTER_ATIM | ZYD_FILTER_DEASS | \
+ ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH | \
+ (0x7 << 13) | \
+ ZYD_FILTER_PS_POLL | ZYD_FILTER_ACK)
+#define ZYD_FILTER_HOSTAP \
+ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_REASS_REQ | \
+ ZYD_FILTER_PRB_REQ | ZYD_FILTER_DEASS | ZYD_FILTER_AUTH | \
+ ZYD_FILTER_DEAUTH | ZYD_FILTER_PS_POLL)
+
+struct zyd_tx_desc {
+ uint8_t phy;
+#define ZYD_TX_PHY_SIGNAL(x) ((x) & 0xf)
+#define ZYD_TX_PHY_OFDM (1 << 4)
+#define ZYD_TX_PHY_SHPREAMBLE (1 << 5) /* CCK */
+#define ZYD_TX_PHY_5GHZ (1 << 5) /* OFDM */
+ uint16_t len;
+ uint8_t flags;
+#define ZYD_TX_FLAG_BACKOFF (1 << 0)
+#define ZYD_TX_FLAG_MULTICAST (1 << 1)
+#define ZYD_TX_FLAG_TYPE(x) (((x) & 0x3) << 2)
+#define ZYD_TX_TYPE_DATA 0
+#define ZYD_TX_TYPE_PS_POLL 1
+#define ZYD_TX_TYPE_MGMT 2
+#define ZYD_TX_TYPE_CTL 3
+#define ZYD_TX_FLAG_WAKEUP (1 << 4)
+#define ZYD_TX_FLAG_RTS (1 << 5)
+#define ZYD_TX_FLAG_ENCRYPT (1 << 6)
+#define ZYD_TX_FLAG_CTS_TO_SELF (1 << 7)
+ uint16_t pktlen;
+ uint16_t plcp_length;
+ uint8_t plcp_service;
+#define ZYD_PLCP_LENGEXT 0x80
+ uint16_t nextlen;
+} __packed;
+
+struct zyd_plcphdr {
+ uint8_t signal;
+ uint8_t reserved[2];
+ uint16_t service; /* unaligned! */
+} __packed;
+
+struct zyd_rx_stat {
+ uint8_t signal_cck;
+ uint8_t rssi;
+ uint8_t signal_ofdm;
+ uint8_t cipher;
+#define ZYD_RX_CIPHER_WEP64 1
+#define ZYD_RX_CIPHER_TKIP 2
+#define ZYD_RX_CIPHER_AES 4
+#define ZYD_RX_CIPHER_WEP128 5
+#define ZYD_RX_CIPHER_WEP256 6
+#define ZYD_RX_CIPHER_WEP \
+ (ZYD_RX_CIPHER_WEP64 | ZYD_RX_CIPHER_WEP128 | ZYD_RX_CIPHER_WEP256)
+ uint8_t flags;
+#define ZYD_RX_OFDM (1 << 0)
+#define ZYD_RX_TIMEOUT (1 << 1)
+#define ZYD_RX_OVERRUN (1 << 2)
+#define ZYD_RX_DECRYPTERR (1 << 3)
+#define ZYD_RX_BADCRC32 (1 << 4)
+#define ZYD_RX_NOT2ME (1 << 5)
+#define ZYD_RX_BADCRC16 (1 << 6)
+#define ZYD_RX_ERROR (1 << 7)
+} __packed;
+
+/* this structure may be unaligned */
+struct zyd_rx_desc {
+#define ZYD_MAX_RXFRAMECNT 3
+ uWord len[ZYD_MAX_RXFRAMECNT];
+ uWord tag;
+#define ZYD_TAG_MULTIFRAME 0x697e
+} __packed;
+
+/* I2C bus alike */
+struct zyd_rfwrite_cmd {
+ uint16_t code;
+ uint16_t width;
+ uint16_t bit[32];
+#define ZYD_RF_IF_LE (1 << 1)
+#define ZYD_RF_CLK (1 << 2)
+#define ZYD_RF_DATA (1 << 3)
+} __packed;
+
+struct zyd_cmd {
+ uint16_t code;
+#define ZYD_CMD_IOWR 0x0021 /* write HMAC or PHY register */
+#define ZYD_CMD_IORD 0x0022 /* read HMAC or PHY register */
+#define ZYD_CMD_RFCFG 0x0023 /* write RF register */
+#define ZYD_NOTIF_IORD 0x9001 /* response for ZYD_CMD_IORD */
+#define ZYD_NOTIF_MACINTR 0x9001 /* interrupt notification */
+#define ZYD_NOTIF_RETRYSTATUS 0xa001 /* Tx retry notification */
+ uint8_t data[64];
+} __packed;
+
+/* structure for command ZYD_CMD_IOWR */
+struct zyd_pair {
+ uint16_t reg;
+/* helpers macros to read/write 32-bit registers */
+#define ZYD_REG32_LO(reg) (reg)
+#define ZYD_REG32_HI(reg) \
+ ((reg) + ((((reg) & 0xf000) == 0x9000) ? 2 : 1))
+ uint16_t val;
+} __packed;
+
+/* structure for notification ZYD_NOTIF_RETRYSTATUS */
+struct zyd_notif_retry {
+ uint16_t rate;
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ uint16_t count;
+} __packed;
+
+#define ZYD_CONFIG_INDEX 0
+#define ZYD_IFACE_INDEX 0
+
+#define ZYD_INTR_TIMEOUT 1000
+#define ZYD_TX_TIMEOUT 10000
+
+#define ZYD_MAX_TXBUFSZ \
+ (sizeof(struct zyd_tx_desc) + MCLBYTES)
+#define ZYD_MIN_FRAGSZ \
+ (sizeof(struct zyd_plcphdr) + IEEE80211_MIN_LEN + \
+ sizeof(struct zyd_rx_stat))
+#define ZYD_MIN_RXBUFSZ ZYD_MIN_FRAGSZ
+#define ZYX_MAX_RXBUFSZ \
+ ((sizeof (struct zyd_plcphdr) + IEEE80211_MAX_LEN + \
+ sizeof (struct zyd_rx_stat)) * ZYD_MAX_RXFRAMECNT + \
+ sizeof (struct zyd_rx_desc))
+#define ZYD_TX_DESC_SIZE (sizeof (struct zyd_tx_desc))
+
+#define ZYD_RX_LIST_CNT 1
+#define ZYD_TX_LIST_CNT 5
+#define ZYD_CMD_FLAG_READ (1 << 0)
+#define ZYD_CMD_FLAG_SENT (1 << 1)
+
+/* quickly determine if a given rate is CCK or OFDM */
+#define ZYD_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
+
+struct zyd_phy_pair {
+ uint16_t reg;
+ uint8_t val;
+};
+
+struct zyd_mac_pair {
+ uint16_t reg;
+ uint32_t val;
+};
+
+struct zyd_tx_data {
+ STAILQ_ENTRY(zyd_tx_data) next;
+ struct zyd_softc *sc;
+ struct zyd_tx_desc desc;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ int rate;
+};
+typedef STAILQ_HEAD(, zyd_tx_data) zyd_txdhead;
+
+struct zyd_rx_data {
+ struct mbuf *m;
+ int rssi;
+};
+
+struct zyd_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_antsignal;
+ int8_t wr_antnoise;
+} __packed __aligned(8);
+
+#define ZYD_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct zyd_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed __aligned(8);
+
+#define ZYD_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct zyd_softc; /* forward declaration */
+
+struct zyd_rf {
+ /* RF methods */
+ int (*init)(struct zyd_rf *);
+ int (*switch_radio)(struct zyd_rf *, int);
+ int (*set_channel)(struct zyd_rf *, uint8_t);
+ int (*bandedge6)(struct zyd_rf *,
+ struct ieee80211_channel *);
+ /* RF attributes */
+ struct zyd_softc *rf_sc; /* back-pointer */
+ int width;
+ int idx; /* for GIT RF */
+ int update_pwr;
+};
+
+struct zyd_rq {
+ struct zyd_cmd *cmd;
+ const uint16_t *idata;
+ struct zyd_pair *odata;
+ int ilen;
+ int olen;
+ int flags;
+ STAILQ_ENTRY(zyd_rq) rq;
+};
+
+struct zyd_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define ZYD_VAP(vap) ((struct zyd_vap *)(vap))
+
+enum {
+ ZYD_BULK_WR,
+ ZYD_BULK_RD,
+ ZYD_INTR_WR,
+ ZYD_INTR_RD,
+ ZYD_N_TRANSFER = 4,
+};
+
+struct zyd_softc {
+ struct ieee80211com sc_ic;
+ struct ieee80211_ratectl_tx_status sc_txs;
+ struct mbufq sc_snd;
+ device_t sc_dev;
+ struct usb_device *sc_udev;
+
+ struct usb_xfer *sc_xfer[ZYD_N_TRANSFER];
+
+ int sc_flags;
+#define ZYD_FLAG_FWLOADED (1 << 0)
+#define ZYD_FLAG_INITONCE (1 << 1)
+#define ZYD_FLAG_INITDONE (1 << 2)
+#define ZYD_FLAG_DETACHED (1 << 3)
+#define ZYD_FLAG_RUNNING (1 << 4)
+
+ struct zyd_rf sc_rf;
+
+ STAILQ_HEAD(, zyd_rq) sc_rtx;
+ STAILQ_HEAD(, zyd_rq) sc_rqh;
+
+ uint16_t sc_fwbase;
+ uint8_t sc_regdomain;
+ uint8_t sc_macrev;
+ uint16_t sc_fwrev;
+ uint8_t sc_rfrev;
+ uint8_t sc_parev;
+ uint8_t sc_al2230s;
+ uint8_t sc_bandedge6;
+ uint8_t sc_newphy;
+ uint8_t sc_cckgain;
+ uint8_t sc_fix_cr157;
+ uint8_t sc_ledtype;
+ uint8_t sc_txled;
+
+ uint32_t sc_atim_wnd;
+ uint32_t sc_pre_tbtt;
+ uint32_t sc_bcn_int;
+
+ uint8_t sc_pwrcal[14];
+ uint8_t sc_pwrint[14];
+ uint8_t sc_ofdm36_cal[14];
+ uint8_t sc_ofdm48_cal[14];
+ uint8_t sc_ofdm54_cal[14];
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
+
+ struct mtx sc_mtx;
+ struct zyd_tx_data tx_data[ZYD_TX_LIST_CNT];
+ zyd_txdhead tx_q;
+ zyd_txdhead tx_free;
+ int tx_nfree;
+ struct zyd_rx_desc sc_rx_desc;
+ struct zyd_rx_data sc_rx_data[ZYD_MAX_RXFRAMECNT];
+ int sc_rx_count;
+
+ struct zyd_cmd sc_ibuf;
+
+ struct zyd_rx_radiotap_header sc_rxtap;
+ struct zyd_tx_radiotap_header sc_txtap;
+};
+
+#define ZYD_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define ZYD_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define ZYD_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t)
+