Index: freebsd/sys/dev/usb/net/if_aue.c
===================================================================
--- freebsd/sys/dev/usb/net/if_aue.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_aue.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
+}
Index: freebsd/sys/dev/usb/net/if_auereg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_auereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_auereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/if_axe.c
===================================================================
--- freebsd/sys/dev/usb/net/if_axe.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_axe.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
+}
Index: freebsd/sys/dev/usb/net/if_axereg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_axereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_axereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/if_axge.c
===================================================================
--- freebsd/sys/dev/usb/net/if_axge.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_axge.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
+}
Index: freebsd/sys/dev/usb/net/if_axgereg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_axgereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_axgereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/if_cdce.c
===================================================================
--- freebsd/sys/dev/usb/net/if_cdce.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_cdce.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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
Index: freebsd/sys/dev/usb/net/if_cdcereg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_cdcereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_cdcereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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_ */
Index: freebsd/sys/dev/usb/net/if_cue.c
===================================================================
--- freebsd/sys/dev/usb/net/if_cue.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_cue.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
+}
Index: freebsd/sys/dev/usb/net/if_cuereg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_cuereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_cuereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/if_ipheth.c
===================================================================
--- freebsd/sys/dev/usb/net/if_ipheth.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_ipheth.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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;
+	}
+}
Index: freebsd/sys/dev/usb/net/if_iphethvar.h
===================================================================
--- freebsd/sys/dev/usb/net/if_iphethvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_iphethvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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_ */
Index: freebsd/sys/dev/usb/net/if_kue.c
===================================================================
--- freebsd/sys/dev/usb/net/if_kue.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_kue.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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]);
+}
Index: freebsd/sys/dev/usb/net/if_kuefw.h
===================================================================
--- freebsd/sys/dev/usb/net/if_kuefw.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_kuefw.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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
+};
Index: freebsd/sys/dev/usb/net/if_kuereg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_kuereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_kuereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/if_mos.c
===================================================================
--- freebsd/sys/dev/usb/net/if_mos.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_mos.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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;
+}
Index: freebsd/sys/dev/usb/net/if_mosreg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_mosreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_mosreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/if_rue.c
===================================================================
--- freebsd/sys/dev/usb/net/if_rue.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_rue.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
+}
Index: freebsd/sys/dev/usb/net/if_ruereg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_ruereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_ruereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/if_smsc.c
===================================================================
--- freebsd/sys/dev/usb/net/if_smsc.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_smsc.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
Index: freebsd/sys/dev/usb/net/if_smscreg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_smscreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_smscreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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_ */
Index: freebsd/sys/dev/usb/net/if_udav.c
===================================================================
--- freebsd/sys/dev/usb/net/if_udav.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_udav.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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 */
+}
Index: freebsd/sys/dev/usb/net/if_udavreg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_udavreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_udavreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/if_ure.c
===================================================================
--- freebsd/sys/dev/usb/net/if_ure.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_ure.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
+}
Index: freebsd/sys/dev/usb/net/if_urereg.h
===================================================================
--- freebsd/sys/dev/usb/net/if_urereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/if_urereg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/net/ruephy.c
===================================================================
--- freebsd/sys/dev/usb/net/ruephy.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/ruephy.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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;
+}
Index: freebsd/sys/dev/usb/net/ruephyreg.h
===================================================================
--- freebsd/sys/dev/usb/net/ruephyreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/ruephyreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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_ */
Index: freebsd/sys/dev/usb/net/usb_ethernet.c
===================================================================
--- freebsd/sys/dev/usb/net/usb_ethernet.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/usb_ethernet.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
Index: freebsd/sys/dev/usb/net/usb_ethernet.h
===================================================================
--- freebsd/sys/dev/usb/net/usb_ethernet.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/net/usb_ethernet.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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_ */
Index: freebsd/sys/dev/usb/wlan/if_rsu.c
===================================================================
--- freebsd/sys/dev/usb/wlan/if_rsu.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_rsu.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
+}
Index: freebsd/sys/dev/usb/wlan/if_rsureg.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_rsureg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_rsureg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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
+};
Index: freebsd/sys/dev/usb/wlan/if_rum.c
===================================================================
--- freebsd/sys/dev/usb/wlan/if_rum.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_rum.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
Index: freebsd/sys/dev/usb/wlan/if_rumfw.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_rumfw.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_rumfw.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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
+};
Index: freebsd/sys/dev/usb/wlan/if_rumreg.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_rumreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_rumreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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
Index: freebsd/sys/dev/usb/wlan/if_rumvar.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_rumvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_rumvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/wlan/if_run.c
===================================================================
--- freebsd/sys/dev/usb/wlan/if_run.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_run.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
Index: freebsd/sys/dev/usb/wlan/if_runreg.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_runreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_runreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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_ */
Index: freebsd/sys/dev/usb/wlan/if_runvar.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_runvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_runvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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_ */
Index: freebsd/sys/dev/usb/wlan/if_uath.c
===================================================================
--- freebsd/sys/dev/usb/wlan/if_uath.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_uath.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
Index: freebsd/sys/dev/usb/wlan/if_uathreg.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_uathreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_uathreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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))
Index: freebsd/sys/dev/usb/wlan/if_uathvar.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_uathvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_uathvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/wlan/if_upgt.c
===================================================================
--- freebsd/sys/dev/usb/wlan/if_upgt.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_upgt.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
Index: freebsd/sys/dev/usb/wlan/if_upgtvar.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_upgtvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_upgtvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/wlan/if_ural.c
===================================================================
--- freebsd/sys/dev/usb/wlan/if_ural.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_ural.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
+}
Index: freebsd/sys/dev/usb/wlan/if_uralreg.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_uralreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_uralreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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
Index: freebsd/sys/dev/usb/wlan/if_uralvar.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_uralvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_uralvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/wlan/if_urtw.c
===================================================================
--- freebsd/sys/dev/usb/wlan/if_urtw.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_urtw.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
Index: freebsd/sys/dev/usb/wlan/if_urtwreg.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_urtwreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_urtwreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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;
Index: freebsd/sys/dev/usb/wlan/if_urtwvar.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_urtwvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_urtwvar.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
Index: freebsd/sys/dev/usb/wlan/if_zyd.c
===================================================================
--- freebsd/sys/dev/usb/wlan/if_zyd.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_zyd.c	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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);
Index: freebsd/sys/dev/usb/wlan/if_zydfw.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_zydfw.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_zydfw.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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
+};
Index: freebsd/sys/dev/usb/wlan/if_zydreg.h
===================================================================
--- freebsd/sys/dev/usb/wlan/if_zydreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
+++ freebsd/sys/dev/usb/wlan/if_zydreg.h	(revision d145449b7426cf070c5b8a526785cc00ea45c4c6)
@@ -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)
+
