diff options
author | Christian Mauderer <Christian.Mauderer@embedded-brains.de> | 2016-11-14 13:30:48 +0100 |
---|---|---|
committer | Christian Mauderer <Christian.Mauderer@embedded-brains.de> | 2017-01-17 12:50:57 +0100 |
commit | d145449b7426cf070c5b8a526785cc00ea45c4c6 (patch) | |
tree | fdd21cc557b90a908c89de2e1b5565486ad4439c /freebsd | |
parent | firmware: Port to RTEMS. (diff) | |
download | rtems-libbsd-d145449b7426cf070c5b8a526785cc00ea45c4c6.tar.bz2 |
Import USB and USB WLAN from FreeBSD.
Diffstat (limited to 'freebsd')
52 files changed, 53856 insertions, 0 deletions
diff --git a/freebsd/sys/dev/usb/net/if_aue.c b/freebsd/sys/dev/usb/net/if_aue.c new file mode 100644 index 00000000..b79959f9 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_aue.c @@ -0,0 +1,1068 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Copyright (c) 2006 + * Alfred Perlstein <alfred@FreeBSD.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver. + * Datasheet is available from http://www.admtek.com.tw. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + * + * SMP locking by Alfred Perlstein <alfred@FreeBSD.org>. + * RED Inc. + */ + +/* + * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet + * support: the control endpoint for reading/writing registers, burst + * read endpoint for packet reception, burst write for packet transmission + * and one for "interrupts." The chip uses the same RX filter scheme + * as the other ADMtek ethernet parts: one perfect filter entry for the + * the station address and a 64-bit multicast hash table. The chip supports + * both MII and HomePNA attachments. + * + * Since the maximum data transfer speed of USB is supposed to be 12Mbps, + * you're never really going to get 100Mbps speeds from this device. I + * think the idea is to allow the device to connect to 10 or 100Mbps + * networks, not necessarily to provide 100Mbps performance. Also, since + * the controller uses an external PHY chip, it's possible that board + * designers might simply choose a 10Mbps PHY. + * + * Registers are accessed using uether_do_request(). Packet + * transfers are done using usbd_transfer() and friends. + */ + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR aue_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_auereg.h> + +#ifdef USB_DEBUG +static int aue_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue"); +SYSCTL_INT(_hw_usb_aue, OID_AUTO, debug, CTLFLAG_RWTUN, &aue_debug, 0, + "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ +static const STRUCT_USB_HOST_ID aue_devs[] = { +#define AUE_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } + AUE_DEV(3COM, 3C460B, AUE_FLAG_PII), + AUE_DEV(ABOCOM, DSB650TX_PNA, 0), + AUE_DEV(ABOCOM, UFE1000, AUE_FLAG_LSYS), + AUE_DEV(ABOCOM, XX10, 0), + AUE_DEV(ABOCOM, XX1, AUE_FLAG_PNA | AUE_FLAG_PII), + AUE_DEV(ABOCOM, XX2, AUE_FLAG_PII), + AUE_DEV(ABOCOM, XX4, AUE_FLAG_PNA), + AUE_DEV(ABOCOM, XX5, AUE_FLAG_PNA), + AUE_DEV(ABOCOM, XX6, AUE_FLAG_PII), + AUE_DEV(ABOCOM, XX7, AUE_FLAG_PII), + AUE_DEV(ABOCOM, XX8, AUE_FLAG_PII), + AUE_DEV(ABOCOM, XX9, AUE_FLAG_PNA), + AUE_DEV(ACCTON, SS1001, AUE_FLAG_PII), + AUE_DEV(ACCTON, USB320_EC, 0), + AUE_DEV(ADMTEK, PEGASUSII_2, AUE_FLAG_PII), + AUE_DEV(ADMTEK, PEGASUSII_3, AUE_FLAG_PII), + AUE_DEV(ADMTEK, PEGASUSII_4, AUE_FLAG_PII), + AUE_DEV(ADMTEK, PEGASUSII, AUE_FLAG_PII), + AUE_DEV(ADMTEK, PEGASUS, AUE_FLAG_PNA | AUE_FLAG_DUAL_PHY), + AUE_DEV(AEI, FASTETHERNET, AUE_FLAG_PII), + AUE_DEV(ALLIEDTELESYN, ATUSB100, AUE_FLAG_PII), + AUE_DEV(ATEN, UC110T, AUE_FLAG_PII), + AUE_DEV(BELKIN, USB2LAN, AUE_FLAG_PII), + AUE_DEV(BILLIONTON, USB100, 0), + AUE_DEV(BILLIONTON, USBE100, AUE_FLAG_PII), + AUE_DEV(BILLIONTON, USBEL100, 0), + AUE_DEV(BILLIONTON, USBLP100, AUE_FLAG_PNA), + AUE_DEV(COREGA, FETHER_USB_TXS, AUE_FLAG_PII), + AUE_DEV(COREGA, FETHER_USB_TX, 0), + AUE_DEV(DLINK, DSB650TX1, AUE_FLAG_LSYS), + AUE_DEV(DLINK, DSB650TX2, AUE_FLAG_LSYS | AUE_FLAG_PII), + AUE_DEV(DLINK, DSB650TX3, AUE_FLAG_LSYS | AUE_FLAG_PII), + AUE_DEV(DLINK, DSB650TX4, AUE_FLAG_LSYS | AUE_FLAG_PII), + AUE_DEV(DLINK, DSB650TX_PNA, AUE_FLAG_PNA), + AUE_DEV(DLINK, DSB650TX, AUE_FLAG_LSYS), + AUE_DEV(DLINK, DSB650, AUE_FLAG_LSYS), + AUE_DEV(ELCON, PLAN, AUE_FLAG_PNA | AUE_FLAG_PII), + AUE_DEV(ELECOM, LDUSB20, AUE_FLAG_PII), + AUE_DEV(ELECOM, LDUSBLTX, AUE_FLAG_PII), + AUE_DEV(ELECOM, LDUSBTX0, 0), + AUE_DEV(ELECOM, LDUSBTX1, AUE_FLAG_LSYS), + AUE_DEV(ELECOM, LDUSBTX2, 0), + AUE_DEV(ELECOM, LDUSBTX3, AUE_FLAG_LSYS), + AUE_DEV(ELSA, USB2ETHERNET, 0), + AUE_DEV(GIGABYTE, GNBR402W, 0), + AUE_DEV(HAWKING, UF100, AUE_FLAG_PII), + AUE_DEV(HP, HN210E, AUE_FLAG_PII), + AUE_DEV(IODATA, USBETTXS, AUE_FLAG_PII), + AUE_DEV(IODATA, USBETTX, 0), + AUE_DEV(KINGSTON, KNU101TX, 0), + AUE_DEV(LINKSYS, USB100H1, AUE_FLAG_LSYS | AUE_FLAG_PNA), + AUE_DEV(LINKSYS, USB100TX, AUE_FLAG_LSYS), + AUE_DEV(LINKSYS, USB10TA, AUE_FLAG_LSYS), + AUE_DEV(LINKSYS, USB10TX1, AUE_FLAG_LSYS | AUE_FLAG_PII), + AUE_DEV(LINKSYS, USB10TX2, AUE_FLAG_LSYS | AUE_FLAG_PII), + AUE_DEV(LINKSYS, USB10T, AUE_FLAG_LSYS), + AUE_DEV(MELCO, LUA2TX5, AUE_FLAG_PII), + AUE_DEV(MELCO, LUATX1, 0), + AUE_DEV(MELCO, LUATX5, 0), + AUE_DEV(MICROSOFT, MN110, AUE_FLAG_PII), + AUE_DEV(NETGEAR, FA101, AUE_FLAG_PII), + AUE_DEV(SIEMENS, SPEEDSTREAM, AUE_FLAG_PII), + AUE_DEV(SIIG2, USBTOETHER, AUE_FLAG_PII), + AUE_DEV(SMARTBRIDGES, SMARTNIC, AUE_FLAG_PII), + AUE_DEV(SMC, 2202USB, 0), + AUE_DEV(SMC, 2206USB, AUE_FLAG_PII), + AUE_DEV(SOHOWARE, NUB100, 0), + AUE_DEV(SOHOWARE, NUB110, AUE_FLAG_PII), +#undef AUE_DEV +}; + +/* prototypes */ + +static device_probe_t aue_probe; +static device_attach_t aue_attach; +static device_detach_t aue_detach; +static miibus_readreg_t aue_miibus_readreg; +static miibus_writereg_t aue_miibus_writereg; +static miibus_statchg_t aue_miibus_statchg; + +static usb_callback_t aue_intr_callback; +static usb_callback_t aue_bulk_read_callback; +static usb_callback_t aue_bulk_write_callback; + +static uether_fn_t aue_attach_post; +static uether_fn_t aue_init; +static uether_fn_t aue_stop; +static uether_fn_t aue_start; +static uether_fn_t aue_tick; +static uether_fn_t aue_setmulti; +static uether_fn_t aue_setpromisc; + +static uint8_t aue_csr_read_1(struct aue_softc *, uint16_t); +static uint16_t aue_csr_read_2(struct aue_softc *, uint16_t); +static void aue_csr_write_1(struct aue_softc *, uint16_t, uint8_t); +static void aue_csr_write_2(struct aue_softc *, uint16_t, uint16_t); +static uint16_t aue_eeprom_getword(struct aue_softc *, int); +static void aue_reset(struct aue_softc *); +static void aue_reset_pegasus_II(struct aue_softc *); + +static int aue_ifmedia_upd(struct ifnet *); +static void aue_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static const struct usb_config aue_config[AUE_N_TRANSFER] = { + + [AUE_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (MCLBYTES + 2), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = aue_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [AUE_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = aue_bulk_read_callback, + }, + + [AUE_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .bufsize = 0, /* use wMaxPacketSize */ + .callback = aue_intr_callback, + }, +}; + +static device_method_t aue_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aue_probe), + DEVMETHOD(device_attach, aue_attach), + DEVMETHOD(device_detach, aue_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, aue_miibus_readreg), + DEVMETHOD(miibus_writereg, aue_miibus_writereg), + DEVMETHOD(miibus_statchg, aue_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t aue_driver = { + .name = "aue", + .methods = aue_methods, + .size = sizeof(struct aue_softc) +}; + +static devclass_t aue_devclass; + +DRIVER_MODULE(aue, uhub, aue_driver, aue_devclass, NULL, 0); +DRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(aue, uether, 1, 1, 1); +MODULE_DEPEND(aue, usb, 1, 1, 1); +MODULE_DEPEND(aue, ether, 1, 1, 1); +MODULE_DEPEND(aue, miibus, 1, 1, 1); +MODULE_VERSION(aue, 1); +USB_PNP_HOST_INFO(aue_devs); + +static const struct usb_ether_methods aue_ue_methods = { + .ue_attach_post = aue_attach_post, + .ue_start = aue_start, + .ue_init = aue_init, + .ue_stop = aue_stop, + .ue_tick = aue_tick, + .ue_setmulti = aue_setmulti, + .ue_setpromisc = aue_setpromisc, + .ue_mii_upd = aue_ifmedia_upd, + .ue_mii_sts = aue_ifmedia_sts, +}; + +#define AUE_SETBIT(sc, reg, x) \ + aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x)) + +#define AUE_CLRBIT(sc, reg, x) \ + aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x)) + +static uint8_t +aue_csr_read_1(struct aue_softc *sc, uint16_t reg) +{ + struct usb_device_request req; + usb_error_t err; + uint8_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = AUE_UR_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + err = uether_do_request(&sc->sc_ue, &req, &val, 1000); + if (err) + return (0); + return (val); +} + +static uint16_t +aue_csr_read_2(struct aue_softc *sc, uint16_t reg) +{ + struct usb_device_request req; + usb_error_t err; + uint16_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = AUE_UR_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 2); + + err = uether_do_request(&sc->sc_ue, &req, &val, 1000); + if (err) + return (0); + return (le16toh(val)); +} + +static void +aue_csr_write_1(struct aue_softc *sc, uint16_t reg, uint8_t val) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = AUE_UR_WRITEREG; + req.wValue[0] = val; + req.wValue[1] = 0; + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + if (uether_do_request(&sc->sc_ue, &req, &val, 1000)) { + /* error ignored */ + } +} + +static void +aue_csr_write_2(struct aue_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = AUE_UR_WRITEREG; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 2); + + val = htole16(val); + + if (uether_do_request(&sc->sc_ue, &req, &val, 1000)) { + /* error ignored */ + } +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static uint16_t +aue_eeprom_getword(struct aue_softc *sc, int addr) +{ + int i; + + aue_csr_write_1(sc, AUE_EE_REG, addr); + aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ); + + for (i = 0; i != AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE) + break; + if (uether_pause(&sc->sc_ue, hz / 100)) + break; + } + + if (i == AUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "EEPROM read timed out\n"); + + return (aue_csr_read_2(sc, AUE_EE_DATA)); +} + +/* + * Read station address(offset 0) from the EEPROM. + */ +static void +aue_read_mac(struct aue_softc *sc, uint8_t *eaddr) +{ + int i, offset; + uint16_t word; + + for (i = 0, offset = 0; i < ETHER_ADDR_LEN / 2; i++) { + word = aue_eeprom_getword(sc, offset + i); + eaddr[i * 2] = (uint8_t)word; + eaddr[i * 2 + 1] = (uint8_t)(word >> 8); + } +} + +static int +aue_miibus_readreg(device_t dev, int phy, int reg) +{ + struct aue_softc *sc = device_get_softc(dev); + int i, locked; + uint16_t val = 0; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AUE_LOCK(sc); + + /* + * The Am79C901 HomePNA PHY actually contains two transceivers: a 1Mbps + * HomePNA PHY and a 10Mbps full/half duplex ethernet PHY with NWAY + * autoneg. However in the ADMtek adapter, only the 1Mbps PHY is + * actually connected to anything, so we ignore the 10Mbps one. It + * happens to be configured for MII address 3, so we filter that out. + */ + if (sc->sc_flags & AUE_FLAG_DUAL_PHY) { + if (phy == 3) + goto done; +#if 0 + if (phy != 1) + goto done; +#endif + } + aue_csr_write_1(sc, AUE_PHY_ADDR, phy); + aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ); + + for (i = 0; i != AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) + break; + if (uether_pause(&sc->sc_ue, hz / 100)) + break; + } + + if (i == AUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "MII read timed out\n"); + + val = aue_csr_read_2(sc, AUE_PHY_DATA); + +done: + if (!locked) + AUE_UNLOCK(sc); + return (val); +} + +static int +aue_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct aue_softc *sc = device_get_softc(dev); + int i; + int locked; + + if (phy == 3) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AUE_LOCK(sc); + + aue_csr_write_2(sc, AUE_PHY_DATA, data); + aue_csr_write_1(sc, AUE_PHY_ADDR, phy); + aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE); + + for (i = 0; i != AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) + break; + if (uether_pause(&sc->sc_ue, hz / 100)) + break; + } + + if (i == AUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "MII write timed out\n"); + + if (!locked) + AUE_UNLOCK(sc); + return (0); +} + +static void +aue_miibus_statchg(device_t dev) +{ + struct aue_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + int locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AUE_LOCK(sc); + + AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) + AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); + else + AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) + AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); + else + AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); + + AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); + + /* + * Set the LED modes on the LinkSys adapter. + * This turns on the 'dual link LED' bin in the auxmode + * register of the Broadcom PHY. + */ + if (sc->sc_flags & AUE_FLAG_LSYS) { + uint16_t auxmode; + + auxmode = aue_miibus_readreg(dev, 0, 0x1b); + aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04); + } + if (!locked) + AUE_UNLOCK(sc); +} + +#define AUE_BITS 6 +static void +aue_setmulti(struct usb_ether *ue) +{ + struct aue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t h = 0; + uint32_t i; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); + return; + } + + AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); + + /* now program new ones */ + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_le(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1); + hashtbl[(h >> 3)] |= 1 << (h & 0x7); + } + if_maddr_runlock(ifp); + + /* write the hashtable */ + for (i = 0; i != 8; i++) + aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]); +} + +static void +aue_reset_pegasus_II(struct aue_softc *sc) +{ + /* Magic constants taken from Linux driver. */ + aue_csr_write_1(sc, AUE_REG_1D, 0); + aue_csr_write_1(sc, AUE_REG_7B, 2); +#if 0 + if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode) + aue_csr_write_1(sc, AUE_REG_81, 6); + else +#endif + aue_csr_write_1(sc, AUE_REG_81, 2); +} + +static void +aue_reset(struct aue_softc *sc) +{ + int i; + + AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC); + + for (i = 0; i != AUE_TIMEOUT; i++) { + if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) + break; + if (uether_pause(&sc->sc_ue, hz / 100)) + break; + } + + if (i == AUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "reset failed\n"); + + /* + * The PHY(s) attached to the Pegasus chip may be held + * in reset until we flip on the GPIO outputs. Make sure + * to set the GPIO pins high so that the PHY(s) will + * be enabled. + * + * NOTE: We used to force all of the GPIO pins low first and then + * enable the ones we want. This has been changed to better + * match the ADMtek's reference design to avoid setting the + * power-down configuration line of the PHY at the same time + * it is reset. + */ + aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1); + aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1|AUE_GPIO_OUT0); + + if (sc->sc_flags & AUE_FLAG_LSYS) { + /* Grrr. LinkSys has to be different from everyone else. */ + aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1); + aue_csr_write_1(sc, AUE_GPIO0, + AUE_GPIO_SEL0|AUE_GPIO_SEL1|AUE_GPIO_OUT0); + } + if (sc->sc_flags & AUE_FLAG_PII) + aue_reset_pegasus_II(sc); + + /* Wait a little while for the chip to get its brains in order: */ + uether_pause(&sc->sc_ue, hz / 100); +} + +static void +aue_attach_post(struct usb_ether *ue) +{ + struct aue_softc *sc = uether_getsc(ue); + + /* reset the adapter */ + aue_reset(sc); + + /* get station address from the EEPROM */ + aue_read_mac(sc, ue->ue_eaddr); +} + +/* + * Probe for a Pegasus chip. + */ +static int +aue_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != AUE_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != AUE_IFACE_IDX) + return (ENXIO); + /* + * Belkin USB Bluetooth dongles of the F8T012xx1 model series conflict + * with older Belkin USB2LAN adapters. Skip if_aue if we detect one of + * the devices that look like Bluetooth adapters. + */ + if (uaa->info.idVendor == USB_VENDOR_BELKIN && + uaa->info.idProduct == USB_PRODUCT_BELKIN_F8T012 && + uaa->info.bcdDevice == 0x0413) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(aue_devs, sizeof(aue_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +aue_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct aue_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + if (uaa->info.bcdDevice >= 0x0201) { + /* XXX currently undocumented */ + sc->sc_flags |= AUE_FLAG_VER_2; + } + + device_set_usb_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = AUE_IFACE_IDX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, aue_config, AUE_N_TRANSFER, + sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &aue_ue_methods; + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + aue_detach(dev); + return (ENXIO); /* failure */ +} + +static int +aue_detach(device_t dev) +{ + struct aue_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, AUE_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +aue_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct aue_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct aue_intrpkt pkt; + struct usb_page_cache *pc; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && + actlen >= (int)sizeof(pkt)) { + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &pkt, sizeof(pkt)); + + if (pkt.aue_txstat0) + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + if (pkt.aue_txstat0 & (AUE_TXSTAT0_LATECOLL | + AUE_TXSTAT0_EXCESSCOLL)) + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1); + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + return; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +aue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct aue_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + struct aue_rxpkt stat; + struct usb_page_cache *pc; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + pc = usbd_xfer_get_frame(xfer, 0); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "received %d bytes\n", actlen); + + if (sc->sc_flags & AUE_FLAG_VER_2) { + + if (actlen == 0) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + } else { + + if (actlen <= (int)(sizeof(stat) + ETHER_CRC_LEN)) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + usbd_copy_out(pc, actlen - sizeof(stat), &stat, + sizeof(stat)); + + /* + * turn off all the non-error bits in the rx status + * word: + */ + stat.aue_rxstat &= AUE_RXSTAT_MASK; + if (stat.aue_rxstat) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + /* No errors; receive the packet. */ + actlen -= (sizeof(stat) + ETHER_CRC_LEN); + } + uether_rxbuf(ue, pc, 0, actlen); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read error, %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +aue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct aue_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + uint8_t buf[2]; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + pc = usbd_xfer_get_frame(xfer, 0); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer of %d bytes complete\n", actlen); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & AUE_FLAG_LINK) == 0) { + /* + * don't send anything if there is no link ! + */ + return; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + if (m->m_pkthdr.len > MCLBYTES) + m->m_pkthdr.len = MCLBYTES; + if (sc->sc_flags & AUE_FLAG_VER_2) { + + usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); + + usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); + + } else { + + usbd_xfer_set_frame_len(xfer, 0, (m->m_pkthdr.len + 2)); + + /* + * The ADMtek documentation says that the + * packet length is supposed to be specified + * in the first two bytes of the transfer, + * however it actually seems to ignore this + * info and base the frame size on the bulk + * transfer length. + */ + buf[0] = (uint8_t)(m->m_pkthdr.len); + buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); + + usbd_copy_in(pc, 0, buf, 2); + usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len); + } + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usbd_transfer_submit(xfer); + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +aue_tick(struct usb_ether *ue) +{ + struct aue_softc *sc = uether_getsc(ue); + struct mii_data *mii = GET_MII(sc); + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & AUE_FLAG_LINK) == 0 + && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sc_flags |= AUE_FLAG_LINK; + aue_start(ue); + } +} + +static void +aue_start(struct usb_ether *ue) +{ + struct aue_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[AUE_INTR_DT_RD]); + usbd_transfer_start(sc->sc_xfer[AUE_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[AUE_BULK_DT_WR]); +} + +static void +aue_init(struct usb_ether *ue) +{ + struct aue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + int i; + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Cancel pending I/O + */ + aue_reset(sc); + + /* Set MAC address */ + for (i = 0; i != ETHER_ADDR_LEN; i++) + aue_csr_write_1(sc, AUE_PAR0 + i, IF_LLADDR(ifp)[i]); + + /* update promiscuous setting */ + aue_setpromisc(ue); + + /* Load the multicast filter. */ + aue_setmulti(ue); + + /* Enable RX and TX */ + aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB); + AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB); + AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR); + + usbd_xfer_set_stall(sc->sc_xfer[AUE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + aue_start(ue); +} + +static void +aue_setpromisc(struct usb_ether *ue) +{ + struct aue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + /* if we want promiscuous mode, set the allframes bit: */ + if (ifp->if_flags & IFF_PROMISC) + AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); + else + AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); +} + +/* + * Set media options. + */ +static int +aue_ifmedia_upd(struct ifnet *ifp) +{ + struct aue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + struct mii_softc *miisc; + int error; + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_flags &= ~AUE_FLAG_LINK; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + return (error); +} + +/* + * Report current media status. + */ +static void +aue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct aue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + AUE_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + AUE_UNLOCK(sc); +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +aue_stop(struct usb_ether *ue) +{ + struct aue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + AUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->sc_flags &= ~AUE_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[AUE_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[AUE_BULK_DT_RD]); + usbd_transfer_stop(sc->sc_xfer[AUE_INTR_DT_RD]); + + aue_csr_write_1(sc, AUE_CTL0, 0); + aue_csr_write_1(sc, AUE_CTL1, 0); + aue_reset(sc); +} diff --git a/freebsd/sys/dev/usb/net/if_auereg.h b/freebsd/sys/dev/usb/net/if_auereg.h new file mode 100644 index 00000000..4d0843eb --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_auereg.h @@ -0,0 +1,220 @@ +/*- + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Register definitions for ADMtek Pegasus AN986 USB to Ethernet + * chip. The Pegasus uses a total of four USB endpoints: the control + * endpoint (0), a bulk read endpoint for receiving packets (1), + * a bulk write endpoint for sending packets (2) and an interrupt + * endpoint for passing RX and TX status (3). Endpoint 0 is used + * to read and write the ethernet module's registers. All registers + * are 8 bits wide. + * + * Packet transfer is done in 64 byte chunks. The last chunk in a + * transfer is denoted by having a length less that 64 bytes. For + * the RX case, the data includes an optional RX status word. + */ + +#define AUE_UR_READREG 0xF0 +#define AUE_UR_WRITEREG 0xF1 + +#define AUE_CONFIG_INDEX 0 /* config number 1 */ +#define AUE_IFACE_IDX 0 + +/* + * Note that while the ADMtek technically has four endpoints, the control + * endpoint (endpoint 0) is regarded as special by the USB code and drivers + * don't have direct access to it (we access it using usbd_do_request() + * when reading/writing registers. Consequently, our endpoint indexes + * don't match those in the ADMtek Pegasus manual: we consider the RX data + * endpoint to be index 0 and work up from there. + */ +enum { + AUE_BULK_DT_WR, + AUE_BULK_DT_RD, + AUE_INTR_DT_RD, + AUE_N_TRANSFER, +}; + +#define AUE_INTR_PKTLEN 0x8 + +#define AUE_CTL0 0x00 +#define AUE_CTL1 0x01 +#define AUE_CTL2 0x02 +#define AUE_MAR0 0x08 +#define AUE_MAR1 0x09 +#define AUE_MAR2 0x0A +#define AUE_MAR3 0x0B +#define AUE_MAR4 0x0C +#define AUE_MAR5 0x0D +#define AUE_MAR6 0x0E +#define AUE_MAR7 0x0F +#define AUE_MAR AUE_MAR0 +#define AUE_PAR0 0x10 +#define AUE_PAR1 0x11 +#define AUE_PAR2 0x12 +#define AUE_PAR3 0x13 +#define AUE_PAR4 0x14 +#define AUE_PAR5 0x15 +#define AUE_PAR AUE_PAR0 +#define AUE_PAUSE0 0x18 +#define AUE_PAUSE1 0x19 +#define AUE_PAUSE AUE_PAUSE0 +#define AUE_RX_FLOWCTL_CNT 0x1A +#define AUE_RX_FLOWCTL_FIFO 0x1B +#define AUE_REG_1D 0x1D +#define AUE_EE_REG 0x20 +#define AUE_EE_DATA0 0x21 +#define AUE_EE_DATA1 0x22 +#define AUE_EE_DATA AUE_EE_DATA0 +#define AUE_EE_CTL 0x23 +#define AUE_PHY_ADDR 0x25 +#define AUE_PHY_DATA0 0x26 +#define AUE_PHY_DATA1 0x27 +#define AUE_PHY_DATA AUE_PHY_DATA0 +#define AUE_PHY_CTL 0x28 +#define AUE_USB_STS 0x2A +#define AUE_TXSTAT0 0x2B +#define AUE_TXSTAT1 0x2C +#define AUE_TXSTAT AUE_TXSTAT0 +#define AUE_RXSTAT 0x2D +#define AUE_PKTLOST0 0x2E +#define AUE_PKTLOST1 0x2F +#define AUE_PKTLOST AUE_PKTLOST0 + +#define AUE_REG_7B 0x7B +#define AUE_GPIO0 0x7E +#define AUE_GPIO1 0x7F +#define AUE_REG_81 0x81 + +#define AUE_CTL0_INCLUDE_RXCRC 0x01 +#define AUE_CTL0_ALLMULTI 0x02 +#define AUE_CTL0_STOP_BACKOFF 0x04 +#define AUE_CTL0_RXSTAT_APPEND 0x08 +#define AUE_CTL0_WAKEON_ENB 0x10 +#define AUE_CTL0_RXPAUSE_ENB 0x20 +#define AUE_CTL0_RX_ENB 0x40 +#define AUE_CTL0_TX_ENB 0x80 + +#define AUE_CTL1_HOMELAN 0x04 +#define AUE_CTL1_RESETMAC 0x08 +#define AUE_CTL1_SPEEDSEL 0x10 /* 0 = 10mbps, 1 = 100mbps */ +#define AUE_CTL1_DUPLEX 0x20 /* 0 = half, 1 = full */ +#define AUE_CTL1_DELAYHOME 0x40 + +#define AUE_CTL2_EP3_CLR 0x01 /* reading EP3 clrs status regs */ +#define AUE_CTL2_RX_BADFRAMES 0x02 +#define AUE_CTL2_RX_PROMISC 0x04 +#define AUE_CTL2_LOOPBACK 0x08 +#define AUE_CTL2_EEPROMWR_ENB 0x10 +#define AUE_CTL2_EEPROM_LOAD 0x20 + +#define AUE_EECTL_WRITE 0x01 +#define AUE_EECTL_READ 0x02 +#define AUE_EECTL_DONE 0x04 + +#define AUE_PHYCTL_PHYREG 0x1F +#define AUE_PHYCTL_WRITE 0x20 +#define AUE_PHYCTL_READ 0x40 +#define AUE_PHYCTL_DONE 0x80 + +#define AUE_USBSTS_SUSPEND 0x01 +#define AUE_USBSTS_RESUME 0x02 + +#define AUE_TXSTAT0_JABTIMO 0x04 +#define AUE_TXSTAT0_CARLOSS 0x08 +#define AUE_TXSTAT0_NOCARRIER 0x10 +#define AUE_TXSTAT0_LATECOLL 0x20 +#define AUE_TXSTAT0_EXCESSCOLL 0x40 +#define AUE_TXSTAT0_UNDERRUN 0x80 + +#define AUE_TXSTAT1_PKTCNT 0x0F +#define AUE_TXSTAT1_FIFO_EMPTY 0x40 +#define AUE_TXSTAT1_FIFO_FULL 0x80 + +#define AUE_RXSTAT_OVERRUN 0x01 +#define AUE_RXSTAT_PAUSE 0x02 + +#define AUE_GPIO_IN0 0x01 +#define AUE_GPIO_OUT0 0x02 +#define AUE_GPIO_SEL0 0x04 +#define AUE_GPIO_IN1 0x08 +#define AUE_GPIO_OUT1 0x10 +#define AUE_GPIO_SEL1 0x20 + +#define AUE_TIMEOUT 100 /* 10*ms */ +#define AUE_MIN_FRAMELEN 60 + +#define AUE_RXSTAT_MCAST 0x01 +#define AUE_RXSTAT_GIANT 0x02 +#define AUE_RXSTAT_RUNT 0x04 +#define AUE_RXSTAT_CRCERR 0x08 +#define AUE_RXSTAT_DRIBBLE 0x10 +#define AUE_RXSTAT_MASK 0x1E + +#define GET_MII(sc) uether_getmii(&(sc)->sc_ue) + +struct aue_intrpkt { + uint8_t aue_txstat0; + uint8_t aue_txstat1; + uint8_t aue_rxstat; + uint8_t aue_rxlostpkt0; + uint8_t aue_rxlostpkt1; + uint8_t aue_wakeupstat; + uint8_t aue_rsvd; +} __packed; + +struct aue_rxpkt { + uint16_t aue_pktlen; + uint8_t aue_rxstat; + uint8_t pad; +} __packed; + +struct aue_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[AUE_N_TRANSFER]; + + int sc_flags; +#define AUE_FLAG_LSYS 0x0001 /* use Linksys reset */ +#define AUE_FLAG_PNA 0x0002 /* has Home PNA */ +#define AUE_FLAG_PII 0x0004 /* Pegasus II chip */ +#define AUE_FLAG_LINK 0x0008 /* wait for link to come up */ +#define AUE_FLAG_VER_2 0x0200 /* chip is version 2 */ +#define AUE_FLAG_DUAL_PHY 0x0400 /* chip has two transcivers */ +}; + +#define AUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define AUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/if_axe.c b/freebsd/sys/dev/usb/net/if_axe.c new file mode 100644 index 00000000..35b9ca2e --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_axe.c @@ -0,0 +1,1507 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 + * Bill Paul <wpaul@windriver.com>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver. + * Used in the LinkSys USB200M and various other adapters. + * + * Manuals available from: + * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF + * Note: you need the manual for the AX88170 chip (USB 1.x ethernet + * controller) to find the definitions for the RX control register. + * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF + * + * Written by Bill Paul <wpaul@windriver.com> + * Senior Engineer + * Wind River Systems + */ + +/* + * The AX88172 provides USB ethernet supports at 10 and 100Mbps. + * It uses an external PHY (reference designs use a RealTek chip), + * and has a 64-bit multicast hash filter. There is some information + * missing from the manual which one needs to know in order to make + * the chip function: + * + * - You must set bit 7 in the RX control register, otherwise the + * chip won't receive any packets. + * - You must initialize all 3 IPG registers, or you won't be able + * to send any packets. + * + * Note that this device appears to only support loading the station + * address via autload from the EEPROM (i.e. there's no way to manaully + * set it). + * + * (Adam Weinberger wanted me to name this driver if_gir.c.) + */ + +/* + * Ax88178 and Ax88772 support backported from the OpenBSD driver. + * 2007/02/12, J.R. Oldroyd, fbsd@opal.com + * + * Manual here: + * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf + * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf + */ + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/endian.h> +#include <sys/kernel.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/sx.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/ethernet.h> +#include <net/if_types.h> +#include <net/if_media.h> +#include <net/if_vlan_var.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR axe_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_axereg.h> + +/* + * AXE_178_MAX_FRAME_BURST + * max frame burst size for Ax88178 and Ax88772 + * 0 2048 bytes + * 1 4096 bytes + * 2 8192 bytes + * 3 16384 bytes + * use the largest your system can handle without USB stalling. + * + * NB: 88772 parts appear to generate lots of input errors with + * a 2K rx buffer and 8K is only slightly faster than 4K on an + * EHCI port on a T42 so change at your own risk. + */ +#define AXE_178_MAX_FRAME_BURST 1 + +#define AXE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) + +#ifdef USB_DEBUG +static int axe_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe"); +SYSCTL_INT(_hw_usb_axe, OID_AUTO, debug, CTLFLAG_RWTUN, &axe_debug, 0, + "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ +static const STRUCT_USB_HOST_ID axe_devs[] = { +#define AXE_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } + AXE_DEV(ABOCOM, UF200, 0), + AXE_DEV(ACERCM, EP1427X2, 0), + AXE_DEV(APPLE, ETHERNET, AXE_FLAG_772), + AXE_DEV(ASIX, AX88172, 0), + AXE_DEV(ASIX, AX88178, AXE_FLAG_178), + AXE_DEV(ASIX, AX88772, AXE_FLAG_772), + AXE_DEV(ASIX, AX88772A, AXE_FLAG_772A), + AXE_DEV(ASIX, AX88772B, AXE_FLAG_772B), + AXE_DEV(ASIX, AX88772B_1, AXE_FLAG_772B), + AXE_DEV(ATEN, UC210T, 0), + AXE_DEV(BELKIN, F5D5055, AXE_FLAG_178), + AXE_DEV(BILLIONTON, USB2AR, 0), + AXE_DEV(CISCOLINKSYS, USB200MV2, AXE_FLAG_772A), + AXE_DEV(COREGA, FETHER_USB2_TX, 0), + AXE_DEV(DLINK, DUBE100, 0), + AXE_DEV(DLINK, DUBE100B1, AXE_FLAG_772), + AXE_DEV(DLINK, DUBE100C1, AXE_FLAG_772B), + AXE_DEV(GOODWAY, GWUSB2E, 0), + AXE_DEV(IODATA, ETGUS2, AXE_FLAG_178), + AXE_DEV(JVC, MP_PRX1, 0), + AXE_DEV(LENOVO, ETHERNET, AXE_FLAG_772B), + AXE_DEV(LINKSYS2, USB200M, 0), + AXE_DEV(LINKSYS4, USB1000, AXE_FLAG_178), + AXE_DEV(LOGITEC, LAN_GTJU2A, AXE_FLAG_178), + AXE_DEV(MELCO, LUAU2KTX, 0), + AXE_DEV(MELCO, LUA3U2AGT, AXE_FLAG_178), + AXE_DEV(NETGEAR, FA120, 0), + AXE_DEV(OQO, ETHER01PLUS, AXE_FLAG_772), + AXE_DEV(PLANEX3, GU1000T, AXE_FLAG_178), + AXE_DEV(SITECOM, LN029, 0), + AXE_DEV(SITECOMEU, LN028, AXE_FLAG_178), + AXE_DEV(SITECOMEU, LN031, AXE_FLAG_178), + AXE_DEV(SYSTEMTALKS, SGCX2UL, 0), +#undef AXE_DEV +}; + +static device_probe_t axe_probe; +static device_attach_t axe_attach; +static device_detach_t axe_detach; + +static usb_callback_t axe_bulk_read_callback; +static usb_callback_t axe_bulk_write_callback; + +static miibus_readreg_t axe_miibus_readreg; +static miibus_writereg_t axe_miibus_writereg; +static miibus_statchg_t axe_miibus_statchg; + +static uether_fn_t axe_attach_post; +static uether_fn_t axe_init; +static uether_fn_t axe_stop; +static uether_fn_t axe_start; +static uether_fn_t axe_tick; +static uether_fn_t axe_setmulti; +static uether_fn_t axe_setpromisc; + +static int axe_attach_post_sub(struct usb_ether *); +static int axe_ifmedia_upd(struct ifnet *); +static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int axe_cmd(struct axe_softc *, int, int, int, void *); +static void axe_ax88178_init(struct axe_softc *); +static void axe_ax88772_init(struct axe_softc *); +static void axe_ax88772_phywake(struct axe_softc *); +static void axe_ax88772a_init(struct axe_softc *); +static void axe_ax88772b_init(struct axe_softc *); +static int axe_get_phyno(struct axe_softc *, int); +static int axe_ioctl(struct ifnet *, u_long, caddr_t); +static int axe_rx_frame(struct usb_ether *, struct usb_page_cache *, int); +static int axe_rxeof(struct usb_ether *, struct usb_page_cache *, + unsigned int offset, unsigned int, struct axe_csum_hdr *); +static void axe_csum_cfg(struct usb_ether *); + +static const struct usb_config axe_config[AXE_N_TRANSFER] = { + + [AXE_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .frames = 16, + .bufsize = 16 * MCLBYTES, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = axe_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [AXE_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = 16384, /* bytes */ + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = axe_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, +}; + +static const struct ax88772b_mfb ax88772b_mfb_table[] = { + { 0x8000, 0x8001, 2048 }, + { 0x8100, 0x8147, 4096}, + { 0x8200, 0x81EB, 6144}, + { 0x8300, 0x83D7, 8192}, + { 0x8400, 0x851E, 16384}, + { 0x8500, 0x8666, 20480}, + { 0x8600, 0x87AE, 24576}, + { 0x8700, 0x8A3D, 32768} +}; + +static device_method_t axe_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, axe_probe), + DEVMETHOD(device_attach, axe_attach), + DEVMETHOD(device_detach, axe_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, axe_miibus_readreg), + DEVMETHOD(miibus_writereg, axe_miibus_writereg), + DEVMETHOD(miibus_statchg, axe_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t axe_driver = { + .name = "axe", + .methods = axe_methods, + .size = sizeof(struct axe_softc), +}; + +static devclass_t axe_devclass; + +DRIVER_MODULE(axe, uhub, axe_driver, axe_devclass, NULL, 0); +DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(axe, uether, 1, 1, 1); +MODULE_DEPEND(axe, usb, 1, 1, 1); +MODULE_DEPEND(axe, ether, 1, 1, 1); +MODULE_DEPEND(axe, miibus, 1, 1, 1); +MODULE_VERSION(axe, 1); +USB_PNP_HOST_INFO(axe_devs); + +static const struct usb_ether_methods axe_ue_methods = { + .ue_attach_post = axe_attach_post, + .ue_attach_post_sub = axe_attach_post_sub, + .ue_start = axe_start, + .ue_init = axe_init, + .ue_stop = axe_stop, + .ue_tick = axe_tick, + .ue_setmulti = axe_setmulti, + .ue_setpromisc = axe_setpromisc, + .ue_mii_upd = axe_ifmedia_upd, + .ue_mii_sts = axe_ifmedia_sts, +}; + +static int +axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf) +{ + struct usb_device_request req; + usb_error_t err; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + req.bmRequestType = (AXE_CMD_IS_WRITE(cmd) ? + UT_WRITE_VENDOR_DEVICE : + UT_READ_VENDOR_DEVICE); + req.bRequest = AXE_CMD_CMD(cmd); + USETW(req.wValue, val); + USETW(req.wIndex, index); + USETW(req.wLength, AXE_CMD_LEN(cmd)); + + err = uether_do_request(&sc->sc_ue, &req, buf, 1000); + + return (err); +} + +static int +axe_miibus_readreg(device_t dev, int phy, int reg) +{ + struct axe_softc *sc = device_get_softc(dev); + uint16_t val; + int locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXE_LOCK(sc); + + axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); + axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val); + axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); + + val = le16toh(val); + if (AXE_IS_772(sc) && reg == MII_BMSR) { + /* + * BMSR of AX88772 indicates that it supports extended + * capability but the extended status register is + * revered for embedded ethernet PHY. So clear the + * extended capability bit of BMSR. + */ + val &= ~BMSR_EXTCAP; + } + + if (!locked) + AXE_UNLOCK(sc); + return (val); +} + +static int +axe_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct axe_softc *sc = device_get_softc(dev); + int locked; + + val = htole32(val); + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXE_LOCK(sc); + + axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); + axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val); + axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); + + if (!locked) + AXE_UNLOCK(sc); + return (0); +} + +static void +axe_miibus_statchg(device_t dev) +{ + struct axe_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + struct ifnet *ifp; + uint16_t val; + int err, locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXE_LOCK(sc); + + ifp = uether_getifp(&sc->sc_ue); + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done; + + sc->sc_flags &= ~AXE_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->sc_flags |= AXE_FLAG_LINK; + break; + case IFM_1000_T: + if ((sc->sc_flags & AXE_FLAG_178) == 0) + break; + sc->sc_flags |= AXE_FLAG_LINK; + break; + default: + break; + } + } + + /* Lost link, do nothing. */ + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) + goto done; + + val = 0; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { + val |= AXE_MEDIA_FULL_DUPLEX; + if (AXE_IS_178_FAMILY(sc)) { + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_TXPAUSE) != 0) + val |= AXE_178_MEDIA_TXFLOW_CONTROL_EN; + if ((IFM_OPTIONS(mii->mii_media_active) & + IFM_ETH_RXPAUSE) != 0) + val |= AXE_178_MEDIA_RXFLOW_CONTROL_EN; + } + } + if (AXE_IS_178_FAMILY(sc)) { + val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC; + if ((sc->sc_flags & AXE_FLAG_178) != 0) + val |= AXE_178_MEDIA_ENCK; + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_1000_T: + val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; + break; + case IFM_100_TX: + val |= AXE_178_MEDIA_100TX; + break; + case IFM_10_T: + /* doesn't need to be handled */ + break; + } + } + err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); + if (err) + device_printf(dev, "media change failed, error %d\n", err); +done: + if (!locked) + AXE_UNLOCK(sc); +} + +/* + * Set media options. + */ +static int +axe_ifmedia_upd(struct ifnet *ifp) +{ + struct axe_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + struct mii_softc *miisc; + int error; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + return (error); +} + +/* + * Report current media status. + */ +static void +axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct axe_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + AXE_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + AXE_UNLOCK(sc); +} + +static void +axe_setmulti(struct usb_ether *ue) +{ + struct axe_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t h = 0; + uint16_t rxmode; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); + rxmode = le16toh(rxmode); + + if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { + rxmode |= AXE_RXCMD_ALLMULTI; + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + return; + } + rxmode &= ~AXE_RXCMD_ALLMULTI; + + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + hashtbl[h / 8] |= 1 << (h % 8); + } + if_maddr_runlock(ifp); + + axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl); + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); +} + +static int +axe_get_phyno(struct axe_softc *sc, int sel) +{ + int phyno; + + switch (AXE_PHY_TYPE(sc->sc_phyaddrs[sel])) { + case PHY_TYPE_100_HOME: + case PHY_TYPE_GIG: + phyno = AXE_PHY_NO(sc->sc_phyaddrs[sel]); + break; + case PHY_TYPE_SPECIAL: + /* FALLTHROUGH */ + case PHY_TYPE_RSVD: + /* FALLTHROUGH */ + case PHY_TYPE_NON_SUP: + /* FALLTHROUGH */ + default: + phyno = -1; + break; + } + + return (phyno); +} + +#define AXE_GPIO_WRITE(x, y) do { \ + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, (x), NULL); \ + uether_pause(ue, (y)); \ +} while (0) + +static void +axe_ax88178_init(struct axe_softc *sc) +{ + struct usb_ether *ue; + int gpio0, ledmode, phymode; + uint16_t eeprom, val; + + ue = &sc->sc_ue; + axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); + /* XXX magic */ + axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom); + eeprom = le16toh(eeprom); + axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); + + /* if EEPROM is invalid we have to use to GPIO0 */ + if (eeprom == 0xffff) { + phymode = AXE_PHY_MODE_MARVELL; + gpio0 = 1; + ledmode = 0; + } else { + phymode = eeprom & 0x7f; + gpio0 = (eeprom & 0x80) ? 0 : 1; + ledmode = eeprom >> 8; + } + + if (bootverbose) + device_printf(sc->sc_ue.ue_dev, + "EEPROM data : 0x%04x, phymode : 0x%02x\n", eeprom, + phymode); + /* Program GPIOs depending on PHY hardware. */ + switch (phymode) { + case AXE_PHY_MODE_MARVELL: + if (gpio0 == 1) { + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0_EN, + hz / 32); + AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, + hz / 32); + AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2_EN, hz / 4); + AXE_GPIO_WRITE(AXE_GPIO0_EN | AXE_GPIO2 | AXE_GPIO2_EN, + hz / 32); + } else { + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | + AXE_GPIO1_EN, hz / 3); + if (ledmode == 1) { + AXE_GPIO_WRITE(AXE_GPIO1_EN, hz / 3); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN, + hz / 3); + } else { + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | + AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | + AXE_GPIO2_EN, hz / 4); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | + AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); + } + } + break; + case AXE_PHY_MODE_CICADA: + case AXE_PHY_MODE_CICADA_V2: + case AXE_PHY_MODE_CICADA_V2_ASIX: + if (gpio0 == 1) + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO0 | + AXE_GPIO0_EN, hz / 32); + else + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | + AXE_GPIO1_EN, hz / 32); + break; + case AXE_PHY_MODE_AGERE: + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM | AXE_GPIO1 | + AXE_GPIO1_EN, hz / 32); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | + AXE_GPIO2_EN, hz / 32); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2_EN, hz / 4); + AXE_GPIO_WRITE(AXE_GPIO1 | AXE_GPIO1_EN | AXE_GPIO2 | + AXE_GPIO2_EN, hz / 32); + break; + case AXE_PHY_MODE_REALTEK_8211CL: + case AXE_PHY_MODE_REALTEK_8211BN: + case AXE_PHY_MODE_REALTEK_8251CL: + val = gpio0 == 1 ? AXE_GPIO0 | AXE_GPIO0_EN : + AXE_GPIO1 | AXE_GPIO1_EN; + AXE_GPIO_WRITE(val, hz / 32); + AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); + AXE_GPIO_WRITE(val | AXE_GPIO2_EN, hz / 4); + AXE_GPIO_WRITE(val | AXE_GPIO2 | AXE_GPIO2_EN, hz / 32); + if (phymode == AXE_PHY_MODE_REALTEK_8211CL) { + axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, + 0x1F, 0x0005); + axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, + 0x0C, 0x0000); + val = axe_miibus_readreg(ue->ue_dev, sc->sc_phyno, + 0x0001); + axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, + 0x01, val | 0x0080); + axe_miibus_writereg(ue->ue_dev, sc->sc_phyno, + 0x1F, 0x0000); + } + break; + default: + /* Unknown PHY model or no need to program GPIOs. */ + break; + } + + /* soft reset */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); + uether_pause(ue, hz / 4); + + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); + uether_pause(ue, hz / 4); + /* Enable MII/GMII/RGMII interface to work with external PHY. */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); + uether_pause(ue, hz / 4); + + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); +} + +static void +axe_ax88772_init(struct axe_softc *sc) +{ + axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); + uether_pause(&sc->sc_ue, hz / 16); + + if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) { + /* ask for the embedded PHY */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); + uether_pause(&sc->sc_ue, hz / 64); + + /* power down and reset state, pin reset state */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_CLEAR, NULL); + uether_pause(&sc->sc_ue, hz / 16); + + /* power down/reset state, pin operating state */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); + uether_pause(&sc->sc_ue, hz / 4); + + /* power up, reset */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); + + /* power up, operating */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); + } else { + /* ask for external PHY */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); + uether_pause(&sc->sc_ue, hz / 64); + + /* power down internal PHY */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, + AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); + } + + uether_pause(&sc->sc_ue, hz / 4); + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); +} + +static void +axe_ax88772_phywake(struct axe_softc *sc) +{ + struct usb_ether *ue; + + ue = &sc->sc_ue; + if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) { + /* Manually select internal(embedded) PHY - MAC mode. */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB | + AXE_SW_PHY_SELECT_EMBEDDED | AXE_SW_PHY_SELECT_SS_MII, + NULL); + uether_pause(&sc->sc_ue, hz / 32); + } else { + /* + * Manually select external PHY - MAC mode. + * Reverse MII/RMII is for AX88772A PHY mode. + */ + axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB | + AXE_SW_PHY_SELECT_EXT | AXE_SW_PHY_SELECT_SS_MII, NULL); + uether_pause(&sc->sc_ue, hz / 32); + } + /* Take PHY out of power down. */ + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | + AXE_SW_RESET_IPRL, NULL); + uether_pause(&sc->sc_ue, hz / 4); + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); + uether_pause(&sc->sc_ue, hz); + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); + uether_pause(&sc->sc_ue, hz / 32); + axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL); + uether_pause(&sc->sc_ue, hz / 32); +} + +static void +axe_ax88772a_init(struct axe_softc *sc) +{ + struct usb_ether *ue; + + ue = &sc->sc_ue; + /* Reload EEPROM. */ + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32); + axe_ax88772_phywake(sc); + /* Stop MAC. */ + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); +} + +static void +axe_ax88772b_init(struct axe_softc *sc) +{ + struct usb_ether *ue; + uint16_t eeprom; + uint8_t *eaddr; + int i; + + ue = &sc->sc_ue; + /* Reload EEPROM. */ + AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32); + /* + * Save PHY power saving configuration(high byte) and + * clear EEPROM checksum value(low byte). + */ + axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_PHY_PWRCFG, &eeprom); + sc->sc_pwrcfg = le16toh(eeprom) & 0xFF00; + + /* + * Auto-loaded default station address from internal ROM is + * 00:00:00:00:00:00 such that an explicit access to EEPROM + * is required to get real station address. + */ + eaddr = ue->ue_eaddr; + for (i = 0; i < ETHER_ADDR_LEN / 2; i++) { + axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_NODE_ID + i, + &eeprom); + eeprom = le16toh(eeprom); + *eaddr++ = (uint8_t)(eeprom & 0xFF); + *eaddr++ = (uint8_t)((eeprom >> 8) & 0xFF); + } + /* Wakeup PHY. */ + axe_ax88772_phywake(sc); + /* Stop MAC. */ + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); +} + +#undef AXE_GPIO_WRITE + +static void +axe_reset(struct axe_softc *sc) +{ + struct usb_config_descriptor *cd; + usb_error_t err; + + cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); + + err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) + DPRINTF("reset failed (ignored)\n"); + + /* Wait a little while for the chip to get its brains in order. */ + uether_pause(&sc->sc_ue, hz / 100); + + /* Reinitialize controller to achieve full reset. */ + if (sc->sc_flags & AXE_FLAG_178) + axe_ax88178_init(sc); + else if (sc->sc_flags & AXE_FLAG_772) + axe_ax88772_init(sc); + else if (sc->sc_flags & AXE_FLAG_772A) + axe_ax88772a_init(sc); + else if (sc->sc_flags & AXE_FLAG_772B) + axe_ax88772b_init(sc); +} + +static void +axe_attach_post(struct usb_ether *ue) +{ + struct axe_softc *sc = uether_getsc(ue); + + /* + * Load PHY indexes first. Needed by axe_xxx_init(). + */ + axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); + if (bootverbose) + device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n", + sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]); + sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); + if (sc->sc_phyno == -1) + sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); + if (sc->sc_phyno == -1) { + device_printf(sc->sc_ue.ue_dev, + "no valid PHY address found, assuming PHY address 0\n"); + sc->sc_phyno = 0; + } + + /* Initialize controller and get station address. */ + if (sc->sc_flags & AXE_FLAG_178) { + axe_ax88178_init(sc); + axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); + } else if (sc->sc_flags & AXE_FLAG_772) { + axe_ax88772_init(sc); + axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); + } else if (sc->sc_flags & AXE_FLAG_772A) { + axe_ax88772a_init(sc); + axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); + } else if (sc->sc_flags & AXE_FLAG_772B) { + axe_ax88772b_init(sc); + } else + axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr); + + /* + * Fetch IPG values. + */ + if (sc->sc_flags & (AXE_FLAG_772A | AXE_FLAG_772B)) { + /* Set IPG values. */ + sc->sc_ipgs[0] = 0x15; + sc->sc_ipgs[1] = 0x16; + sc->sc_ipgs[2] = 0x1A; + } else + axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs); +} + +static int +axe_attach_post_sub(struct usb_ether *ue) +{ + struct axe_softc *sc; + struct ifnet *ifp; + u_int adv_pause; + int error; + + sc = uether_getsc(ue); + ifp = ue->ue_ifp; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = uether_start; + ifp->if_ioctl = axe_ioctl; + ifp->if_init = uether_init; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + if (AXE_IS_178_FAMILY(sc)) + ifp->if_capabilities |= IFCAP_VLAN_MTU; + if (sc->sc_flags & AXE_FLAG_772B) { + ifp->if_capabilities |= IFCAP_TXCSUM | IFCAP_RXCSUM; + ifp->if_hwassist = AXE_CSUM_FEATURES; + /* + * Checksum offloading of AX88772B also works with VLAN + * tagged frames but there is no way to take advantage + * of the feature because vlan(4) assumes + * IFCAP_VLAN_HWTAGGING is prerequisite condition to + * support checksum offloading with VLAN. VLAN hardware + * tagging support of AX88772B is very limited so it's + * not possible to announce IFCAP_VLAN_HWTAGGING. + */ + } + ifp->if_capenable = ifp->if_capabilities; + if (sc->sc_flags & (AXE_FLAG_772A | AXE_FLAG_772B | AXE_FLAG_178)) + adv_pause = MIIF_DOPAUSE; + else + adv_pause = 0; + mtx_lock(&Giant); + error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, + uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, + BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, adv_pause); + mtx_unlock(&Giant); + + return (error); +} + +/* + * Probe for a AX88172 chip. + */ +static int +axe_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != AXE_CONFIG_IDX) + return (ENXIO); + if (uaa->info.bIfaceIndex != AXE_IFACE_IDX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(axe_devs, sizeof(axe_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +axe_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct axe_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = AXE_IFACE_IDX; + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + axe_config, AXE_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &axe_ue_methods; + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + axe_detach(dev); + return (ENXIO); /* failure */ +} + +static int +axe_detach(device_t dev) +{ + struct axe_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +#if (AXE_BULK_BUF_SIZE >= 0x10000) +#error "Please update axe_bulk_read_callback()!" +#endif + +static void +axe_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct axe_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct usb_page_cache *pc; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + pc = usbd_xfer_get_frame(xfer, 0); + axe_rx_frame(ue, pc, actlen); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read error, %s\n", usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + + } +} + +static int +axe_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen) +{ + struct axe_softc *sc; + struct axe_sframe_hdr hdr; + struct axe_csum_hdr csum_hdr; + int error, len, pos; + + sc = uether_getsc(ue); + pos = 0; + len = 0; + error = 0; + if ((sc->sc_flags & AXE_FLAG_STD_FRAME) != 0) { + while (pos < actlen) { + if ((int)(pos + sizeof(hdr)) > actlen) { + /* too little data */ + error = EINVAL; + break; + } + usbd_copy_out(pc, pos, &hdr, sizeof(hdr)); + + if ((hdr.len ^ hdr.ilen) != sc->sc_lenmask) { + /* we lost sync */ + error = EINVAL; + break; + } + pos += sizeof(hdr); + len = le16toh(hdr.len); + if (pos + len > actlen) { + /* invalid length */ + error = EINVAL; + break; + } + axe_rxeof(ue, pc, pos, len, NULL); + pos += len + (len % 2); + } + } else if ((sc->sc_flags & AXE_FLAG_CSUM_FRAME) != 0) { + while (pos < actlen) { + if ((int)(pos + sizeof(csum_hdr)) > actlen) { + /* too little data */ + error = EINVAL; + break; + } + usbd_copy_out(pc, pos, &csum_hdr, sizeof(csum_hdr)); + + csum_hdr.len = le16toh(csum_hdr.len); + csum_hdr.ilen = le16toh(csum_hdr.ilen); + csum_hdr.cstatus = le16toh(csum_hdr.cstatus); + if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^ + AXE_CSUM_RXBYTES(csum_hdr.ilen)) != + sc->sc_lenmask) { + /* we lost sync */ + error = EINVAL; + break; + } + /* + * Get total transferred frame length including + * checksum header. The length should be multiple + * of 4. + */ + len = sizeof(csum_hdr) + AXE_CSUM_RXBYTES(csum_hdr.len); + len = (len + 3) & ~3; + if (pos + len > actlen) { + /* invalid length */ + error = EINVAL; + break; + } + axe_rxeof(ue, pc, pos + sizeof(csum_hdr), + AXE_CSUM_RXBYTES(csum_hdr.len), &csum_hdr); + pos += len; + } + } else + axe_rxeof(ue, pc, 0, actlen, NULL); + + if (error != 0) + if_inc_counter(ue->ue_ifp, IFCOUNTER_IERRORS, 1); + return (error); +} + +static int +axe_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned int offset, + unsigned int len, struct axe_csum_hdr *csum_hdr) +{ + struct ifnet *ifp = ue->ue_ifp; + struct mbuf *m; + + if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return (EINVAL); + } + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + return (ENOMEM); + } + m->m_len = m->m_pkthdr.len = MCLBYTES; + m_adj(m, ETHER_ALIGN); + + usbd_copy_out(pc, offset, mtod(m, uint8_t *), len); + + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + if (csum_hdr != NULL && csum_hdr->cstatus & AXE_CSUM_HDR_L3_TYPE_IPV4) { + if ((csum_hdr->cstatus & (AXE_CSUM_HDR_L4_CSUM_ERR | + AXE_CSUM_HDR_L3_CSUM_ERR)) == 0) { + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | + CSUM_IP_VALID; + if ((csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) == + AXE_CSUM_HDR_L4_TYPE_TCP || + (csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) == + AXE_CSUM_HDR_L4_TYPE_UDP) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } + } + + _IF_ENQUEUE(&ue->ue_rxq, m); + return (0); +} + +#if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4))) +#error "Please update axe_bulk_write_callback()!" +#endif + +static void +axe_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct axe_softc *sc = usbd_xfer_softc(xfer); + struct axe_sframe_hdr hdr; + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + int nframes, pos; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & AXE_FLAG_LINK) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { + /* + * Don't send anything if there is no link or + * controller is busy. + */ + return; + } + + for (nframes = 0; nframes < 16 && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES, + nframes); + pos = 0; + pc = usbd_xfer_get_frame(xfer, nframes); + if (AXE_IS_178_FAMILY(sc)) { + hdr.len = htole16(m->m_pkthdr.len); + hdr.ilen = ~hdr.len; + /* + * If upper stack computed checksum, driver + * should tell controller not to insert + * computed checksum for checksum offloading + * enabled controller. + */ + if (ifp->if_capabilities & IFCAP_TXCSUM) { + if ((m->m_pkthdr.csum_flags & + AXE_CSUM_FEATURES) != 0) + hdr.len |= htole16( + AXE_TX_CSUM_PSEUDO_HDR); + else + hdr.len |= htole16( + AXE_TX_CSUM_DIS); + } + usbd_copy_in(pc, pos, &hdr, sizeof(hdr)); + pos += sizeof(hdr); + usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len); + pos += m->m_pkthdr.len; + if ((pos % 512) == 0) { + hdr.len = 0; + hdr.ilen = 0xffff; + usbd_copy_in(pc, pos, &hdr, + sizeof(hdr)); + pos += sizeof(hdr); + } + } else { + usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len); + pos += m->m_pkthdr.len; + } + + /* + * XXX + * Update TX packet counter here. This is not + * correct way but it seems that there is no way + * to know how many packets are sent at the end + * of transfer because controller combines + * multiple writes into single one if there is + * room in TX buffer of controller. + */ + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + /* Set frame length. */ + usbd_xfer_set_frame_len(xfer, nframes, pos); + } + if (nframes != 0) { + usbd_xfer_set_frames(xfer, nframes); + usbd_transfer_submit(xfer); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + return; + /* NOTREACHED */ + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + + } +} + +static void +axe_tick(struct usb_ether *ue) +{ + struct axe_softc *sc = uether_getsc(ue); + struct mii_data *mii = GET_MII(sc); + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { + axe_miibus_statchg(ue->ue_dev); + if ((sc->sc_flags & AXE_FLAG_LINK) != 0) + axe_start(ue); + } +} + +static void +axe_start(struct usb_ether *ue) +{ + struct axe_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[AXE_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[AXE_BULK_DT_WR]); +} + +static void +axe_csum_cfg(struct usb_ether *ue) +{ + struct axe_softc *sc; + struct ifnet *ifp; + uint16_t csum1, csum2; + + sc = uether_getsc(ue); + AXE_LOCK_ASSERT(sc, MA_OWNED); + + if ((sc->sc_flags & AXE_FLAG_772B) != 0) { + ifp = uether_getifp(ue); + csum1 = 0; + csum2 = 0; + if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) + csum1 |= AXE_TXCSUM_IP | AXE_TXCSUM_TCP | + AXE_TXCSUM_UDP; + axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL); + csum1 = 0; + csum2 = 0; + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) + csum1 |= AXE_RXCSUM_IP | AXE_RXCSUM_IPVE | + AXE_RXCSUM_TCP | AXE_RXCSUM_UDP | AXE_RXCSUM_ICMP | + AXE_RXCSUM_IGMP; + axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL); + } +} + +static void +axe_init(struct usb_ether *ue) +{ + struct axe_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + uint16_t rxmode; + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + /* Cancel pending I/O */ + axe_stop(ue); + + axe_reset(sc); + + /* Set MAC address and transmitter IPG values. */ + if (AXE_IS_178_FAMILY(sc)) { + axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp)); + axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2], + (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL); + } else { + axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp)); + axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL); + axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL); + axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL); + } + + if (AXE_IS_178_FAMILY(sc)) { + sc->sc_flags &= ~(AXE_FLAG_STD_FRAME | AXE_FLAG_CSUM_FRAME); + if ((sc->sc_flags & AXE_FLAG_772B) != 0 && + (ifp->if_capenable & IFCAP_RXCSUM) != 0) { + sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK; + sc->sc_flags |= AXE_FLAG_CSUM_FRAME; + } else { + sc->sc_lenmask = AXE_HDR_LEN_MASK; + sc->sc_flags |= AXE_FLAG_STD_FRAME; + } + } + + /* Configure TX/RX checksum offloading. */ + axe_csum_cfg(ue); + + if (sc->sc_flags & AXE_FLAG_772B) { + /* AX88772B uses different maximum frame burst configuration. */ + axe_cmd(sc, AXE_772B_CMD_RXCTL_WRITE_CFG, + ax88772b_mfb_table[AX88772B_MFB_16K].threshold, + ax88772b_mfb_table[AX88772B_MFB_16K].byte_cnt, NULL); + } + + /* Enable receiver, set RX mode. */ + rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE); + if (AXE_IS_178_FAMILY(sc)) { + if (sc->sc_flags & AXE_FLAG_772B) { + /* + * Select RX header format type 1. Aligning IP + * header on 4 byte boundary is not needed when + * checksum offloading feature is not used + * because we always copy the received frame in + * RX handler. When RX checksum offloading is + * active, aligning IP header is required to + * reflect actual frame length including RX + * header size. + */ + rxmode |= AXE_772B_RXCMD_HDR_TYPE_1; + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) + rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN; + } else { + /* + * Default Rx buffer size is too small to get + * maximum performance. + */ + rxmode |= AXE_178_RXCMD_MFB_16384; + } + } else { + rxmode |= AXE_172_RXCMD_UNICAST; + } + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + rxmode |= AXE_RXCMD_PROMISC; + + if (ifp->if_flags & IFF_BROADCAST) + rxmode |= AXE_RXCMD_BROADCAST; + + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + + /* Load the multicast filter. */ + axe_setmulti(ue); + + usbd_xfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + /* Switch to selected media. */ + axe_ifmedia_upd(ifp); +} + +static void +axe_setpromisc(struct usb_ether *ue) +{ + struct axe_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + uint16_t rxmode; + + axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); + + rxmode = le16toh(rxmode); + + if (ifp->if_flags & IFF_PROMISC) { + rxmode |= AXE_RXCMD_PROMISC; + } else { + rxmode &= ~AXE_RXCMD_PROMISC; + } + + axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); + + axe_setmulti(ue); +} + +static void +axe_stop(struct usb_ether *ue) +{ + struct axe_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + AXE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->sc_flags &= ~AXE_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]); +} + +static int +axe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct usb_ether *ue = ifp->if_softc; + struct axe_softc *sc; + struct ifreq *ifr; + int error, mask, reinit; + + sc = uether_getsc(ue); + ifr = (struct ifreq *)data; + error = 0; + reinit = 0; + if (cmd == SIOCSIFCAP) { + AXE_LOCK(sc); + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if ((mask & IFCAP_TXCSUM) != 0 && + (ifp->if_capabilities & IFCAP_TXCSUM) != 0) { + ifp->if_capenable ^= IFCAP_TXCSUM; + if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) + ifp->if_hwassist |= AXE_CSUM_FEATURES; + else + ifp->if_hwassist &= ~AXE_CSUM_FEATURES; + reinit++; + } + if ((mask & IFCAP_RXCSUM) != 0 && + (ifp->if_capabilities & IFCAP_RXCSUM) != 0) { + ifp->if_capenable ^= IFCAP_RXCSUM; + reinit++; + } + if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING) + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + else + reinit = 0; + AXE_UNLOCK(sc); + if (reinit > 0) + uether_init(ue); + } else + error = uether_ioctl(ifp, cmd, data); + + return (error); +} diff --git a/freebsd/sys/dev/usb/net/if_axereg.h b/freebsd/sys/dev/usb/net/if_axereg.h new file mode 100644 index 00000000..64cb2352 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_axereg.h @@ -0,0 +1,363 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 + * Bill Paul <wpaul@windriver.com>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Definitions for the ASIX Electronics AX88172, AX88178 + * and AX88772 to ethernet controllers. + */ + +/* + * Vendor specific commands. ASIX conveniently doesn't document the 'set + * NODEID' command in their datasheet (thanks a lot guys). + * To make handling these commands easier, I added some extra data which is + * decided by the axe_cmd() routine. Commands are encoded in 16 bits, with + * the format: LDCC. L and D are both nibbles in the high byte. L represents + * the data length (0 to 15) and D represents the direction (0 for vendor read, + * 1 for vendor write). CC is the command byte, as specified in the manual. + */ + +#define AXE_CMD_IS_WRITE(x) (((x) & 0x0F00) >> 8) +#define AXE_CMD_LEN(x) (((x) & 0xF000) >> 12) +#define AXE_CMD_CMD(x) ((x) & 0x00FF) + +#define AXE_172_CMD_READ_RXTX_SRAM 0x2002 +#define AXE_182_CMD_READ_RXTX_SRAM 0x8002 +#define AXE_172_CMD_WRITE_RX_SRAM 0x0103 +#define AXE_182_CMD_WRITE_RXTX_SRAM 0x8103 +#define AXE_172_CMD_WRITE_TX_SRAM 0x0104 +#define AXE_CMD_MII_OPMODE_SW 0x0106 +#define AXE_CMD_MII_READ_REG 0x2007 +#define AXE_CMD_MII_WRITE_REG 0x2108 +#define AXE_CMD_MII_READ_OPMODE 0x1009 +#define AXE_CMD_MII_OPMODE_HW 0x010A +#define AXE_CMD_SROM_READ 0x200B +#define AXE_CMD_SROM_WRITE 0x010C +#define AXE_CMD_SROM_WR_ENABLE 0x010D +#define AXE_CMD_SROM_WR_DISABLE 0x010E +#define AXE_CMD_RXCTL_READ 0x200F +#define AXE_CMD_RXCTL_WRITE 0x0110 +#define AXE_CMD_READ_IPG012 0x3011 +#define AXE_172_CMD_WRITE_IPG0 0x0112 +#define AXE_178_CMD_WRITE_IPG012 0x0112 +#define AXE_172_CMD_WRITE_IPG1 0x0113 +#define AXE_178_CMD_READ_NODEID 0x6013 +#define AXE_172_CMD_WRITE_IPG2 0x0114 +#define AXE_178_CMD_WRITE_NODEID 0x6114 +#define AXE_CMD_READ_MCAST 0x8015 +#define AXE_CMD_WRITE_MCAST 0x8116 +#define AXE_172_CMD_READ_NODEID 0x6017 +#define AXE_172_CMD_WRITE_NODEID 0x6118 + +#define AXE_CMD_READ_PHYID 0x2019 +#define AXE_172_CMD_READ_MEDIA 0x101A +#define AXE_178_CMD_READ_MEDIA 0x201A +#define AXE_CMD_WRITE_MEDIA 0x011B +#define AXE_CMD_READ_MONITOR_MODE 0x101C +#define AXE_CMD_WRITE_MONITOR_MODE 0x011D +#define AXE_CMD_READ_GPIO 0x101E +#define AXE_CMD_WRITE_GPIO 0x011F + +#define AXE_CMD_SW_RESET_REG 0x0120 +#define AXE_CMD_SW_PHY_STATUS 0x0021 +#define AXE_CMD_SW_PHY_SELECT 0x0122 + +/* AX88772A and AX88772B only. */ +#define AXE_CMD_READ_VLAN_CTRL 0x4027 +#define AXE_CMD_WRITE_VLAN_CTRL 0x4028 + +#define AXE_772B_CMD_RXCTL_WRITE_CFG 0x012A +#define AXE_772B_CMD_READ_RXCSUM 0x002B +#define AXE_772B_CMD_WRITE_RXCSUM 0x012C +#define AXE_772B_CMD_READ_TXCSUM 0x002D +#define AXE_772B_CMD_WRITE_TXCSUM 0x012E + +#define AXE_SW_RESET_CLEAR 0x00 +#define AXE_SW_RESET_RR 0x01 +#define AXE_SW_RESET_RT 0x02 +#define AXE_SW_RESET_PRTE 0x04 +#define AXE_SW_RESET_PRL 0x08 +#define AXE_SW_RESET_BZ 0x10 +#define AXE_SW_RESET_IPRL 0x20 +#define AXE_SW_RESET_IPPD 0x40 + +/* AX88178 documentation says to always write this bit... */ +#define AXE_178_RESET_MAGIC 0x40 + +#define AXE_178_MEDIA_GMII 0x0001 +#define AXE_MEDIA_FULL_DUPLEX 0x0002 +#define AXE_172_MEDIA_TX_ABORT_ALLOW 0x0004 + +/* AX88178/88772 documentation says to always write 1 to bit 2 */ +#define AXE_178_MEDIA_MAGIC 0x0004 +/* AX88772 documentation says to always write 0 to bit 3 */ +#define AXE_178_MEDIA_ENCK 0x0008 +#define AXE_172_MEDIA_FLOW_CONTROL_EN 0x0010 +#define AXE_178_MEDIA_RXFLOW_CONTROL_EN 0x0010 +#define AXE_178_MEDIA_TXFLOW_CONTROL_EN 0x0020 +#define AXE_178_MEDIA_JUMBO_EN 0x0040 +#define AXE_178_MEDIA_LTPF_ONLY 0x0080 +#define AXE_178_MEDIA_RX_EN 0x0100 +#define AXE_178_MEDIA_100TX 0x0200 +#define AXE_178_MEDIA_SBP 0x0800 +#define AXE_178_MEDIA_SUPERMAC 0x1000 + +#define AXE_RXCMD_PROMISC 0x0001 +#define AXE_RXCMD_ALLMULTI 0x0002 +#define AXE_172_RXCMD_UNICAST 0x0004 +#define AXE_178_RXCMD_KEEP_INVALID_CRC 0x0004 +#define AXE_RXCMD_BROADCAST 0x0008 +#define AXE_RXCMD_MULTICAST 0x0010 +#define AXE_RXCMD_ACCEPT_RUNT 0x0040 /* AX88772B */ +#define AXE_RXCMD_ENABLE 0x0080 +#define AXE_178_RXCMD_MFB_MASK 0x0300 +#define AXE_178_RXCMD_MFB_2048 0x0000 +#define AXE_178_RXCMD_MFB_4096 0x0100 +#define AXE_178_RXCMD_MFB_8192 0x0200 +#define AXE_178_RXCMD_MFB_16384 0x0300 +#define AXE_772B_RXCMD_HDR_TYPE_0 0x0000 +#define AXE_772B_RXCMD_HDR_TYPE_1 0x0100 +#define AXE_772B_RXCMD_IPHDR_ALIGN 0x0200 +#define AXE_772B_RXCMD_ADD_CHKSUM 0x0400 +#define AXE_RXCMD_LOOPBACK 0x1000 /* AX88772A/AX88772B */ + +#define AXE_PHY_SEL_PRI 1 +#define AXE_PHY_SEL_SEC 0 +#define AXE_PHY_TYPE_MASK 0xE0 +#define AXE_PHY_TYPE_SHIFT 5 +#define AXE_PHY_TYPE(x) \ + (((x) & AXE_PHY_TYPE_MASK) >> AXE_PHY_TYPE_SHIFT) + +#define PHY_TYPE_100_HOME 0 /* 10/100 or 1M HOME PHY */ +#define PHY_TYPE_GIG 1 /* Gigabit PHY */ +#define PHY_TYPE_SPECIAL 4 /* Special case */ +#define PHY_TYPE_RSVD 5 /* Reserved */ +#define PHY_TYPE_NON_SUP 7 /* Non-supported PHY */ + +#define AXE_PHY_NO_MASK 0x1F +#define AXE_PHY_NO(x) ((x) & AXE_PHY_NO_MASK) + +#define AXE_772_PHY_NO_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */ + +#define AXE_GPIO0_EN 0x01 +#define AXE_GPIO0 0x02 +#define AXE_GPIO1_EN 0x04 +#define AXE_GPIO1 0x08 +#define AXE_GPIO2_EN 0x10 +#define AXE_GPIO2 0x20 +#define AXE_GPIO_RELOAD_EEPROM 0x80 + +#define AXE_PHY_MODE_MARVELL 0x00 +#define AXE_PHY_MODE_CICADA 0x01 +#define AXE_PHY_MODE_AGERE 0x02 +#define AXE_PHY_MODE_CICADA_V2 0x05 +#define AXE_PHY_MODE_AGERE_GMII 0x06 +#define AXE_PHY_MODE_CICADA_V2_ASIX 0x09 +#define AXE_PHY_MODE_REALTEK_8211CL 0x0C +#define AXE_PHY_MODE_REALTEK_8211BN 0x0D +#define AXE_PHY_MODE_REALTEK_8251CL 0x0E +#define AXE_PHY_MODE_ATTANSIC 0x40 + +/* AX88772A/AX88772B only. */ +#define AXE_SW_PHY_SELECT_EXT 0x0000 +#define AXE_SW_PHY_SELECT_EMBEDDED 0x0001 +#define AXE_SW_PHY_SELECT_AUTO 0x0002 +#define AXE_SW_PHY_SELECT_SS_MII 0x0004 +#define AXE_SW_PHY_SELECT_SS_RVRS_MII 0x0008 +#define AXE_SW_PHY_SELECT_SS_RVRS_RMII 0x000C +#define AXE_SW_PHY_SELECT_SS_ENB 0x0010 + +/* AX88772A/AX88772B VLAN control. */ +#define AXE_VLAN_CTRL_ENB 0x00001000 +#define AXE_VLAN_CTRL_STRIP 0x00002000 +#define AXE_VLAN_CTRL_VID1_MASK 0x00000FFF +#define AXE_VLAN_CTRL_VID2_MASK 0x0FFF0000 + +#define AXE_RXCSUM_IP 0x0001 +#define AXE_RXCSUM_IPVE 0x0002 +#define AXE_RXCSUM_IPV6E 0x0004 +#define AXE_RXCSUM_TCP 0x0008 +#define AXE_RXCSUM_UDP 0x0010 +#define AXE_RXCSUM_ICMP 0x0020 +#define AXE_RXCSUM_IGMP 0x0040 +#define AXE_RXCSUM_ICMP6 0x0080 +#define AXE_RXCSUM_TCPV6 0x0100 +#define AXE_RXCSUM_UDPV6 0x0200 +#define AXE_RXCSUM_ICMPV6 0x0400 +#define AXE_RXCSUM_IGMPV6 0x0800 +#define AXE_RXCSUM_ICMP6V6 0x1000 +#define AXE_RXCSUM_FOPC 0x8000 + +#define AXE_RXCSUM_64TE 0x0100 +#define AXE_RXCSUM_PPPOE 0x0200 +#define AXE_RXCSUM_RPCE 0x8000 + +#define AXE_TXCSUM_IP 0x0001 +#define AXE_TXCSUM_TCP 0x0002 +#define AXE_TXCSUM_UDP 0x0004 +#define AXE_TXCSUM_ICMP 0x0008 +#define AXE_TXCSUM_IGMP 0x0010 +#define AXE_TXCSUM_ICMP6 0x0020 +#define AXE_TXCSUM_TCPV6 0x0100 +#define AXE_TXCSUM_UDPV6 0x0200 +#define AXE_TXCSUM_ICMPV6 0x0400 +#define AXE_TXCSUM_IGMPV6 0x0800 +#define AXE_TXCSUM_ICMP6V6 0x1000 + +#define AXE_TXCSUM_64TE 0x0001 +#define AXE_TXCSUM_PPPOE 0x0002 + +#define AXE_BULK_BUF_SIZE 16384 /* bytes */ + +#define AXE_CTL_READ 0x01 +#define AXE_CTL_WRITE 0x02 + +#define AXE_CONFIG_IDX 0 /* config number 1 */ +#define AXE_IFACE_IDX 0 + +/* EEPROM Map. */ +#define AXE_EEPROM_772B_NODE_ID 0x04 +#define AXE_EEPROM_772B_PHY_PWRCFG 0x18 + +struct ax88772b_mfb { + int byte_cnt; + int threshold; + int size; +}; +#define AX88772B_MFB_2K 0 +#define AX88772B_MFB_4K 1 +#define AX88772B_MFB_6K 2 +#define AX88772B_MFB_8K 3 +#define AX88772B_MFB_16K 4 +#define AX88772B_MFB_20K 5 +#define AX88772B_MFB_24K 6 +#define AX88772B_MFB_32K 7 + +struct axe_sframe_hdr { + uint16_t len; +#define AXE_HDR_LEN_MASK 0xFFFF + uint16_t ilen; +} __packed; + +#define AXE_TX_CSUM_PSEUDO_HDR 0x4000 +#define AXE_TX_CSUM_DIS 0x8000 + +/* + * When RX checksum offloading is enabled, AX88772B uses new RX header + * format and it's not compatible with previous RX header format. In + * addition, IP header align option should be enabled to get correct + * frame size including RX header. Total transferred size including + * the RX header is multiple of 4 and controller will pad necessary + * bytes if the length is not multiple of 4. + * This driver does not enable partial checksum feature which will + * compute 16bit checksum from 14th byte to the end of the frame. If + * this feature is enabled, computed checksum value is embedded into + * RX header which in turn means it uses different RX header format. + */ +struct axe_csum_hdr { + uint16_t len; +#define AXE_CSUM_HDR_LEN_MASK 0x07FF +#define AXE_CSUM_HDR_CRC_ERR 0x1000 +#define AXE_CSUM_HDR_MII_ERR 0x2000 +#define AXE_CSUM_HDR_RUNT 0x4000 +#define AXE_CSUM_HDR_BMCAST 0x8000 + uint16_t ilen; + uint16_t cstatus; +#define AXE_CSUM_HDR_VLAN_MASK 0x0007 +#define AXE_CSUM_HDR_VLAN_STRIP 0x0008 +#define AXE_CSUM_HDR_VLAN_PRI_MASK 0x0070 +#define AXE_CSUM_HDR_L4_CSUM_ERR 0x0100 +#define AXE_CSUM_HDR_L3_CSUM_ERR 0x0200 +#define AXE_CSUM_HDR_L4_TYPE_UDP 0x0400 +#define AXE_CSUM_HDR_L4_TYPE_ICMP 0x0800 +#define AXE_CSUM_HDR_L4_TYPE_IGMP 0x0C00 +#define AXE_CSUM_HDR_L4_TYPE_TCP 0x1000 +#define AXE_CSUM_HDR_L4_TYPE_TCPV6 0x1400 +#define AXE_CSUM_HDR_L4_TYPE_MASK 0x1C00 +#define AXE_CSUM_HDR_L3_TYPE_IPV4 0x2000 +#define AXE_CSUM_HDR_L3_TYPE_IPV6 0x4000 + +#ifdef AXE_APPEND_PARTIAL_CSUM + /* + * These members present only when partial checksum + * offloading is enabled. The checksum value is simple + * 16bit sum of received frame starting at offset 14 of + * the frame to the end of the frame excluding FCS bytes. + */ + uint16_t csum_value; + uint16_t dummy; +#endif +} __packed; + +#define AXE_CSUM_RXBYTES(x) ((x) & AXE_CSUM_HDR_LEN_MASK) + +#define GET_MII(sc) uether_getmii(&(sc)->sc_ue) + +/* The interrupt endpoint is currently unused by the ASIX part. */ +enum { + AXE_BULK_DT_WR, + AXE_BULK_DT_RD, + AXE_N_TRANSFER, +}; + +struct axe_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[AXE_N_TRANSFER]; + int sc_phyno; + + int sc_flags; +#define AXE_FLAG_LINK 0x0001 +#define AXE_FLAG_STD_FRAME 0x0010 +#define AXE_FLAG_CSUM_FRAME 0x0020 +#define AXE_FLAG_772 0x1000 /* AX88772 */ +#define AXE_FLAG_772A 0x2000 /* AX88772A */ +#define AXE_FLAG_772B 0x4000 /* AX88772B */ +#define AXE_FLAG_178 0x8000 /* AX88178 */ + + uint8_t sc_ipgs[3]; + uint8_t sc_phyaddrs[2]; + uint16_t sc_pwrcfg; + uint16_t sc_lenmask; +}; + +#define AXE_IS_178_FAMILY(sc) \ + ((sc)->sc_flags & (AXE_FLAG_772 | AXE_FLAG_772A | AXE_FLAG_772B | \ + AXE_FLAG_178)) + +#define AXE_IS_772(sc) \ + ((sc)->sc_flags & (AXE_FLAG_772 | AXE_FLAG_772A | AXE_FLAG_772B)) + +#define AXE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define AXE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/if_axge.c b/freebsd/sys/dev/usb/net/if_axge.c new file mode 100644 index 00000000..fd50dcdf --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_axge.c @@ -0,0 +1,1056 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2013-2014 Kevin Lo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * ASIX Electronics AX88178A/AX88179 USB 2.0/3.0 gigabit ethernet driver. + */ + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/endian.h> +#include <sys/kernel.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/unistd.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR axge_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_axgereg.h> + +/* + * Various supported device vendors/products. + */ + +static const STRUCT_USB_HOST_ID axge_devs[] = { +#define AXGE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + AXGE_DEV(ASIX, AX88178A), + AXGE_DEV(ASIX, AX88179), + AXGE_DEV(DLINK, DUB1312), + AXGE_DEV(LENOVO, GIGALAN), + AXGE_DEV(SITECOMEU, LN032), +#undef AXGE_DEV +}; + +static const struct { + uint8_t ctrl; + uint8_t timer_l; + uint8_t timer_h; + uint8_t size; + uint8_t ifg; +} __packed axge_bulk_size[] = { + { 7, 0x4f, 0x00, 0x12, 0xff }, + { 7, 0x20, 0x03, 0x16, 0xff }, + { 7, 0xae, 0x07, 0x18, 0xff }, + { 7, 0xcc, 0x4c, 0x18, 0x08 } +}; + +/* prototypes */ + +static device_probe_t axge_probe; +static device_attach_t axge_attach; +static device_detach_t axge_detach; + +static usb_callback_t axge_bulk_read_callback; +static usb_callback_t axge_bulk_write_callback; + +static miibus_readreg_t axge_miibus_readreg; +static miibus_writereg_t axge_miibus_writereg; +static miibus_statchg_t axge_miibus_statchg; + +static uether_fn_t axge_attach_post; +static uether_fn_t axge_init; +static uether_fn_t axge_stop; +static uether_fn_t axge_start; +static uether_fn_t axge_tick; +static uether_fn_t axge_rxfilter; + +static int axge_read_mem(struct axge_softc *, uint8_t, uint16_t, + uint16_t, void *, int); +static void axge_write_mem(struct axge_softc *, uint8_t, uint16_t, + uint16_t, void *, int); +static uint8_t axge_read_cmd_1(struct axge_softc *, uint8_t, uint16_t); +static uint16_t axge_read_cmd_2(struct axge_softc *, uint8_t, uint16_t, + uint16_t); +static void axge_write_cmd_1(struct axge_softc *, uint8_t, uint16_t, + uint8_t); +static void axge_write_cmd_2(struct axge_softc *, uint8_t, uint16_t, + uint16_t, uint16_t); +static void axge_chip_init(struct axge_softc *); +static void axge_reset(struct axge_softc *); + +static int axge_attach_post_sub(struct usb_ether *); +static int axge_ifmedia_upd(struct ifnet *); +static void axge_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int axge_ioctl(struct ifnet *, u_long, caddr_t); +static void axge_rx_frame(struct usb_ether *, struct usb_page_cache *, int); +static void axge_rxeof(struct usb_ether *, struct usb_page_cache *, + unsigned int, unsigned int, uint32_t); +static void axge_csum_cfg(struct usb_ether *); + +#define AXGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) + +#ifdef USB_DEBUG +static int axge_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, axge, CTLFLAG_RW, 0, "USB axge"); +SYSCTL_INT(_hw_usb_axge, OID_AUTO, debug, CTLFLAG_RWTUN, &axge_debug, 0, + "Debug level"); +#endif + +static const struct usb_config axge_config[AXGE_N_TRANSFER] = { + [AXGE_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .frames = AXGE_N_FRAMES, + .bufsize = AXGE_N_FRAMES * MCLBYTES, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = axge_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + [AXGE_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = 65536, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = axge_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, +}; + +static device_method_t axge_methods[] = { + /* Device interface. */ + DEVMETHOD(device_probe, axge_probe), + DEVMETHOD(device_attach, axge_attach), + DEVMETHOD(device_detach, axge_detach), + + /* MII interface. */ + DEVMETHOD(miibus_readreg, axge_miibus_readreg), + DEVMETHOD(miibus_writereg, axge_miibus_writereg), + DEVMETHOD(miibus_statchg, axge_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t axge_driver = { + .name = "axge", + .methods = axge_methods, + .size = sizeof(struct axge_softc), +}; + +static devclass_t axge_devclass; + +DRIVER_MODULE(axge, uhub, axge_driver, axge_devclass, NULL, NULL); +DRIVER_MODULE(miibus, axge, miibus_driver, miibus_devclass, NULL, NULL); +MODULE_DEPEND(axge, uether, 1, 1, 1); +MODULE_DEPEND(axge, usb, 1, 1, 1); +MODULE_DEPEND(axge, ether, 1, 1, 1); +MODULE_DEPEND(axge, miibus, 1, 1, 1); +MODULE_VERSION(axge, 1); +USB_PNP_HOST_INFO(axge_devs); + +static const struct usb_ether_methods axge_ue_methods = { + .ue_attach_post = axge_attach_post, + .ue_attach_post_sub = axge_attach_post_sub, + .ue_start = axge_start, + .ue_init = axge_init, + .ue_stop = axge_stop, + .ue_tick = axge_tick, + .ue_setmulti = axge_rxfilter, + .ue_setpromisc = axge_rxfilter, + .ue_mii_upd = axge_ifmedia_upd, + .ue_mii_sts = axge_ifmedia_sts, +}; + +static int +axge_read_mem(struct axge_softc *sc, uint8_t cmd, uint16_t index, + uint16_t val, void *buf, int len) +{ + struct usb_device_request req; + + AXGE_LOCK_ASSERT(sc, MA_OWNED); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = cmd; + USETW(req.wValue, val); + USETW(req.wIndex, index); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static void +axge_write_mem(struct axge_softc *sc, uint8_t cmd, uint16_t index, + uint16_t val, void *buf, int len) +{ + struct usb_device_request req; + + AXGE_LOCK_ASSERT(sc, MA_OWNED); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = cmd; + USETW(req.wValue, val); + USETW(req.wIndex, index); + USETW(req.wLength, len); + + if (uether_do_request(&sc->sc_ue, &req, buf, 1000)) { + /* Error ignored. */ + } +} + +static uint8_t +axge_read_cmd_1(struct axge_softc *sc, uint8_t cmd, uint16_t reg) +{ + uint8_t val; + + axge_read_mem(sc, cmd, 1, reg, &val, 1); + return (val); +} + +static uint16_t +axge_read_cmd_2(struct axge_softc *sc, uint8_t cmd, uint16_t index, + uint16_t reg) +{ + uint8_t val[2]; + + axge_read_mem(sc, cmd, index, reg, &val, 2); + return (UGETW(val)); +} + +static void +axge_write_cmd_1(struct axge_softc *sc, uint8_t cmd, uint16_t reg, uint8_t val) +{ + axge_write_mem(sc, cmd, 1, reg, &val, 1); +} + +static void +axge_write_cmd_2(struct axge_softc *sc, uint8_t cmd, uint16_t index, + uint16_t reg, uint16_t val) +{ + uint8_t temp[2]; + + USETW(temp, val); + axge_write_mem(sc, cmd, index, reg, &temp, 2); +} + +static int +axge_miibus_readreg(device_t dev, int phy, int reg) +{ + struct axge_softc *sc; + uint16_t val; + int locked; + + sc = device_get_softc(dev); + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXGE_LOCK(sc); + + val = axge_read_cmd_2(sc, AXGE_ACCESS_PHY, reg, phy); + + if (!locked) + AXGE_UNLOCK(sc); + + return (val); +} + +static int +axge_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct axge_softc *sc; + int locked; + + sc = device_get_softc(dev); + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXGE_LOCK(sc); + + axge_write_cmd_2(sc, AXGE_ACCESS_PHY, reg, phy, val); + + if (!locked) + AXGE_UNLOCK(sc); + + return (0); +} + +static void +axge_miibus_statchg(device_t dev) +{ + struct axge_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + uint8_t link_status, tmp[5]; + uint16_t val; + int locked; + + sc = device_get_softc(dev); + mii = GET_MII(sc); + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + AXGE_LOCK(sc); + + ifp = uether_getifp(&sc->sc_ue); + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done; + + sc->sc_flags &= ~AXGE_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + case IFM_1000_T: + sc->sc_flags |= AXGE_FLAG_LINK; + break; + default: + break; + } + } + + /* Lost link, do nothing. */ + if ((sc->sc_flags & AXGE_FLAG_LINK) == 0) + goto done; + + link_status = axge_read_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PLSR); + + val = 0; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { + val |= MSR_FD; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) + val |= MSR_TFC; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) + val |= MSR_RFC; + } + val |= MSR_RE; + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_1000_T: + val |= MSR_GM | MSR_EN_125MHZ; + if (link_status & PLSR_USB_SS) + memcpy(tmp, &axge_bulk_size[0], 5); + else if (link_status & PLSR_USB_HS) + memcpy(tmp, &axge_bulk_size[1], 5); + else + memcpy(tmp, &axge_bulk_size[3], 5); + break; + case IFM_100_TX: + val |= MSR_PS; + if (link_status & (PLSR_USB_SS | PLSR_USB_HS)) + memcpy(tmp, &axge_bulk_size[2], 5); + else + memcpy(tmp, &axge_bulk_size[3], 5); + break; + case IFM_10_T: + memcpy(tmp, &axge_bulk_size[3], 5); + break; + } + /* Rx bulk configuration. */ + axge_write_mem(sc, AXGE_ACCESS_MAC, 5, AXGE_RX_BULKIN_QCTRL, tmp, 5); + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, val); +done: + if (!locked) + AXGE_UNLOCK(sc); +} + +static void +axge_chip_init(struct axge_softc *sc) +{ + /* Power up ethernet PHY. */ + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, 0); + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, EPPRCR_IPRL); + uether_pause(&sc->sc_ue, hz / 4); + axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CLK_SELECT, + AXGE_CLK_SELECT_ACS | AXGE_CLK_SELECT_BCS); + uether_pause(&sc->sc_ue, hz / 10); +} + +static void +axge_reset(struct axge_softc *sc) +{ + struct usb_config_descriptor *cd; + usb_error_t err; + + cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); + + err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) + DPRINTF("reset failed (ignored)\n"); + + /* Wait a little while for the chip to get its brains in order. */ + uether_pause(&sc->sc_ue, hz / 100); + + /* Reinitialize controller to achieve full reset. */ + axge_chip_init(sc); +} + +static void +axge_attach_post(struct usb_ether *ue) +{ + struct axge_softc *sc; + + sc = uether_getsc(ue); + + /* Initialize controller and get station address. */ + axge_chip_init(sc); + axge_read_mem(sc, AXGE_ACCESS_MAC, ETHER_ADDR_LEN, AXGE_NIDR, + ue->ue_eaddr, ETHER_ADDR_LEN); +} + +static int +axge_attach_post_sub(struct usb_ether *ue) +{ + struct axge_softc *sc; + struct ifnet *ifp; + int error; + + sc = uether_getsc(ue); + ifp = ue->ue_ifp; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = uether_start; + ifp->if_ioctl = axge_ioctl; + ifp->if_init = uether_init; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_TXCSUM | IFCAP_RXCSUM; + ifp->if_hwassist = AXGE_CSUM_FEATURES; + ifp->if_capenable = ifp->if_capabilities; + + mtx_lock(&Giant); + error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, + uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, + BMSR_DEFCAPMASK, AXGE_PHY_ADDR, MII_OFFSET_ANY, MIIF_DOPAUSE); + mtx_unlock(&Giant); + + return (error); +} + +/* + * Set media options. + */ +static int +axge_ifmedia_upd(struct ifnet *ifp) +{ + struct axge_softc *sc; + struct mii_data *mii; + struct mii_softc *miisc; + int error; + + sc = ifp->if_softc; + mii = GET_MII(sc); + AXGE_LOCK_ASSERT(sc, MA_OWNED); + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + + return (error); +} + +/* + * Report current media status. + */ +static void +axge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct axge_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = GET_MII(sc); + AXGE_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + AXGE_UNLOCK(sc); +} + +/* + * Probe for a AX88179 chip. + */ +static int +axge_probe(device_t dev) +{ + struct usb_attach_arg *uaa; + + uaa = device_get_ivars(dev); + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != AXGE_CONFIG_IDX) + return (ENXIO); + if (uaa->info.bIfaceIndex != AXGE_IFACE_IDX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(axge_devs, sizeof(axge_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +axge_attach(device_t dev) +{ + struct usb_attach_arg *uaa; + struct axge_softc *sc; + struct usb_ether *ue; + uint8_t iface_index; + int error; + + uaa = device_get_ivars(dev); + sc = device_get_softc(dev); + ue = &sc->sc_ue; + + device_set_usb_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = AXGE_IFACE_IDX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, axge_config, AXGE_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed\n"); + mtx_destroy(&sc->sc_mtx); + return (ENXIO); + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &axge_ue_methods; + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + axge_detach(dev); + return (ENXIO); /* failure */ +} + +static int +axge_detach(device_t dev) +{ + struct axge_softc *sc; + struct usb_ether *ue; + uint16_t val; + + sc = device_get_softc(dev); + ue = &sc->sc_ue; + if (device_is_attached(dev)) { + AXGE_LOCK(sc); + /* + * XXX + * ether_ifdetach(9) should be called first. + */ + axge_stop(ue); + /* Force bulk-in to return a zero-length USB packet. */ + val = axge_read_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR); + val |= EPPRCR_BZ | EPPRCR_IPRL; + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_EPPRCR, val); + /* Change clock. */ + axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CLK_SELECT, 0); + /* Disable MAC. */ + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, 0); + AXGE_UNLOCK(sc); + } + usbd_transfer_unsetup(sc->sc_xfer, AXGE_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +axge_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct axge_softc *sc; + struct usb_ether *ue; + struct usb_page_cache *pc; + int actlen; + + sc = usbd_xfer_softc(xfer); + ue = &sc->sc_ue; + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + pc = usbd_xfer_get_frame(xfer, 0); + axge_rx_frame(ue, pc, actlen); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + break; + + default: + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +axge_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct axge_softc *sc; + struct ifnet *ifp; + struct usb_page_cache *pc; + struct mbuf *m; + struct axge_frame_txhdr txhdr; + int nframes, pos; + + sc = usbd_xfer_softc(xfer); + ifp = uether_getifp(&sc->sc_ue); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & AXGE_FLAG_LINK) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { + /* + * Don't send anything if there is no link or + * controller is busy. + */ + return; + } + + for (nframes = 0; nframes < AXGE_N_FRAMES && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES, + nframes); + pc = usbd_xfer_get_frame(xfer, nframes); + txhdr.mss = 0; + txhdr.len = htole32(AXGE_TXBYTES(m->m_pkthdr.len)); + if ((ifp->if_capenable & IFCAP_TXCSUM) != 0 && + (m->m_pkthdr.csum_flags & AXGE_CSUM_FEATURES) == 0) + txhdr.len |= htole32(AXGE_CSUM_DISABLE); + + pos = 0; + usbd_copy_in(pc, pos, &txhdr, sizeof(txhdr)); + pos += sizeof(txhdr); + usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len); + pos += m->m_pkthdr.len; + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + /* Set frame length. */ + usbd_xfer_set_frame_len(xfer, nframes, pos); + } + if (nframes != 0) { + /* + * XXX + * Update TX packet counter here. This is not + * correct way but it seems that there is no way + * to know how many packets are sent at the end + * of transfer because controller combines + * multiple writes into single one if there is + * room in TX buffer of controller. + */ + if_inc_counter(ifp, IFCOUNTER_OPACKETS, nframes); + usbd_xfer_set_frames(xfer, nframes); + usbd_transfer_submit(xfer); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + return; + /* NOTREACHED */ + default: + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + + } +} + +static void +axge_tick(struct usb_ether *ue) +{ + struct axge_softc *sc; + struct mii_data *mii; + + sc = uether_getsc(ue); + mii = GET_MII(sc); + AXGE_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); +} + +static void +axge_rxfilter(struct usb_ether *ue) +{ + struct axge_softc *sc; + struct ifnet *ifp; + struct ifmultiaddr *ifma; + uint32_t h; + uint16_t rxmode; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + sc = uether_getsc(ue); + ifp = uether_getifp(ue); + h = 0; + AXGE_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Configure RX settings. + * Don't set RCR_IPE(IP header alignment on 32bit boundary) to disable + * inserting extra padding bytes. This wastes ethernet to USB host + * bandwidth as well as complicating RX handling logic. Current USB + * framework requires copying RX frames to mbufs so there is no need + * to worry about alignment. + */ + rxmode = RCR_DROP_CRCERR | RCR_START; + if (ifp->if_flags & IFF_BROADCAST) + rxmode |= RCR_ACPT_BCAST; + if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { + if (ifp->if_flags & IFF_PROMISC) + rxmode |= RCR_PROMISC; + rxmode |= RCR_ACPT_ALL_MCAST; + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, rxmode); + return; + } + + rxmode |= RCR_ACPT_MCAST; + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + hashtbl[h / 8] |= 1 << (h % 8); + } + if_maddr_runlock(ifp); + + axge_write_mem(sc, AXGE_ACCESS_MAC, 8, AXGE_MFA, (void *)&hashtbl, 8); + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_RCR, rxmode); +} + +static void +axge_start(struct usb_ether *ue) +{ + struct axge_softc *sc; + + sc = uether_getsc(ue); + /* + * Start the USB transfers, if not already started. + */ + usbd_transfer_start(sc->sc_xfer[AXGE_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[AXGE_BULK_DT_WR]); +} + +static void +axge_init(struct usb_ether *ue) +{ + struct axge_softc *sc; + struct ifnet *ifp; + + sc = uether_getsc(ue); + ifp = uether_getifp(ue); + AXGE_LOCK_ASSERT(sc, MA_OWNED); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + axge_stop(ue); + + axge_reset(sc); + + /* Set MAC address. */ + axge_write_mem(sc, AXGE_ACCESS_MAC, ETHER_ADDR_LEN, AXGE_NIDR, + IF_LLADDR(ifp), ETHER_ADDR_LEN); + + axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PWLLR, 0x34); + axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_PWLHR, 0x52); + + /* Configure TX/RX checksum offloading. */ + axge_csum_cfg(ue); + + /* Configure RX filters. */ + axge_rxfilter(ue); + + /* + * XXX + * Controller supports wakeup on link change detection, + * magic packet and wakeup frame recpetion. But it seems + * there is no framework for USB ethernet suspend/wakeup. + * Disable all wakeup functions. + */ + axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_MMSR, 0); + (void)axge_read_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_MMSR); + + /* Configure default medium type. */ + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, MSR_GM | MSR_FD | + MSR_RFC | MSR_TFC | MSR_RE); + + usbd_xfer_set_stall(sc->sc_xfer[AXGE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + /* Switch to selected media. */ + axge_ifmedia_upd(ifp); +} + +static void +axge_stop(struct usb_ether *ue) +{ + struct axge_softc *sc; + struct ifnet *ifp; + uint16_t val; + + sc = uether_getsc(ue); + ifp = uether_getifp(ue); + + AXGE_LOCK_ASSERT(sc, MA_OWNED); + + val = axge_read_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR); + val &= ~MSR_RE; + axge_write_cmd_2(sc, AXGE_ACCESS_MAC, 2, AXGE_MSR, val); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->sc_flags &= ~AXGE_FLAG_LINK; + + /* + * Stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[AXGE_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[AXGE_BULK_DT_RD]); +} + +static int +axge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct usb_ether *ue; + struct axge_softc *sc; + struct ifreq *ifr; + int error, mask, reinit; + + ue = ifp->if_softc; + sc = uether_getsc(ue); + ifr = (struct ifreq *)data; + error = 0; + reinit = 0; + if (cmd == SIOCSIFCAP) { + AXGE_LOCK(sc); + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if ((mask & IFCAP_TXCSUM) != 0 && + (ifp->if_capabilities & IFCAP_TXCSUM) != 0) { + ifp->if_capenable ^= IFCAP_TXCSUM; + if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) + ifp->if_hwassist |= AXGE_CSUM_FEATURES; + else + ifp->if_hwassist &= ~AXGE_CSUM_FEATURES; + reinit++; + } + if ((mask & IFCAP_RXCSUM) != 0 && + (ifp->if_capabilities & IFCAP_RXCSUM) != 0) { + ifp->if_capenable ^= IFCAP_RXCSUM; + reinit++; + } + if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING) + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + else + reinit = 0; + AXGE_UNLOCK(sc); + if (reinit > 0) + uether_init(ue); + } else + error = uether_ioctl(ifp, cmd, data); + + return (error); +} + +static void +axge_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen) +{ + struct axge_frame_rxhdr pkt_hdr; + uint32_t rxhdr; + uint32_t pos; + uint32_t pkt_cnt, pkt_end; + uint32_t hdr_off; + uint32_t pktlen; + + /* verify we have enough data */ + if (actlen < (int)sizeof(rxhdr)) + return; + + pos = 0; + + usbd_copy_out(pc, actlen - sizeof(rxhdr), &rxhdr, sizeof(rxhdr)); + rxhdr = le32toh(rxhdr); + + pkt_cnt = rxhdr & 0xFFFF; + hdr_off = pkt_end = (rxhdr >> 16) & 0xFFFF; + + /* + * <----------------------- actlen ------------------------> + * [frame #0]...[frame #N][pkt_hdr #0]...[pkt_hdr #N][rxhdr] + * Each RX frame would be aligned on 8 bytes boundary. If + * RCR_IPE bit is set in AXGE_RCR register, there would be 2 + * padding bytes and 6 dummy bytes(as the padding also should + * be aligned on 8 bytes boundary) for each RX frame to align + * IP header on 32bits boundary. Driver don't set RCR_IPE bit + * of AXGE_RCR register, so there should be no padding bytes + * which simplifies RX logic a lot. + */ + while (pkt_cnt--) { + /* verify the header offset */ + if ((int)(hdr_off + sizeof(pkt_hdr)) > actlen) { + DPRINTF("End of packet headers\n"); + break; + } + usbd_copy_out(pc, hdr_off, &pkt_hdr, sizeof(pkt_hdr)); + pkt_hdr.status = le32toh(pkt_hdr.status); + pktlen = AXGE_RXBYTES(pkt_hdr.status); + if (pos + pktlen > pkt_end) { + DPRINTF("Data position reached end\n"); + break; + } + + if (AXGE_RX_ERR(pkt_hdr.status) != 0) { + DPRINTF("Dropped a packet\n"); + if_inc_counter(ue->ue_ifp, IFCOUNTER_IERRORS, 1); + } else + axge_rxeof(ue, pc, pos, pktlen, pkt_hdr.status); + pos += (pktlen + 7) & ~7; + hdr_off += sizeof(pkt_hdr); + } +} + +static void +axge_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned int offset, + unsigned int len, uint32_t status) +{ + struct ifnet *ifp; + struct mbuf *m; + + ifp = ue->ue_ifp; + if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return; + } + + if (len > MHLEN - ETHER_ALIGN) + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + else + m = m_gethdr(M_NOWAIT, MT_DATA); + if (m == NULL) { + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + return; + } + m->m_pkthdr.rcvif = ifp; + m->m_len = m->m_pkthdr.len = len; + m->m_data += ETHER_ALIGN; + + usbd_copy_out(pc, offset, mtod(m, uint8_t *), len); + + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + if ((status & AXGE_RX_L3_CSUM_ERR) == 0 && + (status & AXGE_RX_L3_TYPE_MASK) == AXGE_RX_L3_TYPE_IPV4) + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | + CSUM_IP_VALID; + if ((status & AXGE_RX_L4_CSUM_ERR) == 0 && + ((status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_UDP || + (status & AXGE_RX_L4_TYPE_MASK) == AXGE_RX_L4_TYPE_TCP)) { + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | + CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + + _IF_ENQUEUE(&ue->ue_rxq, m); +} + +static void +axge_csum_cfg(struct usb_ether *ue) +{ + struct axge_softc *sc; + struct ifnet *ifp; + uint8_t csum; + + sc = uether_getsc(ue); + AXGE_LOCK_ASSERT(sc, MA_OWNED); + ifp = uether_getifp(ue); + + csum = 0; + if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) + csum |= CTCR_IP | CTCR_TCP | CTCR_UDP; + axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CTCR, csum); + + csum = 0; + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) + csum |= CRCR_IP | CRCR_TCP | CRCR_UDP; + axge_write_cmd_1(sc, AXGE_ACCESS_MAC, AXGE_CRCR, csum); +} diff --git a/freebsd/sys/dev/usb/net/if_axgereg.h b/freebsd/sys/dev/usb/net/if_axgereg.h new file mode 100644 index 00000000..c073610f --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_axgereg.h @@ -0,0 +1,209 @@ +/*- + * Copyright (c) 2013-2014 Kevin Lo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define AXGE_ACCESS_MAC 0x01 +#define AXGE_ACCESS_PHY 0x02 +#define AXGE_ACCESS_WAKEUP 0x03 +#define AXGE_ACCESS_EEPROM 0x04 +#define AXGE_ACCESS_EFUSE 0x05 +#define AXGE_RELOAD_EEPROM_EFUSE 0x06 +#define AXGE_WRITE_EFUSE_EN 0x09 +#define AXGE_WRITE_EFUSE_DIS 0x0A +#define AXGE_ACCESS_MFAB 0x10 + +/* Physical link status register */ +#define AXGE_PLSR 0x02 +#define PLSR_USB_FS 0x01 +#define PLSR_USB_HS 0x02 +#define PLSR_USB_SS 0x04 + +/* EEPROM address register */ +#define AXGE_EAR 0x07 + +/* EEPROM data low register */ +#define AXGE_EDLR 0x08 + +/* EEPROM data high register */ +#define AXGE_EDHR 0x09 + +/* EEPROM command register */ +#define AXGE_ECR 0x0a + +/* Rx control register */ +#define AXGE_RCR 0x0b +#define RCR_STOP 0x0000 +#define RCR_PROMISC 0x0001 +#define RCR_ACPT_ALL_MCAST 0x0002 +#define RCR_AUTOPAD_BNDRY 0x0004 +#define RCR_ACPT_BCAST 0x0008 +#define RCR_ACPT_MCAST 0x0010 +#define RCR_ACPT_PHY_MCAST 0x0020 +#define RCR_START 0x0080 +#define RCR_DROP_CRCERR 0x0100 +#define RCR_IPE 0x0200 +#define RCR_TX_CRC_PAD 0x0400 + +/* Node id register */ +#define AXGE_NIDR 0x10 + +/* Multicast filter array */ +#define AXGE_MFA 0x16 + +/* Medium status register */ +#define AXGE_MSR 0x22 +#define MSR_GM 0x0001 +#define MSR_FD 0x0002 +#define MSR_EN_125MHZ 0x0008 +#define MSR_RFC 0x0010 +#define MSR_TFC 0x0020 +#define MSR_RE 0x0100 +#define MSR_PS 0x0200 + +/* Monitor mode status register */ +#define AXGE_MMSR 0x24 +#define MMSR_RWLC 0x02 +#define MMSR_RWMP 0x04 +#define MMSR_RWWF 0x08 +#define MMSR_RW_FLAG 0x10 +#define MMSR_PME_POL 0x20 +#define MMSR_PME_TYPE 0x40 +#define MMSR_PME_IND 0x80 + +/* GPIO control/status register */ +#define AXGE_GPIOCR 0x25 + +/* Ethernet PHY power & reset control register */ +#define AXGE_EPPRCR 0x26 +#define EPPRCR_BZ 0x0010 +#define EPPRCR_IPRL 0x0020 +#define EPPRCR_AUTODETACH 0x1000 + +#define AXGE_RX_BULKIN_QCTRL 0x2e + +#define AXGE_CLK_SELECT 0x33 +#define AXGE_CLK_SELECT_BCS 0x01 +#define AXGE_CLK_SELECT_ACS 0x02 +#define AXGE_CLK_SELECT_ACSREQ 0x10 +#define AXGE_CLK_SELECT_ULR 0x08 + +/* COE Rx control register */ +#define AXGE_CRCR 0x34 +#define CRCR_IP 0x01 +#define CRCR_TCP 0x02 +#define CRCR_UDP 0x04 +#define CRCR_ICMP 0x08 +#define CRCR_IGMP 0x10 +#define CRCR_TCPV6 0x20 +#define CRCR_UDPV6 0x40 +#define CRCR_ICMPV6 0x80 + +/* COE Tx control register */ +#define AXGE_CTCR 0x35 +#define CTCR_IP 0x01 +#define CTCR_TCP 0x02 +#define CTCR_UDP 0x04 +#define CTCR_ICMP 0x08 +#define CTCR_IGMP 0x10 +#define CTCR_TCPV6 0x20 +#define CTCR_UDPV6 0x40 +#define CTCR_ICMPV6 0x80 + +/* Pause water level high register */ +#define AXGE_PWLHR 0x54 + +/* Pause water level low register */ +#define AXGE_PWLLR 0x55 + +#define AXGE_CONFIG_IDX 0 /* config number 1 */ +#define AXGE_IFACE_IDX 0 + +#define GET_MII(sc) uether_getmii(&(sc)->sc_ue) + +/* The interrupt endpoint is currently unused by the ASIX part. */ +enum { + AXGE_BULK_DT_WR, + AXGE_BULK_DT_RD, + AXGE_N_TRANSFER, +}; + +#define AXGE_N_FRAMES 16 + +struct axge_frame_txhdr { + uint32_t len; +#define AXGE_TXLEN_MASK 0x0001FFFF +#define AXGE_VLAN_INSERT 0x20000000 +#define AXGE_CSUM_DISABLE 0x80000000 + uint32_t mss; +#define AXGE_MSS_MASK 0x00003FFF +#define AXGE_PADDING 0x80008000 +#define AXGE_VLAN_TAG_MASK 0xFFFF0000 +} __packed; + +#define AXGE_TXBYTES(x) ((x) & AXGE_TXLEN_MASK) + +#define AXGE_PHY_ADDR 3 + +struct axge_frame_rxhdr { + uint32_t status; +#define AXGE_RX_L4_CSUM_ERR 0x00000001 +#define AXGE_RX_L3_CSUM_ERR 0x00000002 +#define AXGE_RX_L4_TYPE_UDP 0x00000004 +#define AXGE_RX_L4_TYPE_ICMP 0x00000008 +#define AXGE_RX_L4_TYPE_IGMP 0x0000000C +#define AXGE_RX_L4_TYPE_TCP 0x00000010 +#define AXGE_RX_L4_TYPE_MASK 0x0000001C +#define AXGE_RX_L3_TYPE_IPV4 0x00000020 +#define AXGE_RX_L3_TYPE_IPV6 0x00000040 +#define AXGE_RX_L3_TYPE_MASK 0x00000060 +#define AXGE_RX_VLAN_IND_MASK 0x00000700 +#define AXGE_RX_GOOD_PKT 0x00000800 +#define AXGE_RX_VLAN_PRI_MASK 0x00007000 +#define AXGE_RX_MBCAST 0x00008000 +#define AXGE_RX_LEN_MASK 0x1FFF0000 +#define AXGE_RX_CRC_ERR 0x20000000 +#define AXGE_RX_MII_ERR 0x40000000 +#define AXGE_RX_DROP_PKT 0x80000000 +#define AXGE_RX_LEN_SHIFT 16 +} __packed; + +#define AXGE_RXBYTES(x) (((x) & AXGE_RX_LEN_MASK) >> AXGE_RX_LEN_SHIFT) +#define AXGE_RX_ERR(x) \ + ((x) & (AXGE_RX_CRC_ERR | AXGE_RX_MII_ERR | AXGE_RX_DROP_PKT)) + +struct axge_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[AXGE_N_TRANSFER]; + + int sc_flags; +#define AXGE_FLAG_LINK 0x0001 /* got a link */ +}; + +#define AXGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define AXGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define AXGE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/if_cdce.c b/freebsd/sys/dev/usb/net/if_cdce.c new file mode 100644 index 00000000..ecab60ea --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_cdce.c @@ -0,0 +1,1583 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com> + * Copyright (c) 2003-2005 Craig Boston + * Copyright (c) 2004 Daniel Hartmeier + * Copyright (c) 2009 Hans Petter Selasky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul, THE VOICES IN HIS HEAD OR + * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * USB Communication Device Class (Ethernet Networking Control Model) + * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf + */ + +/* + * USB Network Control Model (NCM) + * http://www.usb.org/developers/devclass_docs/NCM10.zip + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usb_cdc.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR cdce_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_msctest.h> +#include <rtems/bsd/local/usb_if.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_cdcereg.h> + +static device_probe_t cdce_probe; +static device_attach_t cdce_attach; +static device_detach_t cdce_detach; +static device_suspend_t cdce_suspend; +static device_resume_t cdce_resume; +static usb_handle_request_t cdce_handle_request; + +static usb_callback_t cdce_bulk_write_callback; +static usb_callback_t cdce_bulk_read_callback; +static usb_callback_t cdce_intr_read_callback; +static usb_callback_t cdce_intr_write_callback; + +#if CDCE_HAVE_NCM +static usb_callback_t cdce_ncm_bulk_write_callback; +static usb_callback_t cdce_ncm_bulk_read_callback; +#endif + +static uether_fn_t cdce_attach_post; +static uether_fn_t cdce_init; +static uether_fn_t cdce_stop; +static uether_fn_t cdce_start; +static uether_fn_t cdce_setmulti; +static uether_fn_t cdce_setpromisc; + +static uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t); + +#ifdef USB_DEBUG +static int cdce_debug = 0; +static int cdce_tx_interval = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet"); +SYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RWTUN, &cdce_debug, 0, + "Debug level"); +SYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RWTUN, &cdce_tx_interval, 0, + "NCM transmit interval in ms"); +#endif + +static const struct usb_config cdce_config[CDCE_N_TRANSFER] = { + + [CDCE_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .if_index = 0, + .frames = CDCE_FRAMES_MAX, + .bufsize = (CDCE_FRAMES_MAX * MCLBYTES), + .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, + .callback = cdce_bulk_read_callback, + .timeout = 0, /* no timeout */ + .usb_mode = USB_MODE_DUAL, /* both modes */ + }, + + [CDCE_BULK_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .if_index = 0, + .frames = CDCE_FRAMES_MAX, + .bufsize = (CDCE_FRAMES_MAX * MCLBYTES), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, + .callback = cdce_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + .usb_mode = USB_MODE_DUAL, /* both modes */ + }, + + [CDCE_INTR_RX] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .if_index = 1, + .bufsize = CDCE_IND_SIZE_MAX, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, + .callback = cdce_intr_read_callback, + .timeout = 0, + .usb_mode = USB_MODE_HOST, + }, + + [CDCE_INTR_TX] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .if_index = 1, + .bufsize = CDCE_IND_SIZE_MAX, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, + .callback = cdce_intr_write_callback, + .timeout = 10000, /* 10 seconds */ + .usb_mode = USB_MODE_DEVICE, + }, +}; + +#if CDCE_HAVE_NCM +static const struct usb_config cdce_ncm_config[CDCE_N_TRANSFER] = { + + [CDCE_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .if_index = 0, + .frames = CDCE_NCM_RX_FRAMES_MAX, + .bufsize = (CDCE_NCM_RX_FRAMES_MAX * CDCE_NCM_RX_MAXLEN), + .flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,}, + .callback = cdce_ncm_bulk_read_callback, + .timeout = 0, /* no timeout */ + .usb_mode = USB_MODE_DUAL, /* both modes */ + }, + + [CDCE_BULK_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .if_index = 0, + .frames = CDCE_NCM_TX_FRAMES_MAX, + .bufsize = (CDCE_NCM_TX_FRAMES_MAX * CDCE_NCM_TX_MAXLEN), + .flags = {.pipe_bof = 1,}, + .callback = cdce_ncm_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + .usb_mode = USB_MODE_DUAL, /* both modes */ + }, + + [CDCE_INTR_RX] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .if_index = 1, + .bufsize = CDCE_IND_SIZE_MAX, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, + .callback = cdce_intr_read_callback, + .timeout = 0, + .usb_mode = USB_MODE_HOST, + }, + + [CDCE_INTR_TX] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .if_index = 1, + .bufsize = CDCE_IND_SIZE_MAX, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, + .callback = cdce_intr_write_callback, + .timeout = 10000, /* 10 seconds */ + .usb_mode = USB_MODE_DEVICE, + }, +}; +#endif + +static device_method_t cdce_methods[] = { + /* USB interface */ + DEVMETHOD(usb_handle_request, cdce_handle_request), + + /* Device interface */ + DEVMETHOD(device_probe, cdce_probe), + DEVMETHOD(device_attach, cdce_attach), + DEVMETHOD(device_detach, cdce_detach), + DEVMETHOD(device_suspend, cdce_suspend), + DEVMETHOD(device_resume, cdce_resume), + + DEVMETHOD_END +}; + +static driver_t cdce_driver = { + .name = "cdce", + .methods = cdce_methods, + .size = sizeof(struct cdce_softc), +}; + +static devclass_t cdce_devclass; +static eventhandler_tag cdce_etag; + +static int cdce_driver_loaded(struct module *, int, void *); + +static const STRUCT_USB_HOST_ID cdce_switch_devs[] = { + {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E3272_INIT, MSC_EJECT_HUAWEI2)}, +}; + +static const STRUCT_USB_HOST_ID cdce_host_devs[] = { + {USB_VPI(USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, CDCE_FLAG_ZAURUS)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)}, + + {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), + USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x16), + USB_DRIVER_INFO(0)}, + {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), + USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x46), + USB_DRIVER_INFO(0)}, + {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), + USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x76), + USB_DRIVER_INFO(0)}, +}; + +static const STRUCT_USB_DUAL_ID cdce_dual_devs[] = { + {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)}, + {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)}, + {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_NETWORK_CONTROL_MODEL, 0)}, +}; + +DRIVER_MODULE(cdce, uhub, cdce_driver, cdce_devclass, cdce_driver_loaded, 0); +MODULE_VERSION(cdce, 1); +MODULE_DEPEND(cdce, uether, 1, 1, 1); +MODULE_DEPEND(cdce, usb, 1, 1, 1); +MODULE_DEPEND(cdce, ether, 1, 1, 1); +USB_PNP_DEVICE_INFO(cdce_switch_devs); +USB_PNP_HOST_INFO(cdce_host_devs); +USB_PNP_DUAL_INFO(cdce_dual_devs); + +static const struct usb_ether_methods cdce_ue_methods = { + .ue_attach_post = cdce_attach_post, + .ue_start = cdce_start, + .ue_init = cdce_init, + .ue_stop = cdce_stop, + .ue_setmulti = cdce_setmulti, + .ue_setpromisc = cdce_setpromisc, +}; + +#if CDCE_HAVE_NCM +/*------------------------------------------------------------------------* + * cdce_ncm_init + * + * Return values: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +cdce_ncm_init(struct cdce_softc *sc) +{ + struct usb_ncm_parameters temp; + struct usb_device_request req; + struct usb_ncm_func_descriptor *ufd; + uint8_t value[8]; + int err; + + ufd = usbd_find_descriptor(sc->sc_ue.ue_udev, NULL, + sc->sc_ifaces_index[1], UDESC_CS_INTERFACE, 0xFF, + UCDC_NCM_FUNC_DESC_SUBTYPE, 0xFF); + + /* verify length of NCM functional descriptor */ + if (ufd != NULL) { + if (ufd->bLength < sizeof(*ufd)) + ufd = NULL; + else + DPRINTFN(1, "Found NCM functional descriptor.\n"); + } + + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = UCDC_NCM_GET_NTB_PARAMETERS; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wLength, sizeof(temp)); + + err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, + &temp, 0, NULL, 1000 /* ms */); + if (err) + return (1); + + /* Read correct set of parameters according to device mode */ + + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { + sc->sc_ncm.rx_max = UGETDW(temp.dwNtbInMaxSize); + sc->sc_ncm.tx_max = UGETDW(temp.dwNtbOutMaxSize); + sc->sc_ncm.tx_remainder = UGETW(temp.wNdpOutPayloadRemainder); + sc->sc_ncm.tx_modulus = UGETW(temp.wNdpOutDivisor); + sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpOutAlignment); + sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams); + } else { + sc->sc_ncm.rx_max = UGETDW(temp.dwNtbOutMaxSize); + sc->sc_ncm.tx_max = UGETDW(temp.dwNtbInMaxSize); + sc->sc_ncm.tx_remainder = UGETW(temp.wNdpInPayloadRemainder); + sc->sc_ncm.tx_modulus = UGETW(temp.wNdpInDivisor); + sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpInAlignment); + sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams); + } + + /* Verify maximum receive length */ + + if ((sc->sc_ncm.rx_max < 32) || + (sc->sc_ncm.rx_max > CDCE_NCM_RX_MAXLEN)) { + DPRINTFN(1, "Using default maximum receive length\n"); + sc->sc_ncm.rx_max = CDCE_NCM_RX_MAXLEN; + } + + /* Verify maximum transmit length */ + + if ((sc->sc_ncm.tx_max < 32) || + (sc->sc_ncm.tx_max > CDCE_NCM_TX_MAXLEN)) { + DPRINTFN(1, "Using default maximum transmit length\n"); + sc->sc_ncm.tx_max = CDCE_NCM_TX_MAXLEN; + } + + /* + * Verify that the structure alignment is: + * - power of two + * - not greater than the maximum transmit length + * - not less than four bytes + */ + if ((sc->sc_ncm.tx_struct_align < 4) || + (sc->sc_ncm.tx_struct_align != + ((-sc->sc_ncm.tx_struct_align) & sc->sc_ncm.tx_struct_align)) || + (sc->sc_ncm.tx_struct_align >= sc->sc_ncm.tx_max)) { + DPRINTFN(1, "Using default other alignment: 4 bytes\n"); + sc->sc_ncm.tx_struct_align = 4; + } + + /* + * Verify that the payload alignment is: + * - power of two + * - not greater than the maximum transmit length + * - not less than four bytes + */ + if ((sc->sc_ncm.tx_modulus < 4) || + (sc->sc_ncm.tx_modulus != + ((-sc->sc_ncm.tx_modulus) & sc->sc_ncm.tx_modulus)) || + (sc->sc_ncm.tx_modulus >= sc->sc_ncm.tx_max)) { + DPRINTFN(1, "Using default transmit modulus: 4 bytes\n"); + sc->sc_ncm.tx_modulus = 4; + } + + /* Verify that the payload remainder */ + + if ((sc->sc_ncm.tx_remainder >= sc->sc_ncm.tx_modulus)) { + DPRINTFN(1, "Using default transmit remainder: 0 bytes\n"); + sc->sc_ncm.tx_remainder = 0; + } + + /* + * Offset the TX remainder so that IP packet payload starts at + * the tx_modulus. This is not too clear in the specification. + */ + + sc->sc_ncm.tx_remainder = + (sc->sc_ncm.tx_remainder - ETHER_HDR_LEN) & + (sc->sc_ncm.tx_modulus - 1); + + /* Verify max datagrams */ + + if (sc->sc_ncm.tx_nframe == 0 || + sc->sc_ncm.tx_nframe > (CDCE_NCM_SUBFRAMES_MAX - 1)) { + DPRINTFN(1, "Using default max " + "subframes: %u units\n", CDCE_NCM_SUBFRAMES_MAX - 1); + /* need to reserve one entry for zero padding */ + sc->sc_ncm.tx_nframe = (CDCE_NCM_SUBFRAMES_MAX - 1); + } + + /* Additional configuration, will fail in device side mode, which is OK. */ + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_NCM_SET_NTB_INPUT_SIZE; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + + if (ufd != NULL && + (ufd->bmNetworkCapabilities & UCDC_NCM_CAP_MAX_DGRAM)) { + USETW(req.wLength, 8); + USETDW(value, sc->sc_ncm.rx_max); + USETW(value + 4, (CDCE_NCM_SUBFRAMES_MAX - 1)); + USETW(value + 6, 0); + } else { + USETW(req.wLength, 4); + USETDW(value, sc->sc_ncm.rx_max); + } + + err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, + &value, 0, NULL, 1000 /* ms */); + if (err) { + DPRINTFN(1, "Setting input size " + "to %u failed.\n", sc->sc_ncm.rx_max); + } + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_NCM_SET_CRC_MODE; + USETW(req.wValue, 0); /* no CRC */ + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, + NULL, 0, NULL, 1000 /* ms */); + if (err) { + DPRINTFN(1, "Setting CRC mode to off failed.\n"); + } + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_NCM_SET_NTB_FORMAT; + USETW(req.wValue, 0); /* NTB-16 */ + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req, + NULL, 0, NULL, 1000 /* ms */); + if (err) { + DPRINTFN(1, "Setting NTB format to 16-bit failed.\n"); + } + + return (0); /* success */ +} +#endif + +static void +cdce_test_autoinst(void *arg, struct usb_device *udev, + struct usb_attach_arg *uaa) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *id; + + if (uaa->dev_state != UAA_DEV_READY) + return; + + iface = usbd_get_iface(udev, 0); + if (iface == NULL) + return; + id = iface->idesc; + if (id == NULL || id->bInterfaceClass != UICLASS_MASS) + return; + if (usbd_lookup_id_by_uaa(cdce_switch_devs, sizeof(cdce_switch_devs), uaa)) + return; /* no device match */ + + if (usb_msc_eject(udev, 0, USB_GET_DRIVER_INFO(uaa)) == 0) { + /* success, mark the udev as disappearing */ + uaa->dev_state = UAA_DEV_EJECTING; + } +} + +static int +cdce_driver_loaded(struct module *mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + /* register our autoinstall handler */ + cdce_etag = EVENTHANDLER_REGISTER(usb_dev_configured, + cdce_test_autoinst, NULL, EVENTHANDLER_PRI_ANY); + return (0); + case MOD_UNLOAD: + EVENTHANDLER_DEREGISTER(usb_dev_configured, cdce_etag); + return (0); + default: + return (EOPNOTSUPP); + } +} + +static int +cdce_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + int error; + + error = usbd_lookup_id_by_uaa(cdce_host_devs, sizeof(cdce_host_devs), uaa); + if (error) + error = usbd_lookup_id_by_uaa(cdce_dual_devs, sizeof(cdce_dual_devs), uaa); + return (error); +} + +static void +cdce_attach_post(struct usb_ether *ue) +{ + /* no-op */ + return; +} + +static int +cdce_attach(device_t dev) +{ + struct cdce_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct usb_interface *iface; + const struct usb_cdc_union_descriptor *ud; + const struct usb_interface_descriptor *id; + const struct usb_cdc_ethernet_descriptor *ued; + const struct usb_config *pcfg; + uint32_t seed; + int error; + uint8_t i; + uint8_t data_iface_no; + char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */ + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + sc->sc_ue.ue_udev = uaa->device; + + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + ud = usbd_find_descriptor + (uaa->device, NULL, uaa->info.bIfaceIndex, + UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_UNION, 0xFF); + + if ((ud == NULL) || (ud->bLength < sizeof(*ud)) || + (sc->sc_flags & CDCE_FLAG_NO_UNION)) { + DPRINTFN(1, "No union descriptor!\n"); + sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex; + sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; + goto alloc_transfers; + } + data_iface_no = ud->bSlaveInterface[0]; + + for (i = 0;; i++) { + + iface = usbd_get_iface(uaa->device, i); + + if (iface) { + + id = usbd_get_interface_descriptor(iface); + + if (id && (id->bInterfaceNumber == data_iface_no)) { + sc->sc_ifaces_index[0] = i; + sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex; + usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); + break; + } + } else { + device_printf(dev, "no data interface found\n"); + goto detach; + } + } + + /* + * <quote> + * + * The Data Class interface of a networking device shall have + * a minimum of two interface settings. The first setting + * (the default interface setting) includes no endpoints and + * therefore no networking traffic is exchanged whenever the + * default interface setting is selected. One or more + * additional interface settings are used for normal + * operation, and therefore each includes a pair of endpoints + * (one IN, and one OUT) to exchange network traffic. Select + * an alternate interface setting to initialize the network + * aspects of the device and to enable the exchange of + * network traffic. + * + * </quote> + * + * Some devices, most notably cable modems, include interface + * settings that have no IN or OUT endpoint, therefore loop + * through the list of all available interface settings + * looking for one with both IN and OUT endpoints. + */ + +alloc_transfers: + + pcfg = cdce_config; /* Default Configuration */ + + for (i = 0; i != 32; i++) { + + error = usbd_set_alt_interface_index(uaa->device, + sc->sc_ifaces_index[0], i); + if (error) + break; +#if CDCE_HAVE_NCM + if ((i == 0) && (cdce_ncm_init(sc) == 0)) + pcfg = cdce_ncm_config; +#endif + error = usbd_transfer_setup(uaa->device, + sc->sc_ifaces_index, sc->sc_xfer, + pcfg, CDCE_N_TRANSFER, sc, &sc->sc_mtx); + + if (error == 0) + break; + } + + if (error || (i == 32)) { + device_printf(dev, "No valid alternate " + "setting found\n"); + goto detach; + } + + ued = usbd_find_descriptor + (uaa->device, NULL, uaa->info.bIfaceIndex, + UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_ENF, 0xFF); + + if ((ued == NULL) || (ued->bLength < sizeof(*ued))) { + error = USB_ERR_INVAL; + } else { + error = usbd_req_get_string_any(uaa->device, NULL, + eaddr_str, sizeof(eaddr_str), ued->iMacAddress); + } + + if (error) { + + /* fake MAC address */ + + device_printf(dev, "faking MAC address\n"); + seed = ticks; + sc->sc_ue.ue_eaddr[0] = 0x2a; + memcpy(&sc->sc_ue.ue_eaddr[1], &seed, sizeof(uint32_t)); + sc->sc_ue.ue_eaddr[5] = device_get_unit(dev); + + } else { + + memset(sc->sc_ue.ue_eaddr, 0, sizeof(sc->sc_ue.ue_eaddr)); + + for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) { + + char c = eaddr_str[i]; + + if ('0' <= c && c <= '9') + c -= '0'; + else if (c != 0) + c -= 'A' - 10; + else + break; + + c &= 0xf; + + if ((i & 1) == 0) + c <<= 4; + sc->sc_ue.ue_eaddr[i / 2] |= c; + } + + if (uaa->usb_mode == USB_MODE_DEVICE) { + /* + * Do not use the same MAC address like the peer ! + */ + sc->sc_ue.ue_eaddr[5] ^= 0xFF; + } + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &cdce_ue_methods; + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + cdce_detach(dev); + return (ENXIO); /* failure */ +} + +static int +cdce_detach(device_t dev) +{ + struct cdce_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + /* stop all USB transfers first */ + usbd_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +cdce_start(struct usb_ether *ue) +{ + struct cdce_softc *sc = uether_getsc(ue); + + /* + * Start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[CDCE_BULK_TX]); + usbd_transfer_start(sc->sc_xfer[CDCE_BULK_RX]); +} + +static void +cdce_free_queue(struct mbuf **ppm, uint8_t n) +{ + uint8_t x; + for (x = 0; x != n; x++) { + if (ppm[x] != NULL) { + m_freem(ppm[x]); + ppm[x] = NULL; + } + } +} + +static void +cdce_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct mbuf *m; + struct mbuf *mt; + uint32_t crc; + uint8_t x; + int actlen, aframes; + + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); + + DPRINTFN(1, "\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", + actlen, aframes); + + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* free all previous TX buffers */ + cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + for (x = 0; x != CDCE_FRAMES_MAX; x++) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + break; + + if (sc->sc_flags & CDCE_FLAG_ZAURUS) { + /* + * Zaurus wants a 32-bit CRC appended + * to every frame + */ + + crc = cdce_m_crc32(m, 0, m->m_pkthdr.len); + crc = htole32(crc); + + if (!m_append(m, 4, (void *)&crc)) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + continue; + } + } + if (m->m_len != m->m_pkthdr.len) { + mt = m_defrag(m, M_NOWAIT); + if (mt == NULL) { + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + continue; + } + m = mt; + } + if (m->m_pkthdr.len > MCLBYTES) { + m->m_pkthdr.len = MCLBYTES; + } + sc->sc_tx_buf[x] = m; + usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); + + /* + * If there's a BPF listener, bounce a copy of + * this frame to him: + */ + BPF_MTAP(ifp, m); + } + if (x != 0) { + usbd_xfer_set_frames(xfer, x); + + usbd_transfer_submit(xfer); + } + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + /* free all previous TX buffers */ + cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX); + + /* count output errors */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (error != USB_ERR_CANCELLED) { + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + } + goto tr_setup; + } + break; + } +} + +static int32_t +cdce_m_crc32_cb(void *arg, void *src, uint32_t count) +{ + uint32_t *p_crc = arg; + + *p_crc = crc32_raw(src, count, *p_crc); + return (0); +} + +static uint32_t +cdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len) +{ + uint32_t crc = 0xFFFFFFFF; + int error; + + error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc); + return (crc ^ 0xFFFFFFFF); +} + +static void +cdce_init(struct usb_ether *ue) +{ + struct cdce_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + CDCE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + /* start interrupt transfer */ + usbd_transfer_start(sc->sc_xfer[CDCE_INTR_RX]); + usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]); + + /* + * Stall data write direction, which depends on USB mode. + * + * Some USB host stacks (e.g. Mac OS X) don't clears stall + * bit as it should, so set it in our host mode only. + */ + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) + usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]); + + /* start data transfers */ + cdce_start(ue); +} + +static void +cdce_stop(struct usb_ether *ue) +{ + struct cdce_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + CDCE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_RX]); + usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_TX]); + usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_RX]); + usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_TX]); +} + +static void +cdce_setmulti(struct usb_ether *ue) +{ + /* no-op */ + return; +} + +static void +cdce_setpromisc(struct usb_ether *ue) +{ + /* no-op */ + return; +} + +static int +cdce_suspend(device_t dev) +{ + device_printf(dev, "Suspending\n"); + return (0); +} + +static int +cdce_resume(device_t dev) +{ + device_printf(dev, "Resuming\n"); + return (0); +} + +static void +cdce_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct mbuf *m; + uint8_t x; + int actlen; + int aframes; + int len; + + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("received %u bytes in %u frames\n", actlen, aframes); + + for (x = 0; x != aframes; x++) { + + m = sc->sc_rx_buf[x]; + sc->sc_rx_buf[x] = NULL; + len = usbd_xfer_frame_len(xfer, x); + + /* Strip off CRC added by Zaurus, if any */ + if ((sc->sc_flags & CDCE_FLAG_ZAURUS) && len >= 14) + len -= 4; + + if (len < (int)sizeof(struct ether_header)) { + m_freem(m); + continue; + } + /* queue up mbuf */ + uether_rxmbuf(&sc->sc_ue, m, len); + } + + /* FALLTHROUGH */ + case USB_ST_SETUP: + /* + * TODO: Implement support for multi frame transfers, + * when the USB hardware supports it. + */ + for (x = 0; x != 1; x++) { + if (sc->sc_rx_buf[x] == NULL) { + m = uether_newbuf(); + if (m == NULL) + goto tr_stall; + sc->sc_rx_buf[x] = m; + } else { + m = sc->sc_rx_buf[x]; + } + + usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); + } + /* set number of frames and start hardware */ + usbd_xfer_set_frames(xfer, x); + usbd_transfer_submit(xfer); + /* flush any received frames */ + uether_rxflush(&sc->sc_ue); + break; + + default: /* Error */ + DPRINTF("error = %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { +tr_stall: + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frames(xfer, 0); + usbd_transfer_submit(xfer); + } + break; + } + + /* need to free the RX-mbufs when we are cancelled */ + cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX); + break; + } +} + +static void +cdce_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("Received %d bytes\n", actlen); + + /* TODO: decode some indications */ + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* start clear stall */ + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +cdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct usb_cdc_notification req; + struct usb_page_cache *pc; + uint32_t speed; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("Transferred %d bytes\n", actlen); + + switch (sc->sc_notify_state) { + case CDCE_NOTIFY_NETWORK_CONNECTION: + sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE; + break; + case CDCE_NOTIFY_SPEED_CHANGE: + sc->sc_notify_state = CDCE_NOTIFY_DONE; + break; + default: + break; + } + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + /* + * Inform host about connection. Required according to USB CDC + * specification and communicating to Mac OS X USB host stack. + * Some of the values seems ignored by Mac OS X though. + */ + if (sc->sc_notify_state == CDCE_NOTIFY_NETWORK_CONNECTION) { + req.bmRequestType = UCDC_NOTIFICATION; + req.bNotification = UCDC_N_NETWORK_CONNECTION; + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wValue, 1); /* Connected */ + USETW(req.wLength, 0); + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &req, sizeof(req)); + usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); + usbd_xfer_set_frames(xfer, 1); + usbd_transfer_submit(xfer); + + } else if (sc->sc_notify_state == CDCE_NOTIFY_SPEED_CHANGE) { + req.bmRequestType = UCDC_NOTIFICATION; + req.bNotification = UCDC_N_CONNECTION_SPEED_CHANGE; + req.wIndex[0] = sc->sc_ifaces_index[1]; + req.wIndex[1] = 0; + USETW(req.wValue, 0); + USETW(req.wLength, 8); + + /* Peak theoretical bulk trasfer rate in bits/s */ + if (usbd_get_speed(sc->sc_ue.ue_udev) != USB_SPEED_FULL) + speed = (13 * 512 * 8 * 1000 * 8); + else + speed = (19 * 64 * 1 * 1000 * 8); + + USETDW(req.data + 0, speed); /* Upstream bit rate */ + USETDW(req.data + 4, speed); /* Downstream bit rate */ + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &req, sizeof(req)); + usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); + usbd_xfer_set_frames(xfer, 1); + usbd_transfer_submit(xfer); + } + break; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { + /* start clear stall */ + usbd_xfer_set_stall(xfer); + } + goto tr_setup; + } + break; + } +} + +static int +cdce_handle_request(device_t dev, + const void *preq, void **pptr, uint16_t *plen, + uint16_t offset, uint8_t *pstate) +{ + struct cdce_softc *sc = device_get_softc(dev); + const struct usb_device_request *req = preq; + uint8_t is_complete = *pstate; + + /* + * When Mac OS X resumes after suspending it expects + * to be notified again after this request. + */ + if (req->bmRequestType == UT_WRITE_CLASS_INTERFACE && \ + req->bRequest == UCDC_NCM_SET_ETHERNET_PACKET_FILTER) { + + if (is_complete == 1) { + mtx_lock(&sc->sc_mtx); + sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE; + usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]); + mtx_unlock(&sc->sc_mtx); + } + + return (0); + } + + return (ENXIO); /* use builtin handler */ +} + +#if CDCE_HAVE_NCM +static void +cdce_ncm_tx_zero(struct usb_page_cache *pc, + uint32_t start, uint32_t end) +{ + if (start >= CDCE_NCM_TX_MAXLEN) + return; + if (end > CDCE_NCM_TX_MAXLEN) + end = CDCE_NCM_TX_MAXLEN; + + usbd_frame_zero(pc, start, end - start); +} + +static uint8_t +cdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, index); + struct mbuf *m; + uint32_t rem; + uint32_t offset; + uint32_t last_offset; + uint16_t n; + uint8_t retval; + + usbd_xfer_set_frame_offset(xfer, index * CDCE_NCM_TX_MAXLEN, index); + + offset = sizeof(sc->sc_ncm.hdr) + + sizeof(sc->sc_ncm.dpt) + sizeof(sc->sc_ncm.dp); + + /* Store last valid offset before alignment */ + last_offset = offset; + + /* Align offset */ + offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder, + offset, sc->sc_ncm.tx_modulus); + + /* Zero pad */ + cdce_ncm_tx_zero(pc, last_offset, offset); + + /* buffer full */ + retval = 2; + + for (n = 0; n != sc->sc_ncm.tx_nframe; n++) { + + /* check if end of transmit buffer is reached */ + + if (offset >= sc->sc_ncm.tx_max) + break; + + /* compute maximum buffer size */ + + rem = sc->sc_ncm.tx_max - offset; + + IFQ_DRV_DEQUEUE(&(ifp->if_snd), m); + + if (m == NULL) { + /* buffer not full */ + retval = 1; + break; + } + + if (m->m_pkthdr.len > (int)rem) { + if (n == 0) { + /* The frame won't fit in our buffer */ + DPRINTFN(1, "Frame too big to be transmitted!\n"); + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + n--; + continue; + } + /* Wait till next buffer becomes ready */ + IFQ_DRV_PREPEND(&(ifp->if_snd), m); + break; + } + usbd_m_copy_in(pc, offset, m, 0, m->m_pkthdr.len); + + USETW(sc->sc_ncm.dp[n].wFrameLength, m->m_pkthdr.len); + USETW(sc->sc_ncm.dp[n].wFrameIndex, offset); + + /* Update offset */ + offset += m->m_pkthdr.len; + + /* Store last valid offset before alignment */ + last_offset = offset; + + /* Align offset */ + offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder, + offset, sc->sc_ncm.tx_modulus); + + /* Zero pad */ + cdce_ncm_tx_zero(pc, last_offset, offset); + + /* + * If there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + /* Free mbuf */ + + m_freem(m); + + /* Pre-increment interface counter */ + + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + } + + if (n == 0) + return (0); + + rem = (sizeof(sc->sc_ncm.dpt) + (4 * n) + 4); + + USETW(sc->sc_ncm.dpt.wLength, rem); + + /* zero the rest of the data pointer entries */ + for (; n != CDCE_NCM_SUBFRAMES_MAX; n++) { + USETW(sc->sc_ncm.dp[n].wFrameLength, 0); + USETW(sc->sc_ncm.dp[n].wFrameIndex, 0); + } + + offset = last_offset; + + /* Align offset */ + offset = CDCE_NCM_ALIGN(0, offset, CDCE_NCM_TX_MINLEN); + + /* Optimise, save bandwidth and force short termination */ + if (offset >= sc->sc_ncm.tx_max) + offset = sc->sc_ncm.tx_max; + else + offset ++; + + /* Zero pad */ + cdce_ncm_tx_zero(pc, last_offset, offset); + + /* set frame length */ + usbd_xfer_set_frame_len(xfer, index, offset); + + /* Fill out 16-bit header */ + sc->sc_ncm.hdr.dwSignature[0] = 'N'; + sc->sc_ncm.hdr.dwSignature[1] = 'C'; + sc->sc_ncm.hdr.dwSignature[2] = 'M'; + sc->sc_ncm.hdr.dwSignature[3] = 'H'; + USETW(sc->sc_ncm.hdr.wHeaderLength, sizeof(sc->sc_ncm.hdr)); + USETW(sc->sc_ncm.hdr.wBlockLength, offset); + USETW(sc->sc_ncm.hdr.wSequence, sc->sc_ncm.tx_seq); + USETW(sc->sc_ncm.hdr.wDptIndex, sizeof(sc->sc_ncm.hdr)); + + sc->sc_ncm.tx_seq++; + + /* Fill out 16-bit frame table header */ + sc->sc_ncm.dpt.dwSignature[0] = 'N'; + sc->sc_ncm.dpt.dwSignature[1] = 'C'; + sc->sc_ncm.dpt.dwSignature[2] = 'M'; + sc->sc_ncm.dpt.dwSignature[3] = '0'; + USETW(sc->sc_ncm.dpt.wNextNdpIndex, 0); /* reserved */ + + usbd_copy_in(pc, 0, &(sc->sc_ncm.hdr), sizeof(sc->sc_ncm.hdr)); + usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr), &(sc->sc_ncm.dpt), + sizeof(sc->sc_ncm.dpt)); + usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr) + sizeof(sc->sc_ncm.dpt), + &(sc->sc_ncm.dp), sizeof(sc->sc_ncm.dp)); + return (retval); +} + +static void +cdce_ncm_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + uint16_t x; + uint8_t temp; + int actlen; + int aframes; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); + + DPRINTFN(10, "transfer complete: " + "%u bytes in %u frames\n", actlen, aframes); + + case USB_ST_SETUP: + for (x = 0; x != CDCE_NCM_TX_FRAMES_MAX; x++) { + temp = cdce_ncm_fill_tx_frames(xfer, x); + if (temp == 0) + break; + if (temp == 1) { + x++; + break; + } + } + + if (x != 0) { +#ifdef USB_DEBUG + usbd_xfer_set_interval(xfer, cdce_tx_interval); +#endif + usbd_xfer_set_frames(xfer, x); + usbd_transfer_submit(xfer); + } + break; + + default: /* Error */ + DPRINTFN(10, "Transfer error: %s\n", + usbd_errstr(error)); + + /* update error counter */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (error != USB_ERR_CANCELLED) { + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frames(xfer, 0); + usbd_transfer_submit(xfer); + } + } + break; + } +} + +static void +cdce_ncm_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cdce_softc *sc = usbd_xfer_softc(xfer); + struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct mbuf *m; + int sumdata; + int sumlen; + int actlen; + int aframes; + int temp; + int nframes; + int x; + int offset; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + usbd_xfer_status(xfer, &actlen, &sumlen, &aframes, NULL); + + DPRINTFN(1, "received %u bytes in %u frames\n", + actlen, aframes); + + if (actlen < (int)(sizeof(sc->sc_ncm.hdr) + + sizeof(sc->sc_ncm.dpt))) { + DPRINTFN(1, "frame too short\n"); + goto tr_setup; + } + usbd_copy_out(pc, 0, &(sc->sc_ncm.hdr), + sizeof(sc->sc_ncm.hdr)); + + if ((sc->sc_ncm.hdr.dwSignature[0] != 'N') || + (sc->sc_ncm.hdr.dwSignature[1] != 'C') || + (sc->sc_ncm.hdr.dwSignature[2] != 'M') || + (sc->sc_ncm.hdr.dwSignature[3] != 'H')) { + DPRINTFN(1, "invalid HDR signature: " + "0x%02x:0x%02x:0x%02x:0x%02x\n", + sc->sc_ncm.hdr.dwSignature[0], + sc->sc_ncm.hdr.dwSignature[1], + sc->sc_ncm.hdr.dwSignature[2], + sc->sc_ncm.hdr.dwSignature[3]); + goto tr_stall; + } + temp = UGETW(sc->sc_ncm.hdr.wBlockLength); + if (temp > sumlen) { + DPRINTFN(1, "unsupported block length %u/%u\n", + temp, sumlen); + goto tr_stall; + } + temp = UGETW(sc->sc_ncm.hdr.wDptIndex); + if ((int)(temp + sizeof(sc->sc_ncm.dpt)) > actlen) { + DPRINTFN(1, "invalid DPT index: 0x%04x\n", temp); + goto tr_stall; + } + usbd_copy_out(pc, temp, &(sc->sc_ncm.dpt), + sizeof(sc->sc_ncm.dpt)); + + if ((sc->sc_ncm.dpt.dwSignature[0] != 'N') || + (sc->sc_ncm.dpt.dwSignature[1] != 'C') || + (sc->sc_ncm.dpt.dwSignature[2] != 'M') || + (sc->sc_ncm.dpt.dwSignature[3] != '0')) { + DPRINTFN(1, "invalid DPT signature" + "0x%02x:0x%02x:0x%02x:0x%02x\n", + sc->sc_ncm.dpt.dwSignature[0], + sc->sc_ncm.dpt.dwSignature[1], + sc->sc_ncm.dpt.dwSignature[2], + sc->sc_ncm.dpt.dwSignature[3]); + goto tr_stall; + } + nframes = UGETW(sc->sc_ncm.dpt.wLength) / 4; + + /* Subtract size of header and last zero padded entry */ + if (nframes >= (2 + 1)) + nframes -= (2 + 1); + else + nframes = 0; + + DPRINTFN(1, "nframes = %u\n", nframes); + + temp += sizeof(sc->sc_ncm.dpt); + + if ((temp + (4 * nframes)) > actlen) + goto tr_stall; + + if (nframes > CDCE_NCM_SUBFRAMES_MAX) { + DPRINTFN(1, "Truncating number of frames from %u to %u\n", + nframes, CDCE_NCM_SUBFRAMES_MAX); + nframes = CDCE_NCM_SUBFRAMES_MAX; + } + usbd_copy_out(pc, temp, &(sc->sc_ncm.dp), (4 * nframes)); + + sumdata = 0; + + for (x = 0; x != nframes; x++) { + + offset = UGETW(sc->sc_ncm.dp[x].wFrameIndex); + temp = UGETW(sc->sc_ncm.dp[x].wFrameLength); + + if ((offset == 0) || + (temp < (int)sizeof(struct ether_header)) || + (temp > (MCLBYTES - ETHER_ALIGN))) { + DPRINTFN(1, "NULL frame detected at %d\n", x); + m = NULL; + /* silently ignore this frame */ + continue; + } else if ((offset + temp) > actlen) { + DPRINTFN(1, "invalid frame " + "detected at %d\n", x); + m = NULL; + /* silently ignore this frame */ + continue; + } else if (temp > (int)(MHLEN - ETHER_ALIGN)) { + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + } else { + m = m_gethdr(M_NOWAIT, MT_DATA); + } + + DPRINTFN(16, "frame %u, offset = %u, length = %u \n", + x, offset, temp); + + /* check if we have a buffer */ + if (m) { + m->m_len = m->m_pkthdr.len = temp + ETHER_ALIGN; + m_adj(m, ETHER_ALIGN); + + usbd_copy_out(pc, offset, m->m_data, temp); + + /* enqueue */ + uether_rxmbuf(&sc->sc_ue, m, temp); + + sumdata += temp; + } else { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + } + } + + DPRINTFN(1, "Efficiency: %u/%u bytes\n", sumdata, actlen); + + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, sc->sc_ncm.rx_max); + usbd_xfer_set_frames(xfer, 1); + usbd_transfer_submit(xfer); + uether_rxflush(&sc->sc_ue); /* must be last */ + break; + + default: /* Error */ + DPRINTFN(1, "error = %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { +tr_stall: + if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frames(xfer, 0); + usbd_transfer_submit(xfer); + } + } + break; + } +} +#endif diff --git a/freebsd/sys/dev/usb/net/if_cdcereg.h b/freebsd/sys/dev/usb/net/if_cdcereg.h new file mode 100644 index 00000000..1bc2d603 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_cdcereg.h @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2003-2005 Craig Boston + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul, THE VOICES IN HIS HEAD OR + * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _USB_IF_CDCEREG_H_ +#define _USB_IF_CDCEREG_H_ + +#define CDCE_FRAMES_MAX 8 /* units */ +#define CDCE_IND_SIZE_MAX 32 /* bytes */ + +#define CDCE_NCM_TX_MINLEN 512 /* bytes, must be power of two */ +#define CDCE_NCM_TX_MAXLEN (16384 + 4) /* bytes, must be short terminated */ +#define CDCE_NCM_TX_FRAMES_MAX 8 /* units */ + +#define CDCE_NCM_RX_MAXLEN (1UL << 14) /* bytes */ +#define CDCE_NCM_RX_FRAMES_MAX 1 /* units */ + +#define CDCE_NCM_SUBFRAMES_MAX 32 /* units */ + +#define CDCE_NCM_ALIGN(rem,off,mod) \ + ((uint32_t)(((uint32_t)(rem)) - \ + ((uint32_t)((-(uint32_t)(off)) & (-(uint32_t)(mod)))))) + +#ifndef CDCE_HAVE_NCM +#define CDCE_HAVE_NCM 1 +#endif + +enum { + CDCE_BULK_RX, + CDCE_BULK_TX, + CDCE_INTR_RX, + CDCE_INTR_TX, + CDCE_N_TRANSFER, +}; + +struct cdce_ncm { + struct usb_ncm16_hdr hdr; + struct usb_ncm16_dpt dpt; + struct usb_ncm16_dp dp[CDCE_NCM_SUBFRAMES_MAX]; + uint32_t rx_max; + uint32_t tx_max; + uint16_t tx_remainder; + uint16_t tx_modulus; + uint16_t tx_struct_align; + uint16_t tx_seq; + uint16_t tx_nframe; +}; + +struct cdce_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; +#if CDCE_HAVE_NCM + struct cdce_ncm sc_ncm; +#endif + struct usb_xfer *sc_xfer[CDCE_N_TRANSFER]; + struct mbuf *sc_rx_buf[CDCE_FRAMES_MAX]; + struct mbuf *sc_tx_buf[CDCE_FRAMES_MAX]; + + int sc_flags; +#define CDCE_FLAG_ZAURUS 0x0001 +#define CDCE_FLAG_NO_UNION 0x0002 +#define CDCE_FLAG_RX_DATA 0x0010 + + uint8_t sc_eaddr_str_index; + uint8_t sc_ifaces_index[2]; + uint8_t sc_notify_state; +#define CDCE_NOTIFY_NETWORK_CONNECTION 0 +#define CDCE_NOTIFY_SPEED_CHANGE 1 +#define CDCE_NOTIFY_DONE 2 +}; + +#define CDCE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define CDCE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define CDCE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) +#endif /* _USB_IF_CDCEREG_H_ */ diff --git a/freebsd/sys/dev/usb/net/if_cue.c b/freebsd/sys/dev/usb/net/if_cue.c new file mode 100644 index 00000000..7110f78e --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_cue.c @@ -0,0 +1,657 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate + * adapters and others. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The + * RX filter uses a 512-bit multicast hash table, single perfect entry + * for the station address, and promiscuous mode. Unlike the ADMtek + * and KLSI chips, the CATC ASIC supports read and write combining + * mode where multiple packets can be transferred using a single bulk + * transaction, which helps performance a great deal. + */ + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR cue_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_cuereg.h> + +/* + * Various supported device vendors/products. + */ + +/* Belkin F5U111 adapter covered by NETMATE entry */ + +static const STRUCT_USB_HOST_ID cue_devs[] = { +#define CUE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + CUE_DEV(CATC, NETMATE), + CUE_DEV(CATC, NETMATE2), + CUE_DEV(SMARTBRIDGES, SMARTLINK), +#undef CUE_DEV +}; + +/* prototypes */ + +static device_probe_t cue_probe; +static device_attach_t cue_attach; +static device_detach_t cue_detach; + +static usb_callback_t cue_bulk_read_callback; +static usb_callback_t cue_bulk_write_callback; + +static uether_fn_t cue_attach_post; +static uether_fn_t cue_init; +static uether_fn_t cue_stop; +static uether_fn_t cue_start; +static uether_fn_t cue_tick; +static uether_fn_t cue_setmulti; +static uether_fn_t cue_setpromisc; + +static uint8_t cue_csr_read_1(struct cue_softc *, uint16_t); +static uint16_t cue_csr_read_2(struct cue_softc *, uint8_t); +static int cue_csr_write_1(struct cue_softc *, uint16_t, uint16_t); +static int cue_mem(struct cue_softc *, uint8_t, uint16_t, void *, int); +static int cue_getmac(struct cue_softc *, void *); +static uint32_t cue_mchash(const uint8_t *); +static void cue_reset(struct cue_softc *); + +#ifdef USB_DEBUG +static int cue_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, cue, CTLFLAG_RW, 0, "USB cue"); +SYSCTL_INT(_hw_usb_cue, OID_AUTO, debug, CTLFLAG_RWTUN, &cue_debug, 0, + "Debug level"); +#endif + +static const struct usb_config cue_config[CUE_N_TRANSFER] = { + + [CUE_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (MCLBYTES + 2), + .flags = {.pipe_bof = 1,}, + .callback = cue_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [CUE_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (MCLBYTES + 2), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = cue_bulk_read_callback, + }, +}; + +static device_method_t cue_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cue_probe), + DEVMETHOD(device_attach, cue_attach), + DEVMETHOD(device_detach, cue_detach), + + DEVMETHOD_END +}; + +static driver_t cue_driver = { + .name = "cue", + .methods = cue_methods, + .size = sizeof(struct cue_softc), +}; + +static devclass_t cue_devclass; + +DRIVER_MODULE(cue, uhub, cue_driver, cue_devclass, NULL, 0); +MODULE_DEPEND(cue, uether, 1, 1, 1); +MODULE_DEPEND(cue, usb, 1, 1, 1); +MODULE_DEPEND(cue, ether, 1, 1, 1); +MODULE_VERSION(cue, 1); +USB_PNP_HOST_INFO(cue_devs); + +static const struct usb_ether_methods cue_ue_methods = { + .ue_attach_post = cue_attach_post, + .ue_start = cue_start, + .ue_init = cue_init, + .ue_stop = cue_stop, + .ue_tick = cue_tick, + .ue_setmulti = cue_setmulti, + .ue_setpromisc = cue_setpromisc, +}; + +#define CUE_SETBIT(sc, reg, x) \ + cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x)) + +#define CUE_CLRBIT(sc, reg, x) \ + cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x)) + +static uint8_t +cue_csr_read_1(struct cue_softc *sc, uint16_t reg) +{ + struct usb_device_request req; + uint8_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = CUE_CMD_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + if (uether_do_request(&sc->sc_ue, &req, &val, 1000)) { + /* ignore any errors */ + } + return (val); +} + +static uint16_t +cue_csr_read_2(struct cue_softc *sc, uint8_t reg) +{ + struct usb_device_request req; + uint16_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = CUE_CMD_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 2); + + (void)uether_do_request(&sc->sc_ue, &req, &val, 1000); + return (le16toh(val)); +} + +static int +cue_csr_write_1(struct cue_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = CUE_CMD_WRITEREG; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + return (uether_do_request(&sc->sc_ue, &req, NULL, 1000)); +} + +static int +cue_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, int len) +{ + struct usb_device_request req; + + if (cmd == CUE_CMD_READSRAM) + req.bmRequestType = UT_READ_VENDOR_DEVICE; + else + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = cmd; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +cue_getmac(struct cue_softc *sc, void *buf) +{ + struct usb_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = CUE_CMD_GET_MACADDR; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, ETHER_ADDR_LEN); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +#define CUE_BITS 9 + +static uint32_t +cue_mchash(const uint8_t *addr) +{ + uint32_t crc; + + /* Compute CRC for the address value. */ + crc = ether_crc32_le(addr, ETHER_ADDR_LEN); + + return (crc & ((1 << CUE_BITS) - 1)); +} + +static void +cue_setpromisc(struct usb_ether *ue) +{ + struct cue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + /* if we want promiscuous mode, set the allframes bit */ + if (ifp->if_flags & IFF_PROMISC) + CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); + else + CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); + + /* write multicast hash-bits */ + cue_setmulti(ue); +} + +static void +cue_setmulti(struct usb_ether *ue) +{ + struct cue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t h = 0, i; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + for (i = 0; i < 8; i++) + hashtbl[i] = 0xff; + cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, + &hashtbl, 8); + return; + } + + /* now program new ones */ + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + hashtbl[h >> 3] |= 1 << (h & 0x7); + } + if_maddr_runlock(ifp); + + /* + * Also include the broadcast address in the filter + * so we can receive broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) { + h = cue_mchash(ifp->if_broadcastaddr); + hashtbl[h >> 3] |= 1 << (h & 0x7); + } + + cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &hashtbl, 8); +} + +static void +cue_reset(struct cue_softc *sc) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = CUE_CMD_RESET; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + if (uether_do_request(&sc->sc_ue, &req, NULL, 1000)) { + /* ignore any errors */ + } + + /* + * wait a little while for the chip to get its brains in order: + */ + uether_pause(&sc->sc_ue, hz / 100); +} + +static void +cue_attach_post(struct usb_ether *ue) +{ + struct cue_softc *sc = uether_getsc(ue); + + cue_getmac(sc, ue->ue_eaddr); +} + +static int +cue_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != CUE_CONFIG_IDX) + return (ENXIO); + if (uaa->info.bIfaceIndex != CUE_IFACE_IDX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(cue_devs, sizeof(cue_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +cue_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct cue_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + device_set_usb_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = CUE_IFACE_IDX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, cue_config, CUE_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &cue_ue_methods; + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + cue_detach(dev); + return (ENXIO); /* failure */ +} + +static int +cue_detach(device_t dev) +{ + struct cue_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +cue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cue_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + struct usb_page_cache *pc; + uint8_t buf[2]; + int len; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (actlen <= (int)(2 + sizeof(struct ether_header))) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, buf, 2); + actlen -= 2; + len = buf[0] | (buf[1] << 8); + len = min(actlen, len); + + uether_rxbuf(ue, pc, 2, len); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read error, %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + + } +} + +static void +cue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct cue_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + if (m->m_pkthdr.len > MCLBYTES) + m->m_pkthdr.len = MCLBYTES; + usbd_xfer_set_frame_len(xfer, 0, (m->m_pkthdr.len + 2)); + + /* the first two bytes are the frame length */ + + buf[0] = (uint8_t)(m->m_pkthdr.len); + buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, buf, 2); + usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len); + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usbd_transfer_submit(xfer); + + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +cue_tick(struct usb_ether *ue) +{ + struct cue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_SINGLECOLL)); + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_MULTICOLL)); + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, cue_csr_read_2(sc, CUE_TX_EXCESSCOLL)); + + if (cue_csr_read_2(sc, CUE_RX_FRAMEERR)) + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); +} + +static void +cue_start(struct usb_ether *ue) +{ + struct cue_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[CUE_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[CUE_BULK_DT_WR]); +} + +static void +cue_init(struct usb_ether *ue) +{ + struct cue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + int i; + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + cue_stop(ue); +#if 0 + cue_reset(sc); +#endif + /* Set MAC address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + cue_csr_write_1(sc, CUE_PAR0 - i, IF_LLADDR(ifp)[i]); + + /* Enable RX logic. */ + cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON); + + /* Load the multicast filter */ + cue_setpromisc(ue); + + /* + * Set the number of RX and TX buffers that we want + * to reserve inside the ASIC. + */ + cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); + cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); + + /* Set advanced operation modes. */ + cue_csr_write_1(sc, CUE_ADVANCED_OPMODES, + CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */ + + /* Program the LED operation. */ + cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); + + usbd_xfer_set_stall(sc->sc_xfer[CUE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + cue_start(ue); +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +cue_stop(struct usb_ether *ue) +{ + struct cue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + CUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[CUE_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[CUE_BULK_DT_RD]); + + cue_csr_write_1(sc, CUE_ETHCTL, 0); + cue_reset(sc); +} diff --git a/freebsd/sys/dev/usb/net/if_cuereg.h b/freebsd/sys/dev/usb/net/if_cuereg.h new file mode 100644 index 00000000..ff245c05 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_cuereg.h @@ -0,0 +1,132 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Definitions for the CATC Netmate II USB to ethernet controller. + */ + +/* Vendor specific control commands. */ +#define CUE_CMD_RESET 0xF4 +#define CUE_CMD_GET_MACADDR 0xF2 +#define CUE_CMD_WRITEREG 0xFA +#define CUE_CMD_READREG 0xFB +#define CUE_CMD_READSRAM 0xF1 +#define CUE_CMD_WRITESRAM 0xFC +/* Internal registers. */ +#define CUE_TX_BUFCNT 0x20 +#define CUE_RX_BUFCNT 0x21 +#define CUE_ADVANCED_OPMODES 0x22 +#define CUE_TX_BUFPKTS 0x23 +#define CUE_RX_BUFPKTS 0x24 +#define CUE_RX_MAXCHAIN 0x25 +#define CUE_ETHCTL 0x60 +#define CUE_ETHSTS 0x61 +#define CUE_PAR5 0x62 +#define CUE_PAR4 0x63 +#define CUE_PAR3 0x64 +#define CUE_PAR2 0x65 +#define CUE_PAR1 0x66 +#define CUE_PAR0 0x67 +/* Error counters, all 16 bits wide. */ +#define CUE_TX_SINGLECOLL 0x69 +#define CUE_TX_MULTICOLL 0x6B +#define CUE_TX_EXCESSCOLL 0x6D +#define CUE_RX_FRAMEERR 0x6F +#define CUE_LEDCTL 0x81 +/* Advenced operating mode register. */ +#define CUE_AOP_SRAMWAITS 0x03 +#define CUE_AOP_EMBED_RXLEN 0x08 +#define CUE_AOP_RXCOMBINE 0x10 +#define CUE_AOP_TXCOMBINE 0x20 +#define CUE_AOP_EVEN_PKT_READS 0x40 +#define CUE_AOP_LOOPBK 0x80 +/* Ethernet control register. */ +#define CUE_ETHCTL_RX_ON 0x01 +#define CUE_ETHCTL_LINK_POLARITY 0x02 +#define CUE_ETHCTL_LINK_FORCE_OK 0x04 +#define CUE_ETHCTL_MCAST_ON 0x08 +#define CUE_ETHCTL_PROMISC 0x10 +/* Ethernet status register. */ +#define CUE_ETHSTS_NO_CARRIER 0x01 +#define CUE_ETHSTS_LATECOLL 0x02 +#define CUE_ETHSTS_EXCESSCOLL 0x04 +#define CUE_ETHSTS_TXBUF_AVAIL 0x08 +#define CUE_ETHSTS_BAD_POLARITY 0x10 +#define CUE_ETHSTS_LINK_OK 0x20 +/* LED control register. */ +#define CUE_LEDCTL_BLINK_1X 0x00 +#define CUE_LEDCTL_BLINK_2X 0x01 +#define CUE_LEDCTL_BLINK_QUARTER_ON 0x02 +#define CUE_LEDCTL_BLINK_QUARTER_OFF 0x03 +#define CUE_LEDCTL_OFF 0x04 +#define CUE_LEDCTL_FOLLOW_LINK 0x08 + +/* + * Address in ASIC's internal SRAM where the multicast hash table lives. + * The table is 64 bytes long, giving us a 512-bit table. We have to set + * the bit that corresponds to the broadcast address in order to enable + * reception of broadcast frames. + */ +#define CUE_MCAST_TABLE_ADDR 0xFA80 + +#define CUE_TIMEOUT 1000 +#define CUE_MIN_FRAMELEN 60 +#define CUE_RX_FRAMES 1 +#define CUE_TX_FRAMES 1 + +#define CUE_CTL_READ 0x01 +#define CUE_CTL_WRITE 0x02 + +#define CUE_CONFIG_IDX 0 /* config number 1 */ +#define CUE_IFACE_IDX 0 + +/* The interrupt endpoint is currently unused by the CATC part. */ +enum { + CUE_BULK_DT_WR, + CUE_BULK_DT_RD, + CUE_N_TRANSFER, +}; + +struct cue_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[CUE_N_TRANSFER]; + + int sc_flags; +#define CUE_FLAG_LINK 0x0001 /* got a link */ +}; + +#define CUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define CUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define CUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/if_ipheth.c b/freebsd/sys/dev/usb/net/if_ipheth.c new file mode 100644 index 00000000..025d5208 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_ipheth.c @@ -0,0 +1,548 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2009 Diego Giagio. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Thanks to Diego Giagio for figuring out the programming details for + * the Apple iPhone Ethernet driver. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR ipheth_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_iphethvar.h> + +static device_probe_t ipheth_probe; +static device_attach_t ipheth_attach; +static device_detach_t ipheth_detach; + +static usb_callback_t ipheth_bulk_write_callback; +static usb_callback_t ipheth_bulk_read_callback; + +static uether_fn_t ipheth_attach_post; +static uether_fn_t ipheth_tick; +static uether_fn_t ipheth_init; +static uether_fn_t ipheth_stop; +static uether_fn_t ipheth_start; +static uether_fn_t ipheth_setmulti; +static uether_fn_t ipheth_setpromisc; + +#ifdef USB_DEBUG +static int ipheth_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet"); +SYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RWTUN, &ipheth_debug, 0, "Debug level"); +#endif + +static const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = { + + [IPHETH_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .frames = IPHETH_RX_FRAMES_MAX, + .bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES), + .flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, + .callback = ipheth_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, + + [IPHETH_BULK_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .frames = IPHETH_TX_FRAMES_MAX, + .bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE), + .flags = {.force_short_xfer = 1,}, + .callback = ipheth_bulk_write_callback, + .timeout = IPHETH_TX_TIMEOUT, + }, +}; + +static device_method_t ipheth_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ipheth_probe), + DEVMETHOD(device_attach, ipheth_attach), + DEVMETHOD(device_detach, ipheth_detach), + + DEVMETHOD_END +}; + +static driver_t ipheth_driver = { + .name = "ipheth", + .methods = ipheth_methods, + .size = sizeof(struct ipheth_softc), +}; + +static devclass_t ipheth_devclass; + +static const STRUCT_USB_HOST_ID ipheth_devs[] = { +#if 0 + {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO)}, + {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO)}, + {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO)}, + {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO)}, + {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO)}, + {IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO)}, +#else + /* product agnostic interface match */ + {USB_VENDOR(USB_VENDOR_APPLE), + USB_IFACE_CLASS(IPHETH_USBINTF_CLASS), + USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS), + USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)}, +#endif +}; + +DRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0); +MODULE_VERSION(ipheth, 1); +MODULE_DEPEND(ipheth, uether, 1, 1, 1); +MODULE_DEPEND(ipheth, usb, 1, 1, 1); +MODULE_DEPEND(ipheth, ether, 1, 1, 1); +USB_PNP_HOST_INFO(ipheth_devs); + +static const struct usb_ether_methods ipheth_ue_methods = { + .ue_attach_post = ipheth_attach_post, + .ue_start = ipheth_start, + .ue_init = ipheth_init, + .ue_tick = ipheth_tick, + .ue_stop = ipheth_stop, + .ue_setmulti = ipheth_setmulti, + .ue_setpromisc = ipheth_setpromisc, +}; + +#define IPHETH_ID(v,p,c,sc,pt) \ + USB_VENDOR(v), USB_PRODUCT(p), \ + USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \ + USB_IFACE_PROTOCOL(pt) + +static int +ipheth_get_mac_addr(struct ipheth_softc *sc) +{ + struct usb_device_request req; + int error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = IPHETH_CMD_GET_MACADDR; + req.wValue[0] = 0; + req.wValue[1] = 0; + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + req.wLength[0] = ETHER_ADDR_LEN; + req.wLength[1] = 0; + + error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data); + + if (error) + return (error); + + memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN); + + return (0); +} + +static int +ipheth_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa)); +} + +static int +ipheth_attach(device_t dev) +{ + struct ipheth_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + struct usb_attach_arg *uaa = device_get_ivars(dev); + int error; + + sc->sc_iface_no = uaa->info.bIfaceIndex; + + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + error = usbd_set_alt_interface_index(uaa->device, + uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM); + if (error) { + device_printf(dev, "Cannot set alternate setting\n"); + goto detach; + } + error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no, + sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "Cannot setup USB transfers\n"); + goto detach; + } + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &ipheth_ue_methods; + + error = ipheth_get_mac_addr(sc); + if (error) { + device_printf(dev, "Cannot get MAC address\n"); + goto detach; + } + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + ipheth_detach(dev); + return (ENXIO); /* failure */ +} + +static int +ipheth_detach(device_t dev) +{ + struct ipheth_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + /* stop all USB transfers first */ + usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER); + + uether_ifdetach(ue); + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +ipheth_start(struct usb_ether *ue) +{ + struct ipheth_softc *sc = uether_getsc(ue); + + /* + * Start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]); + usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]); +} + +static void +ipheth_stop(struct usb_ether *ue) +{ + struct ipheth_softc *sc = uether_getsc(ue); + + /* + * Stop the USB transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]); + usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]); +} + +static void +ipheth_tick(struct usb_ether *ue) +{ + struct ipheth_softc *sc = uether_getsc(ue); + struct usb_device_request req; + int error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = IPHETH_CMD_CARRIER_CHECK; + req.wValue[0] = 0; + req.wValue[1] = 0; + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + req.wLength[0] = IPHETH_CTRL_BUF_SIZE; + req.wLength[1] = 0; + + error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT); + + if (error) + return; + + sc->sc_carrier_on = + (sc->sc_data[0] == IPHETH_CARRIER_ON); +} + +static void +ipheth_attach_post(struct usb_ether *ue) +{ + +} + +static void +ipheth_init(struct usb_ether *ue) +{ + struct ipheth_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + IPHETH_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + /* stall data write direction, which depends on USB mode */ + usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]); + + /* start data transfers */ + ipheth_start(ue); +} + +static void +ipheth_setmulti(struct usb_ether *ue) +{ + +} + +static void +ipheth_setpromisc(struct usb_ether *ue) +{ + +} + +static void +ipheth_free_queue(struct mbuf **ppm, uint8_t n) +{ + uint8_t x; + + for (x = 0; x != n; x++) { + if (ppm[x] != NULL) { + m_freem(ppm[x]); + ppm[x] = NULL; + } + } +} + +static void +ipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ipheth_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + uint8_t x; + int actlen; + int aframes; + + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); + + DPRINTFN(1, "\n"); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", + actlen, aframes); + + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* free all previous TX buffers */ + ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + break; + + usbd_xfer_set_frame_offset(xfer, + x * IPHETH_BUF_SIZE, x); + + pc = usbd_xfer_get_frame(xfer, x); + + sc->sc_tx_buf[x] = m; + + if (m->m_pkthdr.len > IPHETH_BUF_SIZE) + m->m_pkthdr.len = IPHETH_BUF_SIZE; + + usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); + + usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE); + + if (IPHETH_BUF_SIZE != m->m_pkthdr.len) { + usbd_frame_zero(pc, m->m_pkthdr.len, + IPHETH_BUF_SIZE - m->m_pkthdr.len); + } + + /* + * If there's a BPF listener, bounce a copy of + * this frame to him: + */ + BPF_MTAP(ifp, m); + } + if (x != 0) { + usbd_xfer_set_frames(xfer, x); + + usbd_transfer_submit(xfer); + } + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + /* free all previous TX buffers */ + ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX); + + /* count output errors */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +ipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ipheth_softc *sc = usbd_xfer_softc(xfer); + struct mbuf *m; + uint8_t x; + int actlen; + int aframes; + int len; + + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTF("received %u bytes in %u frames\n", actlen, aframes); + + for (x = 0; x != aframes; x++) { + + m = sc->sc_rx_buf[x]; + sc->sc_rx_buf[x] = NULL; + len = usbd_xfer_frame_len(xfer, x); + + if (len < (int)(sizeof(struct ether_header) + + IPHETH_RX_ADJ)) { + m_freem(m); + continue; + } + + m_adj(m, IPHETH_RX_ADJ); + + /* queue up mbuf */ + uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ); + } + + /* FALLTHROUGH */ + case USB_ST_SETUP: + + for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) { + if (sc->sc_rx_buf[x] == NULL) { + m = uether_newbuf(); + if (m == NULL) + goto tr_stall; + + /* cancel alignment for ethernet */ + m_adj(m, ETHER_ALIGN); + + sc->sc_rx_buf[x] = m; + } else { + m = sc->sc_rx_buf[x]; + } + + usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); + } + /* set number of frames and start hardware */ + usbd_xfer_set_frames(xfer, x); + usbd_transfer_submit(xfer); + /* flush any received frames */ + uether_rxflush(&sc->sc_ue); + break; + + default: /* Error */ + DPRINTF("error = %s\n", usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + tr_stall: + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frames(xfer, 0); + usbd_transfer_submit(xfer); + break; + } + /* need to free the RX-mbufs when we are cancelled */ + ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX); + break; + } +} diff --git a/freebsd/sys/dev/usb/net/if_iphethvar.h b/freebsd/sys/dev/usb/net/if_iphethvar.h new file mode 100644 index 00000000..65b0c940 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_iphethvar.h @@ -0,0 +1,84 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2009 Diego Giagio. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Thanks to Diego Giagio for figuring out the programming details for + * the Apple iPhone Ethernet driver. + */ + +#ifndef _IF_IPHETHVAR_H_ +#define _IF_IPHETHVAR_H_ + +#define IPHETH_USBINTF_CLASS 255 +#define IPHETH_USBINTF_SUBCLASS 253 +#define IPHETH_USBINTF_PROTO 1 + +#define IPHETH_BUF_SIZE 1516 +#define IPHETH_TX_TIMEOUT 5000 /* ms */ + +#define IPHETH_RX_FRAMES_MAX 1 +#define IPHETH_TX_FRAMES_MAX 8 + +#define IPHETH_RX_ADJ 2 + +#define IPHETH_CFG_INDEX 0 +#define IPHETH_IF_INDEX 2 +#define IPHETH_ALT_INTFNUM 1 + +#define IPHETH_CTRL_ENDP 0x00 +#define IPHETH_CTRL_BUF_SIZE 0x40 +#define IPHETH_CTRL_TIMEOUT 5000 /* ms */ + +#define IPHETH_CMD_GET_MACADDR 0x00 +#define IPHETH_CMD_CARRIER_CHECK 0x45 + +#define IPHETH_CARRIER_ON 0x04 + +enum { + IPHETH_BULK_TX, + IPHETH_BULK_RX, + IPHETH_N_TRANSFER, +}; + +struct ipheth_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + + struct usb_xfer *sc_xfer[IPHETH_N_TRANSFER]; + struct mbuf *sc_rx_buf[IPHETH_RX_FRAMES_MAX]; + struct mbuf *sc_tx_buf[IPHETH_TX_FRAMES_MAX]; + + uint8_t sc_data[IPHETH_CTRL_BUF_SIZE]; + uint8_t sc_iface_no; + uint8_t sc_carrier_on; +}; + +#define IPHETH_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define IPHETH_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define IPHETH_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) + +#endif /* _IF_IPHETHVAR_H_ */ diff --git a/freebsd/sys/dev/usb/net/if_kue.c b/freebsd/sys/dev/usb/net/if_kue.c new file mode 100644 index 00000000..02d3322c --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_kue.c @@ -0,0 +1,714 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The KLSI USB to ethernet adapter chip contains an USB serial interface, + * ethernet MAC and embedded microcontroller (called the QT Engine). + * The chip must have firmware loaded into it before it will operate. + * Packets are passed between the chip and host via bulk transfers. + * There is an interrupt endpoint mentioned in the software spec, however + * it's currently unused. This device is 10Mbps half-duplex only, hence + * there is no media selection logic. The MAC supports a 128 entry + * multicast filter, though the exact size of the filter can depend + * on the firmware. Curiously, while the software spec describes various + * ethernet statistics counters, my sample adapter and firmware combination + * claims not to support any statistics counters at all. + * + * Note that once we load the firmware in the device, we have to be + * careful not to load it again: if you restart your computer but + * leave the adapter attached to the USB controller, it may remain + * powered on and retain its firmware. In this case, we don't need + * to load the firmware a second time. + * + * Special thanks to Rob Furr for providing an ADS Technologies + * adapter for development and testing. No monkeys were harmed during + * the development of this driver. + */ + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR kue_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_kuereg.h> +#include <dev/usb/net/if_kuefw.h> + +/* + * Various supported device vendors/products. + */ +static const STRUCT_USB_HOST_ID kue_devs[] = { +#define KUE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + KUE_DEV(3COM, 3C19250), + KUE_DEV(3COM, 3C460), + KUE_DEV(ABOCOM, URE450), + KUE_DEV(ADS, UBS10BT), + KUE_DEV(ADS, UBS10BTX), + KUE_DEV(AOX, USB101), + KUE_DEV(ASANTE, EA), + KUE_DEV(ATEN, DSB650C), + KUE_DEV(ATEN, UC10T), + KUE_DEV(COREGA, ETHER_USB_T), + KUE_DEV(DLINK, DSB650C), + KUE_DEV(ENTREGA, E45), + KUE_DEV(ENTREGA, XX1), + KUE_DEV(ENTREGA, XX2), + KUE_DEV(IODATA, USBETT), + KUE_DEV(JATON, EDA), + KUE_DEV(KINGSTON, XX1), + KUE_DEV(KLSI, DUH3E10BT), + KUE_DEV(KLSI, DUH3E10BTN), + KUE_DEV(LINKSYS, USB10T), + KUE_DEV(MOBILITY, EA), + KUE_DEV(NETGEAR, EA101), + KUE_DEV(NETGEAR, EA101X), + KUE_DEV(PERACOM, ENET), + KUE_DEV(PERACOM, ENET2), + KUE_DEV(PERACOM, ENET3), + KUE_DEV(PORTGEAR, EA8), + KUE_DEV(PORTGEAR, EA9), + KUE_DEV(PORTSMITH, EEA), + KUE_DEV(SHARK, PA), + KUE_DEV(SILICOM, GPE), + KUE_DEV(SILICOM, U2E), + KUE_DEV(SMC, 2102USB), +#undef KUE_DEV +}; + +/* prototypes */ + +static device_probe_t kue_probe; +static device_attach_t kue_attach; +static device_detach_t kue_detach; + +static usb_callback_t kue_bulk_read_callback; +static usb_callback_t kue_bulk_write_callback; + +static uether_fn_t kue_attach_post; +static uether_fn_t kue_init; +static uether_fn_t kue_stop; +static uether_fn_t kue_start; +static uether_fn_t kue_setmulti; +static uether_fn_t kue_setpromisc; + +static int kue_do_request(struct kue_softc *, + struct usb_device_request *, void *); +static int kue_setword(struct kue_softc *, uint8_t, uint16_t); +static int kue_ctl(struct kue_softc *, uint8_t, uint8_t, uint16_t, + void *, int); +static int kue_load_fw(struct kue_softc *); +static void kue_reset(struct kue_softc *); + +#ifdef USB_DEBUG +static int kue_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, kue, CTLFLAG_RW, 0, "USB kue"); +SYSCTL_INT(_hw_usb_kue, OID_AUTO, debug, CTLFLAG_RWTUN, &kue_debug, 0, + "Debug level"); +#endif + +static const struct usb_config kue_config[KUE_N_TRANSFER] = { + + [KUE_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (MCLBYTES + 2 + 64), + .flags = {.pipe_bof = 1,}, + .callback = kue_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [KUE_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (MCLBYTES + 2), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = kue_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, +}; + +static device_method_t kue_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, kue_probe), + DEVMETHOD(device_attach, kue_attach), + DEVMETHOD(device_detach, kue_detach), + + DEVMETHOD_END +}; + +static driver_t kue_driver = { + .name = "kue", + .methods = kue_methods, + .size = sizeof(struct kue_softc), +}; + +static devclass_t kue_devclass; + +DRIVER_MODULE(kue, uhub, kue_driver, kue_devclass, NULL, 0); +MODULE_DEPEND(kue, uether, 1, 1, 1); +MODULE_DEPEND(kue, usb, 1, 1, 1); +MODULE_DEPEND(kue, ether, 1, 1, 1); +MODULE_VERSION(kue, 1); +USB_PNP_HOST_INFO(kue_devs); + +static const struct usb_ether_methods kue_ue_methods = { + .ue_attach_post = kue_attach_post, + .ue_start = kue_start, + .ue_init = kue_init, + .ue_stop = kue_stop, + .ue_setmulti = kue_setmulti, + .ue_setpromisc = kue_setpromisc, +}; + +/* + * We have a custom do_request function which is almost like the + * regular do_request function, except it has a much longer timeout. + * Why? Because we need to make requests over the control endpoint + * to download the firmware to the device, which can take longer + * than the default timeout. + */ +static int +kue_do_request(struct kue_softc *sc, struct usb_device_request *req, + void *data) +{ + usb_error_t err; + + err = uether_do_request(&sc->sc_ue, req, data, 60000); + + return (err); +} + +static int +kue_setword(struct kue_softc *sc, uint8_t breq, uint16_t word) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = breq; + USETW(req.wValue, word); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + return (kue_do_request(sc, &req, NULL)); +} + +static int +kue_ctl(struct kue_softc *sc, uint8_t rw, uint8_t breq, + uint16_t val, void *data, int len) +{ + struct usb_device_request req; + + if (rw == KUE_CTL_WRITE) + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + else + req.bmRequestType = UT_READ_VENDOR_DEVICE; + + + req.bRequest = breq; + USETW(req.wValue, val); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + return (kue_do_request(sc, &req, data)); +} + +static int +kue_load_fw(struct kue_softc *sc) +{ + struct usb_device_descriptor *dd; + uint16_t hwrev; + usb_error_t err; + + dd = usbd_get_device_descriptor(sc->sc_ue.ue_udev); + hwrev = UGETW(dd->bcdDevice); + + /* + * First, check if we even need to load the firmware. + * If the device was still attached when the system was + * rebooted, it may already have firmware loaded in it. + * If this is the case, we don't need to do it again. + * And in fact, if we try to load it again, we'll hang, + * so we have to avoid this condition if we don't want + * to look stupid. + * + * We can test this quickly by checking the bcdRevision + * code. The NIC will return a different revision code if + * it's probed while the firmware is still loaded and + * running. + */ + if (hwrev == 0x0202) + return(0); + + /* Load code segment */ + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_code_seg, sizeof(kue_code_seg)); + if (err) { + device_printf(sc->sc_ue.ue_dev, "failed to load code segment: %s\n", + usbd_errstr(err)); + return(ENXIO); + } + + /* Load fixup segment */ + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_fix_seg, sizeof(kue_fix_seg)); + if (err) { + device_printf(sc->sc_ue.ue_dev, "failed to load fixup segment: %s\n", + usbd_errstr(err)); + return(ENXIO); + } + + /* Send trigger command. */ + err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, + 0, kue_trig_seg, sizeof(kue_trig_seg)); + if (err) { + device_printf(sc->sc_ue.ue_dev, "failed to load trigger segment: %s\n", + usbd_errstr(err)); + return(ENXIO); + } + + return (0); +} + +static void +kue_setpromisc(struct usb_ether *ue) +{ + struct kue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + KUE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_PROMISC) + sc->sc_rxfilt |= KUE_RXFILT_PROMISC; + else + sc->sc_rxfilt &= ~KUE_RXFILT_PROMISC; + + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); +} + +static void +kue_setmulti(struct usb_ether *ue) +{ + struct kue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + struct ifmultiaddr *ifma; + int i = 0; + + KUE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI; + sc->sc_rxfilt &= ~KUE_RXFILT_MULTICAST; + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); + return; + } + + sc->sc_rxfilt &= ~KUE_RXFILT_ALLMULTI; + + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + /* + * If there are too many addresses for the + * internal filter, switch over to allmulti mode. + */ + if (i == KUE_MCFILTCNT(sc)) + break; + memcpy(KUE_MCFILT(sc, i), + LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + ETHER_ADDR_LEN); + i++; + } + if_maddr_runlock(ifp); + + if (i == KUE_MCFILTCNT(sc)) + sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI; + else { + sc->sc_rxfilt |= KUE_RXFILT_MULTICAST; + kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS, + i, sc->sc_mcfilters, i * ETHER_ADDR_LEN); + } + + kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt); +} + +/* + * Issue a SET_CONFIGURATION command to reset the MAC. This should be + * done after the firmware is loaded into the adapter in order to + * bring it into proper operation. + */ +static void +kue_reset(struct kue_softc *sc) +{ + struct usb_config_descriptor *cd; + usb_error_t err; + + cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); + + err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) + DPRINTF("reset failed (ignored)\n"); + + /* wait a little while for the chip to get its brains in order */ + uether_pause(&sc->sc_ue, hz / 100); +} + +static void +kue_attach_post(struct usb_ether *ue) +{ + struct kue_softc *sc = uether_getsc(ue); + int error; + + /* load the firmware into the NIC */ + error = kue_load_fw(sc); + if (error) { + device_printf(sc->sc_ue.ue_dev, "could not load firmware\n"); + /* ignore the error */ + } + + /* reset the adapter */ + kue_reset(sc); + + /* read ethernet descriptor */ + kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, + 0, &sc->sc_desc, sizeof(sc->sc_desc)); + + /* copy in ethernet address */ + memcpy(ue->ue_eaddr, sc->sc_desc.kue_macaddr, sizeof(ue->ue_eaddr)); +} + +/* + * Probe for a KLSI chip. + */ +static int +kue_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != KUE_CONFIG_IDX) + return (ENXIO); + if (uaa->info.bIfaceIndex != KUE_IFACE_IDX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(kue_devs, sizeof(kue_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do + * setup and ethernet/BPF attach. + */ +static int +kue_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct kue_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + device_set_usb_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = KUE_IFACE_IDX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, kue_config, KUE_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + + sc->sc_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN, + M_USBDEV, M_WAITOK); + if (sc->sc_mcfilters == NULL) { + device_printf(dev, "failed allocating USB memory\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &kue_ue_methods; + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + kue_detach(dev); + return (ENXIO); /* failure */ +} + +static int +kue_detach(device_t dev) +{ + struct kue_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, KUE_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + free(sc->sc_mcfilters, M_USBDEV); + + return (0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void +kue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct kue_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + struct usb_page_cache *pc; + uint8_t buf[2]; + int len; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (actlen <= (int)(2 + sizeof(struct ether_header))) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, buf, 2); + actlen -= 2; + len = buf[0] | (buf[1] << 8); + len = min(actlen, len); + + uether_rxbuf(ue, pc, 2, len); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read error, %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + + } +} + +static void +kue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct kue_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + int total_len; + int temp_len; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + if (m->m_pkthdr.len > MCLBYTES) + m->m_pkthdr.len = MCLBYTES; + temp_len = (m->m_pkthdr.len + 2); + total_len = (temp_len + (64 - (temp_len % 64))); + + /* the first two bytes are the frame length */ + + buf[0] = (uint8_t)(m->m_pkthdr.len); + buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, buf, 2); + usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len); + + usbd_frame_zero(pc, temp_len, total_len - temp_len); + usbd_xfer_set_frame_len(xfer, 0, total_len); + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usbd_transfer_submit(xfer); + + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + + } +} + +static void +kue_start(struct usb_ether *ue) +{ + struct kue_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[KUE_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[KUE_BULK_DT_WR]); +} + +static void +kue_init(struct usb_ether *ue) +{ + struct kue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + KUE_LOCK_ASSERT(sc, MA_OWNED); + + /* set MAC address */ + kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, + 0, IF_LLADDR(ifp), ETHER_ADDR_LEN); + + /* I'm not sure how to tune these. */ +#if 0 + /* + * Leave this one alone for now; setting it + * wrong causes lockups on some machines/controllers. + */ + kue_setword(sc, KUE_CMD_SET_SOFS, 1); +#endif + kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64); + + /* load the multicast filter */ + kue_setpromisc(ue); + + usbd_xfer_set_stall(sc->sc_xfer[KUE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + kue_start(ue); +} + +static void +kue_stop(struct usb_ether *ue) +{ + struct kue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + KUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[KUE_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[KUE_BULK_DT_RD]); +} diff --git a/freebsd/sys/dev/usb/net/if_kuefw.h b/freebsd/sys/dev/usb/net/if_kuefw.h new file mode 100644 index 00000000..2b055a92 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_kuefw.h @@ -0,0 +1,685 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file contains the firmware needed to make the KLSI chip work, + * along with a few constants related to the QT Engine microcontroller + * embedded in the KLSI part. + * + * Firmware is loaded using the vendor-specific 'send scan data' + * command (0xFF). The basic operation is that we must load the + * firmware, then issue some trigger commands to fix it up and start + * it running. There are three transfers: load the binary code, + * load the 'fixup' (data segment?), then issue a command to + * start the code firmware running. The data itself is prefixed by + * a 16-bit signature word, a 16-bit length value, a type byte + * and an interrupt (command) byte. The code segment is of type + * 0x02 (replacement interrupt vector data) and the fixup segment + * is of type 0x03 (replacement interrupt fixup data). The interrupt + * code is 0x64 (load new code). The length word is the total length + * of the segment minus 7. I precomputed the values and stuck them + * into the appropriate locations within the segments to save some + * work in the driver. + */ + +/* QT controller data block types. */ +/* Write data into specific memory location. */ +#define KUE_QTBTYPE_WRITE_DATA 0x00 +/* Write data into interrupt vector location */ +#define KUE_QTBTYPE_WRITE_INTVEC 0x01 +/* Replace interrupt vector with this data */ +#define KUE_QTBTYPE_REPL_INTVEC 0x02 +/* Fixup interrupt vector code with this data */ +#define KUE_QTBTYPE_FIXUP_INTVEC 0x03 +/* Force jump to location */ +#define KUE_QTBTYPE_JUMP 0x04 +/* Force call to location */ +#define KUE_QTBTYPE_CALL 0x05 +/* Force interrupt call */ +#define KUE_QTBTYPE_CALLINTR 0x06 +/* + * Cause data to be written using the specified QT engine + * interrupt, from starting location in memory for a specified + * number of bytes. + */ +#define KUE_QTBTYPE_WRITE_WITH_INTR 0x07 +/* Cause data from stream to be written using specified QT interrupt. */ +#define KUE_QTBTYPE_WRITE_STR_WITH_INTR 0x08 +/* Cause data to be written to config locations. */ +/* Addresses assume 0xc000 offset. */ +#define KUE_QTBTYPE_WRITE_CONFIG 0x09 + +#define KUE_QTINTR_LOAD_CODE 0x64 +#define KUE_QTINTR_TRIGGER_CODE 0x3B +#define KUE_QTINTR_LOAD_CODE_HIGH 0x9C + +/* Firmware code segment */ +static unsigned char kue_code_seg[] = +{ + /******************************************/ + /* NOTE: B6/C3 is data header signature */ + /* 0xAA/0xBB is data length = total */ + /* bytes - 7, 0xCC is type, 0xDD is */ + /* interrupt to use. */ + /******************************************/ + 0xB6, 0xC3, 0xf7, 0x0e, 0x02, 0x64, + 0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00, + 0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f, + 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0, + 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf, + 0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09, + 0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08, + 0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0, + 0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08, + 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1, + 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00, + 0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf, + 0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09, + 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0, + 0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08, + 0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08, + 0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57, + 0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08, + 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00, + 0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09, + 0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59, + 0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00, + 0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1, + 0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03, + 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1, + 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08, + 0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04, + 0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09, + 0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00, + 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57, + 0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08, + 0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08, + 0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00, + 0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, + 0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf, + 0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17, + 0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08, + 0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08, + 0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0, + 0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37, + 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60, + 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9, + 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17, + 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00, + 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00, + 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, + 0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf, + 0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf, + 0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02, + 0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf, + 0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57, + 0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57, + 0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09, + 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02, + 0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01, + 0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02, + 0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1, + 0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77, + 0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02, + 0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77, + 0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07, + 0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00, + 0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77, + 0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda, + 0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, + 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf, + 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8, + 0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77, + 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50, + 0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00, + 0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02, + 0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0, + 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00, + 0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e, + 0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0, + 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07, + 0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07, + 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e, + 0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, + 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf, + 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07, + 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08, + 0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09, + 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07, + 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00, + 0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf, + 0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08, + 0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05, + 0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08, + 0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67, + 0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0, + 0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00, + 0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08, + 0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80, + 0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08, + 0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07, + 0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf, + 0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0, + 0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00, + 0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00, + 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17, + 0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07, + 0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, + 0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03, + 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0, + 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07, + 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00, + 0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, + 0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, + 0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02, + 0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, + 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00, + 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00, + 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00, + 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, + 0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52, + 0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08, + 0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17, + 0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05, + 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04, + 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62, + 0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02, + 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, + 0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77, + 0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9, + 0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1, + 0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27, + 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27, + 0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00, + 0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff, + 0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17, + 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07, + 0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1, + 0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59, + 0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf, + 0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08, + 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf, + 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07, + 0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77, + 0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00, + 0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf, + 0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00, + 0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00, + 0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0, + 0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77, + 0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08, + 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf, + 0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1, + 0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1, + 0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf, + 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09, + 0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53, + 0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05, + 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8, + 0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf, + 0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30, + 0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf, + 0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30, + 0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf, + 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, + 0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09, + 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0, + 0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, + 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, + 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0, + 0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37, + 0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08, + 0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0, + 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17, + 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08, + 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, + 0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0, + 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00, + 0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02, + 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, + 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00, + 0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0, + 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17, + 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04, + 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09, + 0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00, + 0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02, + 0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05, + 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57, + 0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf, + 0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0, + 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09, + 0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0, + 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17, + 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, + 0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00, + 0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf, + 0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17, + 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57, + 0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77, + 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, + 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00, + 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17, + 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1, + 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02, + 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77, + 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, + 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, + 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf, + 0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57, + 0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8, + 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17, + 0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08, + 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09, + 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77, + 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57, + 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19, + 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17, + 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf, + 0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57, + 0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b, + 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05, + 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09, + 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04, + 0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1, + 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf, + 0x68, 0x04, 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80, + 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e, + 0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00, + 0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09, + 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00, + 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07, + 0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13, + 0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08, + 0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09, + 0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09, + 0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b, + 0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00, + 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00, + 0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf, + 0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90, + 0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b, + 0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00, + 0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07, + 0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00, + 0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00, + 0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, + 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0, + 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf, + 0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf, + 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08, + 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf, + 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94, + 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09, + 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf, + 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00, + 0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00, + 0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00, + 0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00, + 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0, + 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0, + 0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a, + 0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a, + 0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b, + 0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02, + 0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00, + 0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57, + 0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37, + 0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2, + 0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00, + 0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57, + 0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b, + 0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09, + 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07, + 0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08, + 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08, + 0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0, + 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, + 0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08, + 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, + 0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09, + 0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08, + 0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c, + 0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf, + 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2, + 0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08, + 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07, + 0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb, + 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, + 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57, + 0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07, + 0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00, + 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08, + 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07, + 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf, + 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00, + 0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf, + 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1, + 0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf, + 0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf, + 0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07, + 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00, + 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00, + 0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02, + 0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, + 0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80, + 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, + 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08, + 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf, + 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf, + 0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf, + 0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07, + 0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1, + 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00, + 0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde, + 0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2, + 0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08, + 0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf, + 0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08, + 0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde, + 0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08, + 0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0, + 0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf, + 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57, + 0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0, + 0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67, + 0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00, + 0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0, + 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf, + 0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf, + 0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08, + 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0, + 0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00, + 0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08, + 0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00, + 0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07, + 0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00, + 0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09, + 0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07, + 0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1, + 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, + 0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07, + 0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08, + 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00, + 0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00, + 0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09, + 0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02, + 0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda, + 0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08, + 0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08, + 0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02, + 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1, + 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0, + 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08, + 0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, + 0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08, + 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08, + 0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07, + 0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, + 0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80, + 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, + 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, + 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00, + 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, + 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, + 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80, + 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07, + 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09, + 0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00, + 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0, + 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf, + 0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf, + 0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00, + 0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d, + 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1, + 0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87, + 0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08, + 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0, + 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0, + 0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87, + 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, + 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, + 0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0, + 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09, + 0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2, + 0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf, + 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02, + 0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08, + 0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77, + 0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf, + 0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e, + 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0, + 0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77, + 0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57, + 0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb, + 0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00, + 0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb, + 0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09, + 0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00, + 0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08, + 0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09, + 0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02, + 0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05, + 0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1, + 0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02, + 0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, + 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04, + 0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00, + 0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf, + 0, 0 +}; + +/* Firmware fixup (data?) segment */ +static unsigned char kue_fix_seg[] = +{ + /******************************************/ + /* NOTE: B6/C3 is data header signature */ + /* 0xAA/0xBB is data length = total */ + /* bytes - 7, 0xCC is type, 0xDD is */ + /* interrupt to use. */ + /******************************************/ + 0xB6, 0xC3, 0xc9, 0x02, 0x03, 0x64, + 0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00, + 0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00, + 0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00, + 0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00, + 0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00, + 0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00, + 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00, + 0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00, + 0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00, + 0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00, + 0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01, + 0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01, + 0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01, + 0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01, + 0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01, + 0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01, + 0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02, + 0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02, + 0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02, + 0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02, + 0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03, + 0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03, + 0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03, + 0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03, + 0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03, + 0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03, + 0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03, + 0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04, + 0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04, + 0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04, + 0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04, + 0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04, + 0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05, + 0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05, + 0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05, + 0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05, + 0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06, + 0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06, + 0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06, + 0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06, + 0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06, + 0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06, + 0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06, + 0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07, + 0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07, + 0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08, + 0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08, + 0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08, + 0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08, + 0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09, + 0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09, + 0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09, + 0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09, + 0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09, + 0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09, + 0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09, + 0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09, + 0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a, + 0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a, + 0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a, + 0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a, + 0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a, + 0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a, + 0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a, + 0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a, + 0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a, + 0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a, + 0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b, + 0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b, + 0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b, + 0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b, + 0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b, + 0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b, + 0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c, + 0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c, + 0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c, + 0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c, + 0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c, + 0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c, + 0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c, + 0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c, + 0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d, + 0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d, + 0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d, + 0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d, + 0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d, + 0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e, + 0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e, + 0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e, + 0, 0 +}; + +/* Fixup command. */ +#define KUE_TRIGCMD_OFFSET 5 +static unsigned char kue_trig_seg[] = { + 0xb6, 0xc3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00 +}; diff --git a/freebsd/sys/dev/usb/net/if_kuereg.h b/freebsd/sys/dev/usb/net/if_kuereg.h new file mode 100644 index 00000000..16ad044d --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_kuereg.h @@ -0,0 +1,141 @@ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Definitions for the KLSI KL5KUSB101B USB to ethernet controller. + * The KLSI part is controlled via vendor control requests, the structure + * of which depend a bit on the firmware running on the internal + * microcontroller. The one exception is the 'send scan data' command, + * which is used to load the firmware. + */ + +#define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00 +#define KUE_CMD_SET_MCAST_FILTERS 0x01 +#define KUE_CMD_SET_PKT_FILTER 0x02 +#define KUE_CMD_GET_ETHERSTATS 0x03 +#define KUE_CMD_GET_GPIO 0x04 +#define KUE_CMD_SET_GPIO 0x05 +#define KUE_CMD_SET_MAC 0x06 +#define KUE_CMD_GET_MAC 0x07 +#define KUE_CMD_SET_URB_SIZE 0x08 +#define KUE_CMD_SET_SOFS 0x09 +#define KUE_CMD_SET_EVEN_PKTS 0x0A +#define KUE_CMD_SEND_SCAN 0xFF + +struct kue_ether_desc { + uint8_t kue_len; + uint8_t kue_rsvd0; + uint8_t kue_rsvd1; + uint8_t kue_macaddr[ETHER_ADDR_LEN]; + uint8_t kue_etherstats[4]; + uint8_t kue_maxseg[2]; + uint8_t kue_mcastfilt[2]; + uint8_t kue_rsvd2; +} __packed; + +#define KUE_ETHERSTATS(x) UGETDW((x)->sc_desc.kue_etherstats) +#define KUE_MAXSEG(x) UGETW((x)->sc_desc.kue_maxseg) +#define KUE_MCFILTCNT(x) (UGETW((x)->sc_desc.kue_mcastfilt) & 0x7FFF) +#define KUE_MCFILT(x, y) \ + (char *)&(sc->sc_mcfilters[y * ETHER_ADDR_LEN]) + +#define KUE_STAT_TX_OK 0x00000001 +#define KUE_STAT_RX_OK 0x00000002 +#define KUE_STAT_TX_ERR 0x00000004 +#define KUE_STAT_RX_ERR 0x00000008 +#define KUE_STAT_RX_NOBUF 0x00000010 +#define KUE_STAT_TX_UCAST_BYTES 0x00000020 +#define KUE_STAT_TX_UCAST_FRAMES 0x00000040 +#define KUE_STAT_TX_MCAST_BYTES 0x00000080 +#define KUE_STAT_TX_MCAST_FRAMES 0x00000100 +#define KUE_STAT_TX_BCAST_BYTES 0x00000200 +#define KUE_STAT_TX_BCAST_FRAMES 0x00000400 +#define KUE_STAT_RX_UCAST_BYTES 0x00000800 +#define KUE_STAT_RX_UCAST_FRAMES 0x00001000 +#define KUE_STAT_RX_MCAST_BYTES 0x00002000 +#define KUE_STAT_RX_MCAST_FRAMES 0x00004000 +#define KUE_STAT_RX_BCAST_BYTES 0x00008000 +#define KUE_STAT_RX_BCAST_FRAMES 0x00010000 +#define KUE_STAT_RX_CRCERR 0x00020000 +#define KUE_STAT_TX_QUEUE_LENGTH 0x00040000 +#define KUE_STAT_RX_ALIGNERR 0x00080000 +#define KUE_STAT_TX_SINGLECOLL 0x00100000 +#define KUE_STAT_TX_MULTICOLL 0x00200000 +#define KUE_STAT_TX_DEFERRED 0x00400000 +#define KUE_STAT_TX_MAXCOLLS 0x00800000 +#define KUE_STAT_RX_OVERRUN 0x01000000 +#define KUE_STAT_TX_UNDERRUN 0x02000000 +#define KUE_STAT_TX_SQE_ERR 0x04000000 +#define KUE_STAT_TX_CARRLOSS 0x08000000 +#define KUE_STAT_RX_LATECOLL 0x10000000 + +#define KUE_RXFILT_PROMISC 0x0001 +#define KUE_RXFILT_ALLMULTI 0x0002 +#define KUE_RXFILT_UNICAST 0x0004 +#define KUE_RXFILT_BROADCAST 0x0008 +#define KUE_RXFILT_MULTICAST 0x0010 + +#define KUE_TIMEOUT 1000 +#define KUE_MIN_FRAMELEN 60 + +#define KUE_CTL_READ 0x01 +#define KUE_CTL_WRITE 0x02 + +#define KUE_CONFIG_IDX 0 /* config number 1 */ +#define KUE_IFACE_IDX 0 + +/* The interrupt endpoint is currently unused by the KLSI part. */ +#define KUE_ENDPT_MAX 4 +enum { + KUE_BULK_DT_WR, + KUE_BULK_DT_RD, + KUE_N_TRANSFER, +}; + +struct kue_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct kue_ether_desc sc_desc; + struct usb_xfer *sc_xfer[KUE_N_TRANSFER]; + uint8_t *sc_mcfilters; + + int sc_flags; +#define KUE_FLAG_LINK 0x0001 + + uint16_t sc_rxfilt; +}; + +#define KUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define KUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define KUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/if_mos.c b/freebsd/sys/dev/usb/net/if_mos.c new file mode 100644 index 00000000..4c997fa4 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_mos.c @@ -0,0 +1,1032 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2011 Rick van der Zwet <info@rickvanderzwet.nl> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 2008 Johann Christian Rode <jcrode@gmx.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 2005, 2006, 2007 Jonathan Gray <jsg@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 + * Bill Paul <wpaul@windriver.com>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Moschip MCS7730/MCS7830/MCS7832 USB to Ethernet controller + * The datasheet is available at the following URL: + * http://www.moschip.com/data/products/MCS7830/Data%20Sheet_7830.pdf + */ + +/* + * The FreeBSD if_mos.c driver is based on various different sources: + * The vendor provided driver at the following URL: + * http://www.moschip.com/data/products/MCS7830/Driver_FreeBSD_7830.tar.gz + * + * Mixed together with the OpenBSD if_mos.c driver for validation and checking + * and the FreeBSD if_reu.c as reference for the USB Ethernet framework. + */ + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR mos_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> + +//#include <dev/usb/net/if_mosreg.h> +#include "if_mosreg.h" + +#ifdef USB_DEBUG +static int mos_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, mos, CTLFLAG_RW, 0, "USB mos"); +SYSCTL_INT(_hw_usb_mos, OID_AUTO, debug, CTLFLAG_RWTUN, &mos_debug, 0, + "Debug level"); +#endif + +#define MOS_DPRINTFN(fmt,...) \ + DPRINTF("mos: %s: " fmt "\n",__FUNCTION__,## __VA_ARGS__) + +#define USB_PRODUCT_MOSCHIP_MCS7730 0x7730 +#define USB_PRODUCT_SITECOMEU_LN030 0x0021 + + + +/* Various supported device vendors/products. */ +static const STRUCT_USB_HOST_ID mos_devs[] = { + {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7730, MCS7730)}, + {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7830, MCS7830)}, + {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7832, MCS7832)}, + {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN030, MCS7830)}, +}; + +static int mos_probe(device_t dev); +static int mos_attach(device_t dev); +static void mos_attach_post(struct usb_ether *ue); +static int mos_detach(device_t dev); + +static void mos_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error); +static void mos_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error); +static void mos_intr_callback(struct usb_xfer *xfer, usb_error_t error); +static void mos_tick(struct usb_ether *); +static void mos_start(struct usb_ether *); +static void mos_init(struct usb_ether *); +static void mos_chip_init(struct mos_softc *); +static void mos_stop(struct usb_ether *); +static int mos_miibus_readreg(device_t, int, int); +static int mos_miibus_writereg(device_t, int, int, int); +static void mos_miibus_statchg(device_t); +static int mos_ifmedia_upd(struct ifnet *); +static void mos_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static void mos_reset(struct mos_softc *sc); + +static int mos_reg_read_1(struct mos_softc *, int); +static int mos_reg_read_2(struct mos_softc *, int); +static int mos_reg_write_1(struct mos_softc *, int, int); +static int mos_reg_write_2(struct mos_softc *, int, int); +static int mos_readmac(struct mos_softc *, uint8_t *); +static int mos_writemac(struct mos_softc *, uint8_t *); +static int mos_write_mcast(struct mos_softc *, u_char *); + +static void mos_setmulti(struct usb_ether *); +static void mos_setpromisc(struct usb_ether *); + +static const struct usb_config mos_config[MOS_ENDPT_MAX] = { + + [MOS_ENDPT_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (MCLBYTES + 2), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = mos_bulk_write_callback, + .timeout = 10000, + }, + + [MOS_ENDPT_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = mos_bulk_read_callback, + }, + + [MOS_ENDPT_INTR] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .bufsize = 0, + .callback = mos_intr_callback, + }, +}; + +static device_method_t mos_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mos_probe), + DEVMETHOD(device_attach, mos_attach), + DEVMETHOD(device_detach, mos_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, mos_miibus_readreg), + DEVMETHOD(miibus_writereg, mos_miibus_writereg), + DEVMETHOD(miibus_statchg, mos_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t mos_driver = { + .name = "mos", + .methods = mos_methods, + .size = sizeof(struct mos_softc) +}; + +static devclass_t mos_devclass; + +DRIVER_MODULE(mos, uhub, mos_driver, mos_devclass, NULL, 0); +DRIVER_MODULE(miibus, mos, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(mos, uether, 1, 1, 1); +MODULE_DEPEND(mos, usb, 1, 1, 1); +MODULE_DEPEND(mos, ether, 1, 1, 1); +MODULE_DEPEND(mos, miibus, 1, 1, 1); +USB_PNP_HOST_INFO(mos_devs); + +static const struct usb_ether_methods mos_ue_methods = { + .ue_attach_post = mos_attach_post, + .ue_start = mos_start, + .ue_init = mos_init, + .ue_stop = mos_stop, + .ue_tick = mos_tick, + .ue_setmulti = mos_setmulti, + .ue_setpromisc = mos_setpromisc, + .ue_mii_upd = mos_ifmedia_upd, + .ue_mii_sts = mos_ifmedia_sts, +}; + + +static int +mos_reg_read_1(struct mos_softc *sc, int reg) +{ + struct usb_device_request req; + usb_error_t err; + uByte val = 0; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = MOS_UR_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + err = uether_do_request(&sc->sc_ue, &req, &val, 1000); + + if (err) { + MOS_DPRINTFN("mos_reg_read_1 error, reg: %d\n", reg); + return (-1); + } + return (val); +} + +static int +mos_reg_read_2(struct mos_softc *sc, int reg) +{ + struct usb_device_request req; + usb_error_t err; + uWord val; + + USETW(val, 0); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = MOS_UR_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 2); + + err = uether_do_request(&sc->sc_ue, &req, &val, 1000); + + if (err) { + MOS_DPRINTFN("mos_reg_read_2 error, reg: %d", reg); + return (-1); + } + return (UGETW(val)); +} + +static int +mos_reg_write_1(struct mos_softc *sc, int reg, int aval) +{ + struct usb_device_request req; + usb_error_t err; + uByte val; + val = aval; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MOS_UR_WRITEREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 1); + + err = uether_do_request(&sc->sc_ue, &req, &val, 1000); + + if (err) { + MOS_DPRINTFN("mos_reg_write_1 error, reg: %d", reg); + return (-1); + } + return (0); +} + +static int +mos_reg_write_2(struct mos_softc *sc, int reg, int aval) +{ + struct usb_device_request req; + usb_error_t err; + uWord val; + + USETW(val, aval); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MOS_UR_WRITEREG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 2); + + err = uether_do_request(&sc->sc_ue, &req, &val, 1000); + + if (err) { + MOS_DPRINTFN("mos_reg_write_2 error, reg: %d", reg); + return (-1); + } + return (0); +} + +static int +mos_readmac(struct mos_softc *sc, u_char *mac) +{ + struct usb_device_request req; + usb_error_t err; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = MOS_UR_READREG; + USETW(req.wValue, 0); + USETW(req.wIndex, MOS_MAC); + USETW(req.wLength, ETHER_ADDR_LEN); + + err = uether_do_request(&sc->sc_ue, &req, mac, 1000); + + if (err) { + return (-1); + } + return (0); +} + +static int +mos_writemac(struct mos_softc *sc, uint8_t *mac) +{ + struct usb_device_request req; + usb_error_t err; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MOS_UR_WRITEREG; + USETW(req.wValue, 0); + USETW(req.wIndex, MOS_MAC); + USETW(req.wLength, ETHER_ADDR_LEN); + + err = uether_do_request(&sc->sc_ue, &req, mac, 1000); + + if (err) { + MOS_DPRINTFN("mos_writemac error"); + return (-1); + } + return (0); +} + +static int +mos_write_mcast(struct mos_softc *sc, u_char *hashtbl) +{ + struct usb_device_request req; + usb_error_t err; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MOS_UR_WRITEREG; + USETW(req.wValue, 0); + USETW(req.wIndex, MOS_MCAST_TABLE); + USETW(req.wLength, 8); + + err = uether_do_request(&sc->sc_ue, &req, hashtbl, 1000); + + if (err) { + MOS_DPRINTFN("mos_reg_mcast error"); + return (-1); + } + return (0); +} + +static int +mos_miibus_readreg(device_t dev, int phy, int reg) +{ + struct mos_softc *sc = device_get_softc(dev); + uWord val; + int i, res, locked; + + USETW(val, 0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + MOS_LOCK(sc); + + mos_reg_write_2(sc, MOS_PHY_DATA, 0); + mos_reg_write_1(sc, MOS_PHY_CTL, (phy & MOS_PHYCTL_PHYADDR) | + MOS_PHYCTL_READ); + mos_reg_write_1(sc, MOS_PHY_STS, (reg & MOS_PHYSTS_PHYREG) | + MOS_PHYSTS_PENDING); + + for (i = 0; i < MOS_TIMEOUT; i++) { + if (mos_reg_read_1(sc, MOS_PHY_STS) & MOS_PHYSTS_READY) + break; + } + if (i == MOS_TIMEOUT) { + MOS_DPRINTFN("MII read timeout"); + } + res = mos_reg_read_2(sc, MOS_PHY_DATA); + + if (!locked) + MOS_UNLOCK(sc); + return (res); +} + +static int +mos_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct mos_softc *sc = device_get_softc(dev); + int i, locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + MOS_LOCK(sc); + + mos_reg_write_2(sc, MOS_PHY_DATA, val); + mos_reg_write_1(sc, MOS_PHY_CTL, (phy & MOS_PHYCTL_PHYADDR) | + MOS_PHYCTL_WRITE); + mos_reg_write_1(sc, MOS_PHY_STS, (reg & MOS_PHYSTS_PHYREG) | + MOS_PHYSTS_PENDING); + + for (i = 0; i < MOS_TIMEOUT; i++) { + if (mos_reg_read_1(sc, MOS_PHY_STS) & MOS_PHYSTS_READY) + break; + } + if (i == MOS_TIMEOUT) + MOS_DPRINTFN("MII write timeout"); + + if (!locked) + MOS_UNLOCK(sc); + return 0; +} + +static void +mos_miibus_statchg(device_t dev) +{ + struct mos_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + int val, err, locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + MOS_LOCK(sc); + + /* disable RX, TX prior to changing FDX, SPEEDSEL */ + val = mos_reg_read_1(sc, MOS_CTL); + val &= ~(MOS_CTL_TX_ENB | MOS_CTL_RX_ENB); + mos_reg_write_1(sc, MOS_CTL, val); + + /* reset register which counts dropped frames */ + mos_reg_write_1(sc, MOS_FRAME_DROP_CNT, 0); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) + val |= MOS_CTL_FDX_ENB; + else + val &= ~(MOS_CTL_FDX_ENB); + + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_100_TX: + val |= MOS_CTL_SPEEDSEL; + break; + case IFM_10_T: + val &= ~(MOS_CTL_SPEEDSEL); + break; + } + + /* re-enable TX, RX */ + val |= (MOS_CTL_TX_ENB | MOS_CTL_RX_ENB); + err = mos_reg_write_1(sc, MOS_CTL, val); + + if (err) + MOS_DPRINTFN("media change failed"); + + if (!locked) + MOS_UNLOCK(sc); +} + +/* + * Set media options. + */ +static int +mos_ifmedia_upd(struct ifnet *ifp) +{ + struct mos_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + struct mii_softc *miisc; + int error; + + MOS_LOCK_ASSERT(sc, MA_OWNED); + + sc->mos_link = 0; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + return (error); +} + +/* + * Report current media status. + */ +static void +mos_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct mos_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + MOS_LOCK(sc); + mii_pollstat(mii); + + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + MOS_UNLOCK(sc); +} + +static void +mos_setpromisc(struct usb_ether *ue) +{ + struct mos_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + uint8_t rxmode; + + MOS_LOCK_ASSERT(sc, MA_OWNED); + + rxmode = mos_reg_read_1(sc, MOS_CTL); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + rxmode |= MOS_CTL_RX_PROMISC; + } else { + rxmode &= ~MOS_CTL_RX_PROMISC; + } + + mos_reg_write_1(sc, MOS_CTL, rxmode); +} + + + +static void +mos_setmulti(struct usb_ether *ue) +{ + struct mos_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + struct ifmultiaddr *ifma; + + uint32_t h = 0; + uint8_t rxmode; + uint8_t hashtbl[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int allmulti = 0; + + MOS_LOCK_ASSERT(sc, MA_OWNED); + + rxmode = mos_reg_read_1(sc, MOS_CTL); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) + allmulti = 1; + + /* get all new ones */ + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) { + allmulti = 1; + continue; + } + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + hashtbl[h / 8] |= 1 << (h % 8); + } + if_maddr_runlock(ifp); + + /* now program new ones */ + if (allmulti == 1) { + rxmode |= MOS_CTL_ALLMULTI; + mos_reg_write_1(sc, MOS_CTL, rxmode); + } else { + rxmode &= ~MOS_CTL_ALLMULTI; + mos_write_mcast(sc, (void *)&hashtbl); + mos_reg_write_1(sc, MOS_CTL, rxmode); + } +} + +static void +mos_reset(struct mos_softc *sc) +{ + uint8_t ctl; + + ctl = mos_reg_read_1(sc, MOS_CTL); + ctl &= ~(MOS_CTL_RX_PROMISC | MOS_CTL_ALLMULTI | MOS_CTL_TX_ENB | + MOS_CTL_RX_ENB); + /* Disable RX, TX, promiscuous and allmulticast mode */ + mos_reg_write_1(sc, MOS_CTL, ctl); + + /* Reset frame drop counter register to zero */ + mos_reg_write_1(sc, MOS_FRAME_DROP_CNT, 0); + + /* Wait a little while for the chip to get its brains in order. */ + usb_pause_mtx(&sc->sc_mtx, hz / 128); + return; +} + +static void +mos_chip_init(struct mos_softc *sc) +{ + int i; + + /* + * Rev.C devices have a pause threshold register which needs to be set + * at startup. + */ + if (mos_reg_read_1(sc, MOS_PAUSE_TRHD) != -1) { + for (i = 0; i < MOS_PAUSE_REWRITES; i++) + mos_reg_write_1(sc, MOS_PAUSE_TRHD, 0); + } + sc->mos_phyaddrs[0] = 1; + sc->mos_phyaddrs[1] = 0xFF; +} + +/* + * Probe for a MCS7x30 chip. + */ +static int +mos_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + int retval; + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != MOS_CONFIG_IDX) + return (ENXIO); + if (uaa->info.bIfaceIndex != MOS_IFACE_IDX) + return (ENXIO); + + retval = usbd_lookup_id_by_uaa(mos_devs, sizeof(mos_devs), uaa); + return (retval); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +mos_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct mos_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + sc->mos_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = MOS_IFACE_IDX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, mos_config, MOS_ENDPT_MAX, + sc, &sc->sc_mtx); + + if (error) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &mos_ue_methods; + + + if (sc->mos_flags & MCS7730) { + MOS_DPRINTFN("model: MCS7730"); + } else if (sc->mos_flags & MCS7830) { + MOS_DPRINTFN("model: MCS7830"); + } else if (sc->mos_flags & MCS7832) { + MOS_DPRINTFN("model: MCS7832"); + } + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); + + +detach: + mos_detach(dev); + return (ENXIO); +} + + +static void +mos_attach_post(struct usb_ether *ue) +{ + struct mos_softc *sc = uether_getsc(ue); + int err; + + /* Read MAC address, inform the world. */ + err = mos_readmac(sc, ue->ue_eaddr); + + if (err) + MOS_DPRINTFN("couldn't get MAC address"); + + MOS_DPRINTFN("address: %s", ether_sprintf(ue->ue_eaddr)); + + mos_chip_init(sc); +} + +static int +mos_detach(device_t dev) +{ + struct mos_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, MOS_ENDPT_MAX); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + + + + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void +mos_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct mos_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + + uint8_t rxstat = 0; + uint32_t actlen; + uint16_t pktlen = 0; + struct usb_page_cache *pc; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + pc = usbd_xfer_get_frame(xfer, 0); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + MOS_DPRINTFN("actlen : %d", actlen); + if (actlen <= 1) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + /* evaluate status byte at the end */ + usbd_copy_out(pc, actlen - sizeof(rxstat), &rxstat, + sizeof(rxstat)); + + if (rxstat != MOS_RXSTS_VALID) { + MOS_DPRINTFN("erroneous frame received"); + if (rxstat & MOS_RXSTS_SHORT_FRAME) + MOS_DPRINTFN("frame size less than 64 bytes"); + if (rxstat & MOS_RXSTS_LARGE_FRAME) { + MOS_DPRINTFN("frame size larger than " + "1532 bytes"); + } + if (rxstat & MOS_RXSTS_CRC_ERROR) + MOS_DPRINTFN("CRC error"); + if (rxstat & MOS_RXSTS_ALIGN_ERROR) + MOS_DPRINTFN("alignment error"); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + /* Remember the last byte was used for the status fields */ + pktlen = actlen - 1; + if (pktlen < sizeof(struct ether_header)) { + MOS_DPRINTFN("error: pktlen %d is smaller " + "than ether_header %zd", pktlen, + sizeof(struct ether_header)); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + uether_rxbuf(ue, pc, 0, actlen); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + default: + MOS_DPRINTFN("bulk read error, %s", usbd_errstr(error)); + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + MOS_DPRINTFN("start rx %i", usbd_xfer_max_len(xfer)); + return; + } +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ +static void +mos_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct mos_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + + + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + MOS_DPRINTFN("transfer of complete"); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + /* + * XXX: don't send anything if there is no link? + */ + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + return; + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); + + usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); + + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usbd_transfer_submit(xfer); + + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + return; + default: + MOS_DPRINTFN("usb error on tx: %s\n", usbd_errstr(error)); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +mos_tick(struct usb_ether *ue) +{ + struct mos_softc *sc = uether_getsc(ue); + struct mii_data *mii = GET_MII(sc); + + MOS_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if (!sc->mos_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + MOS_DPRINTFN("got link"); + sc->mos_link++; + mos_start(ue); + } +} + + +static void +mos_start(struct usb_ether *ue) +{ + struct mos_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[MOS_ENDPT_TX]); + usbd_transfer_start(sc->sc_xfer[MOS_ENDPT_RX]); + usbd_transfer_start(sc->sc_xfer[MOS_ENDPT_INTR]); +} + +static void +mos_init(struct usb_ether *ue) +{ + struct mos_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + uint8_t rxmode; + + MOS_LOCK_ASSERT(sc, MA_OWNED); + + /* Cancel pending I/O and free all RX/TX buffers. */ + mos_reset(sc); + + /* Write MAC address */ + mos_writemac(sc, IF_LLADDR(ifp)); + + /* Read and set transmitter IPG values */ + sc->mos_ipgs[0] = mos_reg_read_1(sc, MOS_IPG0); + sc->mos_ipgs[1] = mos_reg_read_1(sc, MOS_IPG1); + mos_reg_write_1(sc, MOS_IPG0, sc->mos_ipgs[0]); + mos_reg_write_1(sc, MOS_IPG1, sc->mos_ipgs[1]); + + /* + * Enable receiver and transmitter, bridge controls speed/duplex + * mode + */ + rxmode = mos_reg_read_1(sc, MOS_CTL); + rxmode |= MOS_CTL_RX_ENB | MOS_CTL_TX_ENB | MOS_CTL_BS_ENB; + rxmode &= ~(MOS_CTL_SLEEP); + + mos_setpromisc(ue); + + /* XXX: broadcast mode? */ + mos_reg_write_1(sc, MOS_CTL, rxmode); + + /* Load the multicast filter. */ + mos_setmulti(ue); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + mos_start(ue); +} + + +static void +mos_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct mos_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + uint32_t pkt; + int actlen; + + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + MOS_DPRINTFN("actlen %i", actlen); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &pkt, sizeof(pkt)); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + return; + default: + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +mos_stop(struct usb_ether *ue) +{ + struct mos_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + mos_reset(sc); + + MOS_LOCK_ASSERT(sc, MA_OWNED); + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + + /* stop all the transfers, if not already stopped */ + usbd_transfer_stop(sc->sc_xfer[MOS_ENDPT_TX]); + usbd_transfer_stop(sc->sc_xfer[MOS_ENDPT_RX]); + usbd_transfer_stop(sc->sc_xfer[MOS_ENDPT_INTR]); + + sc->mos_link = 0; +} diff --git a/freebsd/sys/dev/usb/net/if_mosreg.h b/freebsd/sys/dev/usb/net/if_mosreg.h new file mode 100644 index 00000000..c811d552 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_mosreg.h @@ -0,0 +1,176 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2010, 2011 Rick van der Zwet <info@rickvanderzwet.nl> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 2008 Johann Christian Rode <jcrode@gmx.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 1997, 1998, 1999, 2000-2003 + * Bill Paul <wpaul@windriver.com>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ravikanth. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul, THE VOICES IN HIS HEAD OR + * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * Register definitions for the Moschip MCS7x30 ethernet controller. + */ +#define MOS_MCAST_TABLE 0x00 +#define MOS_IPG0 0x08 +#define MOS_IPG1 0x09 +#define MOS_PHY_DATA0 0x0a +#define MOS_PHY_DATA1 0x0b +#define MOS_PHY_CTL 0x0c +#define MOS_PHY_STS 0x0d +#define MOS_PHY_DATA MOS_PHY_DATA0 +#define MOS_CTL 0x0e +#define MOS_MAC0 0x0f +#define MOS_MAC1 0x10 +#define MOS_MAC2 0x11 +#define MOS_MAC3 0x12 +#define MOS_MAC4 0x13 +#define MOS_MAC5 0x14 +#define MOS_MAC MOS_MAC0 +/* apparently only available on hardware rev. C */ +#define MOS_FRAME_DROP_CNT 0x15 +#define MOS_PAUSE_TRHD 0x16 + +#define MOS_PHYCTL_PHYADDR 0x1f +#define MOS_PHYCTL_WRITE 0x20 +#define MOS_PHYCTL_READ 0x40 + +#define MOS_PHYSTS_PHYREG 0x1f +#define MOS_PHYSTS_READY 0x40 +#define MOS_PHYSTS_PENDING 0x80 + +#define MOS_CTL_RX_PROMISC 0x01 +#define MOS_CTL_ALLMULTI 0x02 +#define MOS_CTL_SLEEP 0x04 +#define MOS_CTL_TX_ENB 0x08 +/* + * The documentation calls this bit 'reserved', but in the FreeBSD driver + * provided by the vendor, this enables the receiver. + */ +#define MOS_CTL_RX_ENB 0x10 +#define MOS_CTL_FDX_ENB 0x20 +/* 0 = 10 Mbps, 1 = 100 Mbps */ +#define MOS_CTL_SPEEDSEL 0x40 +/* 0 = PHY controls speed/duplex mode, 1 = bridge controls speed/duplex mode */ +#define MOS_CTL_BS_ENB 0x80 + +#define MOS_RXSTS_SHORT_FRAME 0x01 +#define MOS_RXSTS_LENGTH_ERROR 0x02 +#define MOS_RXSTS_ALIGN_ERROR 0x04 +#define MOS_RXSTS_CRC_ERROR 0x08 +#define MOS_RXSTS_LARGE_FRAME 0x10 +#define MOS_RXSTS_VALID 0x20 +/* + * The EtherType field of an Ethernet frame can contain values other than + * the frame length, hence length errors are ignored. + */ +#define MOS_RXSTS_MASK 0x3d + +#define MOS_PAUSE_TRHD_DEFAULT 0 +#define MOS_PAUSE_REWRITES 3 + +#define MOS_TIMEOUT 1000 + +#define MOS_RX_LIST_CNT 1 +#define MOS_TX_LIST_CNT 1 + +/* Maximum size of a fast ethernet frame plus one byte for the status */ +#define MOS_BUFSZ (ETHER_MAX_LEN+1) + +/* + * USB endpoints. + */ +#define MOS_ENDPT_RX 0 +#define MOS_ENDPT_TX 1 +#define MOS_ENDPT_INTR 2 +#define MOS_ENDPT_MAX 3 + +/* + * USB vendor requests. + */ +#define MOS_UR_READREG 0x0e +#define MOS_UR_WRITEREG 0x0d + +#define MOS_CONFIG_IDX 0 +#define MOS_IFACE_IDX 0 + +#define MCS7730 0x0001 +#define MCS7830 0x0002 +#define MCS7832 0x0004 + +#define MOS_INC(x, y) (x) = (x + 1) % y + +struct mos_softc { + struct usb_ether sc_ue; + struct ifnet ifp; + + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[MOS_ENDPT_MAX]; + + uint16_t mos_flags; + + int mos_link; + unsigned char mos_ipgs[2]; + unsigned char mos_phyaddrs[2]; +}; + +#define GET_MII(sc) uether_getmii(&(sc)->sc_ue) +#define MOS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define MOS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define MOS_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/if_rue.c b/freebsd/sys/dev/usb/net/if_rue.c new file mode 100644 index 00000000..d64e1e0a --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_rue.c @@ -0,0 +1,924 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>. + * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul <wpaul@ee.columbia.edu>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * RealTek RTL8150 USB to fast ethernet controller driver. + * Datasheet is available from + * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/. + */ + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR rue_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_ruereg.h> + +#ifdef USB_DEBUG +static int rue_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue"); +SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RWTUN, + &rue_debug, 0, "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ + +static const STRUCT_USB_HOST_ID rue_devs[] = { + {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX, 0)}, + {USB_VPI(USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100, 0)}, + {USB_VPI(USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01, 0)}, +}; + +/* prototypes */ + +static device_probe_t rue_probe; +static device_attach_t rue_attach; +static device_detach_t rue_detach; + +static miibus_readreg_t rue_miibus_readreg; +static miibus_writereg_t rue_miibus_writereg; +static miibus_statchg_t rue_miibus_statchg; + +static usb_callback_t rue_intr_callback; +static usb_callback_t rue_bulk_read_callback; +static usb_callback_t rue_bulk_write_callback; + +static uether_fn_t rue_attach_post; +static uether_fn_t rue_init; +static uether_fn_t rue_stop; +static uether_fn_t rue_start; +static uether_fn_t rue_tick; +static uether_fn_t rue_setmulti; +static uether_fn_t rue_setpromisc; + +static int rue_read_mem(struct rue_softc *, uint16_t, void *, int); +static int rue_write_mem(struct rue_softc *, uint16_t, void *, int); +static uint8_t rue_csr_read_1(struct rue_softc *, uint16_t); +static uint16_t rue_csr_read_2(struct rue_softc *, uint16_t); +static int rue_csr_write_1(struct rue_softc *, uint16_t, uint8_t); +static int rue_csr_write_2(struct rue_softc *, uint16_t, uint16_t); +static int rue_csr_write_4(struct rue_softc *, int, uint32_t); + +static void rue_reset(struct rue_softc *); +static int rue_ifmedia_upd(struct ifnet *); +static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static const struct usb_config rue_config[RUE_N_TRANSFER] = { + + [RUE_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MCLBYTES, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = rue_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [RUE_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (MCLBYTES + 4), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = rue_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, + + [RUE_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .bufsize = 0, /* use wMaxPacketSize */ + .callback = rue_intr_callback, + }, +}; + +static device_method_t rue_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rue_probe), + DEVMETHOD(device_attach, rue_attach), + DEVMETHOD(device_detach, rue_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, rue_miibus_readreg), + DEVMETHOD(miibus_writereg, rue_miibus_writereg), + DEVMETHOD(miibus_statchg, rue_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t rue_driver = { + .name = "rue", + .methods = rue_methods, + .size = sizeof(struct rue_softc), +}; + +static devclass_t rue_devclass; + +DRIVER_MODULE_ORDERED(rue, uhub, rue_driver, rue_devclass, NULL, NULL, + SI_ORDER_ANY); +DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, NULL, NULL); +MODULE_DEPEND(rue, uether, 1, 1, 1); +MODULE_DEPEND(rue, usb, 1, 1, 1); +MODULE_DEPEND(rue, ether, 1, 1, 1); +MODULE_DEPEND(rue, miibus, 1, 1, 1); +MODULE_VERSION(rue, 1); +USB_PNP_HOST_INFO(rue_devs); + +static const struct usb_ether_methods rue_ue_methods = { + .ue_attach_post = rue_attach_post, + .ue_start = rue_start, + .ue_init = rue_init, + .ue_stop = rue_stop, + .ue_tick = rue_tick, + .ue_setmulti = rue_setmulti, + .ue_setpromisc = rue_setpromisc, + .ue_mii_upd = rue_ifmedia_upd, + .ue_mii_sts = rue_ifmedia_sts, +}; + +#define RUE_SETBIT(sc, reg, x) \ + rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x)) + +#define RUE_CLRBIT(sc, reg, x) \ + rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x)) + +static int +rue_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, int len) +{ + struct usb_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = UR_SET_ADDRESS; + USETW(req.wValue, addr); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +rue_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, int len) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UR_SET_ADDRESS; + USETW(req.wValue, addr); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static uint8_t +rue_csr_read_1(struct rue_softc *sc, uint16_t reg) +{ + uint8_t val; + + rue_read_mem(sc, reg, &val, 1); + return (val); +} + +static uint16_t +rue_csr_read_2(struct rue_softc *sc, uint16_t reg) +{ + uint8_t val[2]; + + rue_read_mem(sc, reg, &val, 2); + return (UGETW(val)); +} + +static int +rue_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val) +{ + return (rue_write_mem(sc, reg, &val, 1)); +} + +static int +rue_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val) +{ + uint8_t temp[2]; + + USETW(temp, val); + return (rue_write_mem(sc, reg, &temp, 2)); +} + +static int +rue_csr_write_4(struct rue_softc *sc, int reg, uint32_t val) +{ + uint8_t temp[4]; + + USETDW(temp, val); + return (rue_write_mem(sc, reg, &temp, 4)); +} + +static int +rue_miibus_readreg(device_t dev, int phy, int reg) +{ + struct rue_softc *sc = device_get_softc(dev); + uint16_t rval; + uint16_t ruereg; + int locked; + + if (phy != 0) /* RTL8150 supports PHY == 0, only */ + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + RUE_LOCK(sc); + + switch (reg) { + case MII_BMCR: + ruereg = RUE_BMCR; + break; + case MII_BMSR: + ruereg = RUE_BMSR; + break; + case MII_ANAR: + ruereg = RUE_ANAR; + break; + case MII_ANER: + ruereg = RUE_AER; + break; + case MII_ANLPAR: + ruereg = RUE_ANLP; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + rval = 0; + goto done; + default: + if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) { + rval = rue_csr_read_1(sc, reg); + goto done; + } + device_printf(sc->sc_ue.ue_dev, "bad phy register\n"); + rval = 0; + goto done; + } + + rval = rue_csr_read_2(sc, ruereg); +done: + if (!locked) + RUE_UNLOCK(sc); + return (rval); +} + +static int +rue_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct rue_softc *sc = device_get_softc(dev); + uint16_t ruereg; + int locked; + + if (phy != 0) /* RTL8150 supports PHY == 0, only */ + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + RUE_LOCK(sc); + + switch (reg) { + case MII_BMCR: + ruereg = RUE_BMCR; + break; + case MII_BMSR: + ruereg = RUE_BMSR; + break; + case MII_ANAR: + ruereg = RUE_ANAR; + break; + case MII_ANER: + ruereg = RUE_AER; + break; + case MII_ANLPAR: + ruereg = RUE_ANLP; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + goto done; + default: + if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) { + rue_csr_write_1(sc, reg, data); + goto done; + } + device_printf(sc->sc_ue.ue_dev, " bad phy register\n"); + goto done; + } + rue_csr_write_2(sc, ruereg, data); +done: + if (!locked) + RUE_UNLOCK(sc); + return (0); +} + +static void +rue_miibus_statchg(device_t dev) +{ + /* + * When the code below is enabled the card starts doing weird + * things after link going from UP to DOWN and back UP. + * + * Looks like some of register writes below messes up PHY + * interface. + * + * No visible regressions were found after commenting this code + * out, so that disable it for good. + */ +#if 0 + struct rue_softc *sc = device_get_softc(dev); + struct mii_data *mii = GET_MII(sc); + uint16_t bmcr; + int locked; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + RUE_LOCK(sc); + + RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); + + bmcr = rue_csr_read_2(sc, RUE_BMCR); + + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) + bmcr |= RUE_BMCR_SPD_SET; + else + bmcr &= ~RUE_BMCR_SPD_SET; + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) + bmcr |= RUE_BMCR_DUPLEX; + else + bmcr &= ~RUE_BMCR_DUPLEX; + + rue_csr_write_2(sc, RUE_BMCR, bmcr); + + RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); + + if (!locked) + RUE_UNLOCK(sc); +#endif +} + +static void +rue_setpromisc(struct usb_ether *ue) +{ + struct rue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + RUE_SETBIT(sc, RUE_RCR, RUE_RCR_AAP); + else + RUE_CLRBIT(sc, RUE_RCR, RUE_RCR_AAP); +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void +rue_setmulti(struct usb_ether *ue) +{ + struct rue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + uint16_t rxcfg; + int h = 0; + uint32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + int mcnt = 0; + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + rxcfg = rue_csr_read_2(sc, RUE_RCR); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP); + rxcfg &= ~RUE_RCR_AM; + rue_csr_write_2(sc, RUE_RCR, rxcfg); + rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF); + rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF); + return; + } + + /* first, zot all the existing hash bits */ + rue_csr_write_4(sc, RUE_MAR0, 0); + rue_csr_write_4(sc, RUE_MAR4, 0); + + /* now program new ones */ + if_maddr_rlock(ifp); + TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + mcnt++; + } + if_maddr_runlock(ifp); + + if (mcnt) + rxcfg |= RUE_RCR_AM; + else + rxcfg &= ~RUE_RCR_AM; + + rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP); + + rue_csr_write_2(sc, RUE_RCR, rxcfg); + rue_csr_write_4(sc, RUE_MAR0, hashes[0]); + rue_csr_write_4(sc, RUE_MAR4, hashes[1]); +} + +static void +rue_reset(struct rue_softc *sc) +{ + int i; + + rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST); + + for (i = 0; i != RUE_TIMEOUT; i++) { + if (uether_pause(&sc->sc_ue, hz / 1000)) + break; + if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST)) + break; + } + if (i == RUE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "reset never completed\n"); + + uether_pause(&sc->sc_ue, hz / 100); +} + +static void +rue_attach_post(struct usb_ether *ue) +{ + struct rue_softc *sc = uether_getsc(ue); + + /* reset the adapter */ + rue_reset(sc); + + /* get station address from the EEPROM */ + rue_read_mem(sc, RUE_EEPROM_IDR0, ue->ue_eaddr, ETHER_ADDR_LEN); +} + +/* + * Probe for a RTL8150 chip. + */ +static int +rue_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != RUE_CONFIG_IDX) + return (ENXIO); + if (uaa->info.bIfaceIndex != RUE_IFACE_IDX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(rue_devs, sizeof(rue_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +rue_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct rue_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + device_set_usb_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = RUE_IFACE_IDX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, rue_config, RUE_N_TRANSFER, + sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &rue_ue_methods; + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + rue_detach(dev); + return (ENXIO); /* failure */ +} + +static int +rue_detach(device_t dev) +{ + struct rue_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, RUE_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +rue_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct rue_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct rue_intrpkt pkt; + struct usb_page_cache *pc; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && + actlen >= (int)sizeof(pkt)) { + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &pkt, sizeof(pkt)); + + if_inc_counter(ifp, IFCOUNTER_IERRORS, pkt.rue_rxlost_cnt); + if_inc_counter(ifp, IFCOUNTER_IERRORS, pkt.rue_crcerr_cnt); + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, pkt.rue_col_cnt); + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + return; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +rue_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct rue_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + struct usb_page_cache *pc; + uint16_t status; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (actlen < 4) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, actlen - 4, &status, sizeof(status)); + actlen -= 4; + + /* check receive packet was valid or not */ + status = le16toh(status); + if ((status & RUE_RXSTAT_VALID) == 0) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + uether_rxbuf(ue, pc, 0, actlen); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read error, %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +rue_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct rue_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + int temp_len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & RUE_FLAG_LINK) == 0) { + /* + * don't send anything if there is no link ! + */ + return; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + if (m->m_pkthdr.len > MCLBYTES) + m->m_pkthdr.len = MCLBYTES; + temp_len = m->m_pkthdr.len; + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); + + /* + * This is an undocumented behavior. + * RTL8150 chip doesn't send frame length smaller than + * RUE_MIN_FRAMELEN (60) byte packet. + */ + if (temp_len < RUE_MIN_FRAMELEN) { + usbd_frame_zero(pc, temp_len, + RUE_MIN_FRAMELEN - temp_len); + temp_len = RUE_MIN_FRAMELEN; + } + usbd_xfer_set_frame_len(xfer, 0, temp_len); + + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usbd_transfer_submit(xfer); + + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +rue_tick(struct usb_ether *ue) +{ + struct rue_softc *sc = uether_getsc(ue); + struct mii_data *mii = GET_MII(sc); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & RUE_FLAG_LINK) == 0 + && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sc_flags |= RUE_FLAG_LINK; + rue_start(ue); + } +} + +static void +rue_start(struct usb_ether *ue) +{ + struct rue_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[RUE_INTR_DT_RD]); + usbd_transfer_start(sc->sc_xfer[RUE_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[RUE_BULK_DT_WR]); +} + +static void +rue_init(struct usb_ether *ue) +{ + struct rue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Cancel pending I/O + */ + rue_reset(sc); + + /* Set MAC address */ + rue_write_mem(sc, RUE_IDR0, IF_LLADDR(ifp), ETHER_ADDR_LEN); + + rue_stop(ue); + + /* + * Set the initial TX and RX configuration. + */ + rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG); + rue_csr_write_2(sc, RUE_RCR, RUE_RCR_CONFIG|RUE_RCR_AB); + + /* Load the multicast filter */ + rue_setpromisc(ue); + /* Load the multicast filter. */ + rue_setmulti(ue); + + /* Enable RX and TX */ + rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN)); + + usbd_xfer_set_stall(sc->sc_xfer[RUE_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + rue_start(ue); +} + +/* + * Set media options. + */ +static int +rue_ifmedia_upd(struct ifnet *ifp) +{ + struct rue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + struct mii_softc *miisc; + int error; + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_flags &= ~RUE_FLAG_LINK; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + return (error); +} + +/* + * Report current media status. + */ +static void +rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct rue_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + RUE_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + RUE_UNLOCK(sc); +} + +static void +rue_stop(struct usb_ether *ue) +{ + struct rue_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + RUE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + sc->sc_flags &= ~RUE_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[RUE_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[RUE_BULK_DT_RD]); + usbd_transfer_stop(sc->sc_xfer[RUE_INTR_DT_RD]); + + rue_csr_write_1(sc, RUE_CR, 0x00); + + rue_reset(sc); +} diff --git a/freebsd/sys/dev/usb/net/if_ruereg.h b/freebsd/sys/dev/usb/net/if_ruereg.h new file mode 100644 index 00000000..c90a9692 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_ruereg.h @@ -0,0 +1,178 @@ +/*- + * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define RUE_CONFIG_IDX 0 /* config number 1 */ +#define RUE_IFACE_IDX 0 + +#define RUE_INTR_PKTLEN 0x8 + +#define RUE_TIMEOUT 50 +#define RUE_MIN_FRAMELEN 60 + +/* Registers. */ +#define RUE_IDR0 0x0120 +#define RUE_IDR1 0x0121 +#define RUE_IDR2 0x0122 +#define RUE_IDR3 0x0123 +#define RUE_IDR4 0x0124 +#define RUE_IDR5 0x0125 + +#define RUE_MAR0 0x0126 +#define RUE_MAR1 0x0127 +#define RUE_MAR2 0x0128 +#define RUE_MAR3 0x0129 +#define RUE_MAR4 0x012A +#define RUE_MAR5 0x012B +#define RUE_MAR6 0x012C +#define RUE_MAR7 0x012D + +#define RUE_CR 0x012E /* B, R/W */ +#define RUE_CR_SOFT_RST 0x10 +#define RUE_CR_RE 0x08 +#define RUE_CR_TE 0x04 +#define RUE_CR_EP3CLREN 0x02 + +#define RUE_TCR 0x012F /* B, R/W */ +#define RUE_TCR_TXRR1 0x80 +#define RUE_TCR_TXRR0 0x40 +#define RUE_TCR_IFG1 0x10 +#define RUE_TCR_IFG0 0x08 +#define RUE_TCR_NOCRC 0x01 +#define RUE_TCR_CONFIG (RUE_TCR_TXRR1 | RUE_TCR_TXRR0 | \ + RUE_TCR_IFG1 | RUE_TCR_IFG0) + +#define RUE_RCR 0x0130 /* W, R/W */ +#define RUE_RCR_TAIL 0x80 +#define RUE_RCR_AER 0x40 +#define RUE_RCR_AR 0x20 +#define RUE_RCR_AM 0x10 +#define RUE_RCR_AB 0x08 +#define RUE_RCR_AD 0x04 +#define RUE_RCR_AAM 0x02 +#define RUE_RCR_AAP 0x01 +#define RUE_RCR_CONFIG (RUE_RCR_TAIL | RUE_RCR_AD) + +#define RUE_TSR 0x0132 +#define RUE_RSR 0x0133 +#define RUE_CON0 0x0135 +#define RUE_CON1 0x0136 +#define RUE_MSR 0x0137 +#define RUE_PHYADD 0x0138 +#define RUE_PHYDAT 0x0139 + +#define RUE_PHYCNT 0x013B /* B, R/W */ +#define RUE_PHYCNT_PHYOWN 0x40 +#define RUE_PHYCNT_RWCR 0x20 + +#define RUE_GPPC 0x013D +#define RUE_WAKECNT 0x013E + +#define RUE_BMCR 0x0140 +#define RUE_BMCR_SPD_SET 0x2000 +#define RUE_BMCR_DUPLEX 0x0100 + +#define RUE_BMSR 0x0142 + +#define RUE_ANAR 0x0144 /* W, R/W */ +#define RUE_ANAR_PAUSE 0x0400 + +#define RUE_ANLP 0x0146 /* W, R/O */ +#define RUE_ANLP_PAUSE 0x0400 + +#define RUE_AER 0x0148 + +#define RUE_NWAYT 0x014A +#define RUE_CSCR 0x014C + +#define RUE_CRC0 0x014E +#define RUE_CRC1 0x0150 +#define RUE_CRC2 0x0152 +#define RUE_CRC3 0x0154 +#define RUE_CRC4 0x0156 + +#define RUE_BYTEMASK0 0x0158 +#define RUE_BYTEMASK1 0x0160 +#define RUE_BYTEMASK2 0x0168 +#define RUE_BYTEMASK3 0x0170 +#define RUE_BYTEMASK4 0x0178 + +#define RUE_PHY1 0x0180 +#define RUE_PHY2 0x0184 + +#define RUE_TW1 0x0186 + +#define RUE_REG_MIN 0x0120 +#define RUE_REG_MAX 0x0189 + +/* EEPROM address declarations. */ +#define RUE_EEPROM_BASE 0x1200 +#define RUE_EEPROM_IDR0 (RUE_EEPROM_BASE + 0x02) +#define RUE_EEPROM_IDR1 (RUE_EEPROM_BASE + 0x03) +#define RUE_EEPROM_IDR2 (RUE_EEPROM_BASE + 0x03) +#define RUE_EEPROM_IDR3 (RUE_EEPROM_BASE + 0x03) +#define RUE_EEPROM_IDR4 (RUE_EEPROM_BASE + 0x03) +#define RUE_EEPROM_IDR5 (RUE_EEPROM_BASE + 0x03) +#define RUE_EEPROM_INTERVAL (RUE_EEPROM_BASE + 0x17) + +#define RUE_RXSTAT_VALID (0x01 << 12) +#define RUE_RXSTAT_RUNT (0x02 << 12) +#define RUE_RXSTAT_PMATCH (0x04 << 12) +#define RUE_RXSTAT_MCAST (0x08 << 12) + +#define GET_MII(sc) uether_getmii(&(sc)->sc_ue) + +struct rue_intrpkt { + uint8_t rue_tsr; + uint8_t rue_rsr; + uint8_t rue_gep_msr; + uint8_t rue_waksr; + uint8_t rue_txok_cnt; + uint8_t rue_rxlost_cnt; + uint8_t rue_crcerr_cnt; + uint8_t rue_col_cnt; +} __packed; + +enum { + RUE_BULK_DT_WR, + RUE_BULK_DT_RD, + RUE_INTR_DT_RD, + RUE_N_TRANSFER, +}; + +struct rue_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[RUE_N_TRANSFER]; + + int sc_flags; +#define RUE_FLAG_LINK 0x0001 +}; + +#define RUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define RUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/if_smsc.c b/freebsd/sys/dev/usb/net/if_smsc.c new file mode 100644 index 00000000..5176fda9 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_smsc.c @@ -0,0 +1,1931 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2012 + * Ben Gray <bgray@freebsd.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * SMSC LAN9xxx devices (http://www.smsc.com/) + * + * The LAN9500 & LAN9500A devices are stand-alone USB to Ethernet chips that + * support USB 2.0 and 10/100 Mbps Ethernet. + * + * The LAN951x devices are an integrated USB hub and USB to Ethernet adapter. + * The driver only covers the Ethernet part, the standard USB hub driver + * supports the hub part. + * + * This driver is closely modelled on the Linux driver written and copyrighted + * by SMSC. + * + * + * + * + * H/W TCP & UDP Checksum Offloading + * --------------------------------- + * The chip supports both tx and rx offloading of UDP & TCP checksums, this + * feature can be dynamically enabled/disabled. + * + * RX checksuming is performed across bytes after the IPv4 header to the end of + * the Ethernet frame, this means if the frame is padded with non-zero values + * the H/W checksum will be incorrect, however the rx code compensates for this. + * + * TX checksuming is more complicated, the device requires a special header to + * be prefixed onto the start of the frame which indicates the start and end + * positions of the UDP or TCP frame. This requires the driver to manually + * go through the packet data and decode the headers prior to sending. + * On Linux they generally provide cues to the location of the csum and the + * area to calculate it over, on FreeBSD we seem to have to do it all ourselves, + * hence this is not as optimal and therefore h/w tX checksum is currently not + * implemented. + * + */ +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> +#include <sys/random.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <netinet/in.h> +#include <netinet/ip.h> + +#include <rtems/bsd/local/opt_platform.h> + +#ifdef FDT +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#endif + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR smsc_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> + +#include <dev/usb/net/if_smscreg.h> + +#ifdef USB_DEBUG +static int smsc_debug = 0; + +SYSCTL_NODE(_hw_usb, OID_AUTO, smsc, CTLFLAG_RW, 0, "USB smsc"); +SYSCTL_INT(_hw_usb_smsc, OID_AUTO, debug, CTLFLAG_RWTUN, &smsc_debug, 0, + "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ +static const struct usb_device_id smsc_devs[] = { +#define SMSC_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, i) } + SMSC_DEV(LAN89530_ETH, 0), + SMSC_DEV(LAN9500_ETH, 0), + SMSC_DEV(LAN9500_ETH_2, 0), + SMSC_DEV(LAN9500A_ETH, 0), + SMSC_DEV(LAN9500A_ETH_2, 0), + SMSC_DEV(LAN9505_ETH, 0), + SMSC_DEV(LAN9505A_ETH, 0), + SMSC_DEV(LAN9514_ETH, 0), + SMSC_DEV(LAN9514_ETH_2, 0), + SMSC_DEV(LAN9530_ETH, 0), + SMSC_DEV(LAN9730_ETH, 0), + SMSC_DEV(LAN9500_SAL10, 0), + SMSC_DEV(LAN9505_SAL10, 0), + SMSC_DEV(LAN9500A_SAL10, 0), + SMSC_DEV(LAN9505A_SAL10, 0), + SMSC_DEV(LAN9514_SAL10, 0), + SMSC_DEV(LAN9500A_HAL, 0), + SMSC_DEV(LAN9505A_HAL, 0), +#undef SMSC_DEV +}; + + +#ifdef USB_DEBUG +#define smsc_dbg_printf(sc, fmt, args...) \ + do { \ + if (smsc_debug > 0) \ + device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, ##args); \ + } while(0) +#else +#define smsc_dbg_printf(sc, fmt, args...) do { } while (0) +#endif + +#define smsc_warn_printf(sc, fmt, args...) \ + device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args) + +#define smsc_err_printf(sc, fmt, args...) \ + device_printf((sc)->sc_ue.ue_dev, "error: " fmt, ##args) + + +#define ETHER_IS_ZERO(addr) \ + (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5])) + +#define ETHER_IS_VALID(addr) \ + (!ETHER_IS_MULTICAST(addr) && !ETHER_IS_ZERO(addr)) + +static device_probe_t smsc_probe; +static device_attach_t smsc_attach; +static device_detach_t smsc_detach; + +static usb_callback_t smsc_bulk_read_callback; +static usb_callback_t smsc_bulk_write_callback; + +static miibus_readreg_t smsc_miibus_readreg; +static miibus_writereg_t smsc_miibus_writereg; +static miibus_statchg_t smsc_miibus_statchg; + +#if __FreeBSD_version > 1000000 +static int smsc_attach_post_sub(struct usb_ether *ue); +#endif +static uether_fn_t smsc_attach_post; +static uether_fn_t smsc_init; +static uether_fn_t smsc_stop; +static uether_fn_t smsc_start; +static uether_fn_t smsc_tick; +static uether_fn_t smsc_setmulti; +static uether_fn_t smsc_setpromisc; + +static int smsc_ifmedia_upd(struct ifnet *); +static void smsc_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static int smsc_chip_init(struct smsc_softc *sc); +static int smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); + +static const struct usb_config smsc_config[SMSC_N_TRANSFER] = { + + [SMSC_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .frames = 16, + .bufsize = 16 * (MCLBYTES + 16), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = smsc_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [SMSC_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = 20480, /* bytes */ + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = smsc_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, + + /* The SMSC chip supports an interrupt endpoints, however they aren't + * needed as we poll on the MII status. + */ +}; + +static const struct usb_ether_methods smsc_ue_methods = { + .ue_attach_post = smsc_attach_post, +#if __FreeBSD_version > 1000000 + .ue_attach_post_sub = smsc_attach_post_sub, +#endif + .ue_start = smsc_start, + .ue_ioctl = smsc_ioctl, + .ue_init = smsc_init, + .ue_stop = smsc_stop, + .ue_tick = smsc_tick, + .ue_setmulti = smsc_setmulti, + .ue_setpromisc = smsc_setpromisc, + .ue_mii_upd = smsc_ifmedia_upd, + .ue_mii_sts = smsc_ifmedia_sts, +}; + +/** + * smsc_read_reg - Reads a 32-bit register on the device + * @sc: driver soft context + * @off: offset of the register + * @data: pointer a value that will be populated with the register value + * + * LOCKING: + * The device lock must be held before calling this function. + * + * RETURNS: + * 0 on success, a USB_ERR_?? error code on failure. + */ +static int +smsc_read_reg(struct smsc_softc *sc, uint32_t off, uint32_t *data) +{ + struct usb_device_request req; + uint32_t buf; + usb_error_t err; + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = SMSC_UR_READ_REG; + USETW(req.wValue, 0); + USETW(req.wIndex, off); + USETW(req.wLength, 4); + + err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); + if (err != 0) + smsc_warn_printf(sc, "Failed to read register 0x%0x\n", off); + + *data = le32toh(buf); + + return (err); +} + +/** + * smsc_write_reg - Writes a 32-bit register on the device + * @sc: driver soft context + * @off: offset of the register + * @data: the 32-bit value to write into the register + * + * LOCKING: + * The device lock must be held before calling this function. + * + * RETURNS: + * 0 on success, a USB_ERR_?? error code on failure. + */ +static int +smsc_write_reg(struct smsc_softc *sc, uint32_t off, uint32_t data) +{ + struct usb_device_request req; + uint32_t buf; + usb_error_t err; + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + buf = htole32(data); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = SMSC_UR_WRITE_REG; + USETW(req.wValue, 0); + USETW(req.wIndex, off); + USETW(req.wLength, 4); + + err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); + if (err != 0) + smsc_warn_printf(sc, "Failed to write register 0x%0x\n", off); + + return (err); +} + +/** + * smsc_wait_for_bits - Polls on a register value until bits are cleared + * @sc: soft context + * @reg: offset of the register + * @bits: if the bits are clear the function returns + * + * LOCKING: + * The device lock must be held before calling this function. + * + * RETURNS: + * 0 on success, or a USB_ERR_?? error code on failure. + */ +static int +smsc_wait_for_bits(struct smsc_softc *sc, uint32_t reg, uint32_t bits) +{ + usb_ticks_t start_ticks; + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); + uint32_t val; + int err; + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + start_ticks = (usb_ticks_t)ticks; + do { + if ((err = smsc_read_reg(sc, reg, &val)) != 0) + return (err); + if (!(val & bits)) + return (0); + + uether_pause(&sc->sc_ue, hz / 100); + } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); + + return (USB_ERR_TIMEOUT); +} + +/** + * smsc_eeprom_read - Reads the attached EEPROM + * @sc: soft context + * @off: the eeprom address offset + * @buf: stores the bytes + * @buflen: the number of bytes to read + * + * Simply reads bytes from an attached eeprom. + * + * LOCKING: + * The function takes and releases the device lock if it is not already held. + * + * RETURNS: + * 0 on success, or a USB_ERR_?? error code on failure. + */ +static int +smsc_eeprom_read(struct smsc_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) +{ + usb_ticks_t start_ticks; + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); + int err; + int locked; + uint32_t val; + uint16_t i; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); + + err = smsc_wait_for_bits(sc, SMSC_EEPROM_CMD, SMSC_EEPROM_CMD_BUSY); + if (err != 0) { + smsc_warn_printf(sc, "eeprom busy, failed to read data\n"); + goto done; + } + + /* start reading the bytes, one at a time */ + for (i = 0; i < buflen; i++) { + + val = SMSC_EEPROM_CMD_BUSY | (SMSC_EEPROM_CMD_ADDR_MASK & (off + i)); + if ((err = smsc_write_reg(sc, SMSC_EEPROM_CMD, val)) != 0) + goto done; + + start_ticks = (usb_ticks_t)ticks; + do { + if ((err = smsc_read_reg(sc, SMSC_EEPROM_CMD, &val)) != 0) + goto done; + if (!(val & SMSC_EEPROM_CMD_BUSY) || (val & SMSC_EEPROM_CMD_TIMEOUT)) + break; + + uether_pause(&sc->sc_ue, hz / 100); + } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); + + if (val & (SMSC_EEPROM_CMD_BUSY | SMSC_EEPROM_CMD_TIMEOUT)) { + smsc_warn_printf(sc, "eeprom command failed\n"); + err = USB_ERR_IOERROR; + break; + } + + if ((err = smsc_read_reg(sc, SMSC_EEPROM_DATA, &val)) != 0) + goto done; + + buf[i] = (val & 0xff); + } + +done: + if (!locked) + SMSC_UNLOCK(sc); + + return (err); +} + +/** + * smsc_miibus_readreg - Reads a MII/MDIO register + * @dev: usb ether device + * @phy: the number of phy reading from + * @reg: the register address + * + * Attempts to read a phy register over the MII bus. + * + * LOCKING: + * Takes and releases the device mutex lock if not already held. + * + * RETURNS: + * Returns the 16-bits read from the MII register, if this function fails 0 + * is returned. + */ +static int +smsc_miibus_readreg(device_t dev, int phy, int reg) +{ + struct smsc_softc *sc = device_get_softc(dev); + int locked; + uint32_t addr; + uint32_t val = 0; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); + + if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) { + smsc_warn_printf(sc, "MII is busy\n"); + goto done; + } + + addr = (phy << 11) | (reg << 6) | SMSC_MII_READ; + smsc_write_reg(sc, SMSC_MII_ADDR, addr); + + if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) + smsc_warn_printf(sc, "MII read timeout\n"); + + smsc_read_reg(sc, SMSC_MII_DATA, &val); + val = le32toh(val); + +done: + if (!locked) + SMSC_UNLOCK(sc); + + return (val & 0xFFFF); +} + +/** + * smsc_miibus_writereg - Writes a MII/MDIO register + * @dev: usb ether device + * @phy: the number of phy writing to + * @reg: the register address + * @val: the value to write + * + * Attempts to write a phy register over the MII bus. + * + * LOCKING: + * Takes and releases the device mutex lock if not already held. + * + * RETURNS: + * Always returns 0 regardless of success or failure. + */ +static int +smsc_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct smsc_softc *sc = device_get_softc(dev); + int locked; + uint32_t addr; + + if (sc->sc_phyno != phy) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); + + if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) { + smsc_warn_printf(sc, "MII is busy\n"); + goto done; + } + + val = htole32(val); + smsc_write_reg(sc, SMSC_MII_DATA, val); + + addr = (phy << 11) | (reg << 6) | SMSC_MII_WRITE; + smsc_write_reg(sc, SMSC_MII_ADDR, addr); + + if (smsc_wait_for_bits(sc, SMSC_MII_ADDR, SMSC_MII_BUSY) != 0) + smsc_warn_printf(sc, "MII write timeout\n"); + +done: + if (!locked) + SMSC_UNLOCK(sc); + return (0); +} + + + +/** + * smsc_miibus_statchg - Called to detect phy status change + * @dev: usb ether device + * + * This function is called periodically by the system to poll for status + * changes of the link. + * + * LOCKING: + * Takes and releases the device mutex lock if not already held. + */ +static void +smsc_miibus_statchg(device_t dev) +{ + struct smsc_softc *sc = device_get_softc(dev); + struct mii_data *mii = uether_getmii(&sc->sc_ue); + struct ifnet *ifp; + int locked; + int err; + uint32_t flow; + uint32_t afc_cfg; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); + + ifp = uether_getifp(&sc->sc_ue); + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done; + + /* Use the MII status to determine link status */ + sc->sc_flags &= ~SMSC_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->sc_flags |= SMSC_FLAG_LINK; + break; + case IFM_1000_T: + /* Gigabit ethernet not supported by chipset */ + break; + default: + break; + } + } + + /* Lost link, do nothing. */ + if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { + smsc_dbg_printf(sc, "link flag not set\n"); + goto done; + } + + err = smsc_read_reg(sc, SMSC_AFC_CFG, &afc_cfg); + if (err) { + smsc_warn_printf(sc, "failed to read initial AFC_CFG, error %d\n", err); + goto done; + } + + /* Enable/disable full duplex operation and TX/RX pause */ + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { + smsc_dbg_printf(sc, "full duplex operation\n"); + sc->sc_mac_csr &= ~SMSC_MAC_CSR_RCVOWN; + sc->sc_mac_csr |= SMSC_MAC_CSR_FDPX; + + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) + flow = 0xffff0002; + else + flow = 0; + + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) + afc_cfg |= 0xf; + else + afc_cfg &= ~0xf; + + } else { + smsc_dbg_printf(sc, "half duplex operation\n"); + sc->sc_mac_csr &= ~SMSC_MAC_CSR_FDPX; + sc->sc_mac_csr |= SMSC_MAC_CSR_RCVOWN; + + flow = 0; + afc_cfg |= 0xf; + } + + err = smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); + err += smsc_write_reg(sc, SMSC_FLOW, flow); + err += smsc_write_reg(sc, SMSC_AFC_CFG, afc_cfg); + if (err) + smsc_warn_printf(sc, "media change failed, error %d\n", err); + +done: + if (!locked) + SMSC_UNLOCK(sc); +} + +/** + * smsc_ifmedia_upd - Set media options + * @ifp: interface pointer + * + * Basically boilerplate code that simply calls the mii functions to set the + * media options. + * + * LOCKING: + * The device lock must be held before this function is called. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_ifmedia_upd(struct ifnet *ifp) +{ + struct smsc_softc *sc = ifp->if_softc; + struct mii_data *mii = uether_getmii(&sc->sc_ue); + struct mii_softc *miisc; + int err; + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + err = mii_mediachg(mii); + return (err); +} + +/** + * smsc_ifmedia_sts - Report current media status + * @ifp: inet interface pointer + * @ifmr: interface media request + * + * Basically boilerplate code that simply calls the mii functions to get the + * media status. + * + * LOCKING: + * Internally takes and releases the device lock. + */ +static void +smsc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct smsc_softc *sc = ifp->if_softc; + struct mii_data *mii = uether_getmii(&sc->sc_ue); + + SMSC_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + SMSC_UNLOCK(sc); +} + +/** + * smsc_hash - Calculate the hash of a mac address + * @addr: The mac address to calculate the hash on + * + * This function is used when configuring a range of m'cast mac addresses to + * filter on. The hash of the mac address is put in the device's mac hash + * table. + * + * RETURNS: + * Returns a value from 0-63 value which is the hash of the mac address. + */ +static inline uint32_t +smsc_hash(uint8_t addr[ETHER_ADDR_LEN]) +{ + return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26) & 0x3f; +} + +/** + * smsc_setmulti - Setup multicast + * @ue: usb ethernet device context + * + * Tells the device to either accept frames with a multicast mac address, a + * select group of m'cast mac addresses or just the devices mac address. + * + * LOCKING: + * Should be called with the SMSC lock held. + */ +static void +smsc_setmulti(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t hashtbl[2] = { 0, 0 }; + uint32_t hash; + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { + smsc_dbg_printf(sc, "receive all multicast enabled\n"); + sc->sc_mac_csr |= SMSC_MAC_CSR_MCPAS; + sc->sc_mac_csr &= ~SMSC_MAC_CSR_HPFILT; + + } else { + /* Take the lock of the mac address list before hashing each of them */ + if_maddr_rlock(ifp); + + if (!TAILQ_EMPTY(&ifp->if_multiaddrs)) { + /* We are filtering on a set of address so calculate hashes of each + * of the address and set the corresponding bits in the register. + */ + sc->sc_mac_csr |= SMSC_MAC_CSR_HPFILT; + sc->sc_mac_csr &= ~(SMSC_MAC_CSR_PRMS | SMSC_MAC_CSR_MCPAS); + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + hash = smsc_hash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + hashtbl[hash >> 5] |= 1 << (hash & 0x1F); + } + } else { + /* Only receive packets with destination set to our mac address */ + sc->sc_mac_csr &= ~(SMSC_MAC_CSR_MCPAS | SMSC_MAC_CSR_HPFILT); + } + + if_maddr_runlock(ifp); + + /* Debug */ + if (sc->sc_mac_csr & SMSC_MAC_CSR_HPFILT) + smsc_dbg_printf(sc, "receive select group of macs\n"); + else + smsc_dbg_printf(sc, "receive own packets only\n"); + } + + /* Write the hash table and mac control registers */ + smsc_write_reg(sc, SMSC_HASHH, hashtbl[1]); + smsc_write_reg(sc, SMSC_HASHL, hashtbl[0]); + smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); +} + + +/** + * smsc_setpromisc - Enables/disables promiscuous mode + * @ue: usb ethernet device context + * + * LOCKING: + * Should be called with the SMSC lock held. + */ +static void +smsc_setpromisc(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + smsc_dbg_printf(sc, "promiscuous mode %sabled\n", + (ifp->if_flags & IFF_PROMISC) ? "en" : "dis"); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_PROMISC) + sc->sc_mac_csr |= SMSC_MAC_CSR_PRMS; + else + sc->sc_mac_csr &= ~SMSC_MAC_CSR_PRMS; + + smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); +} + + +/** + * smsc_sethwcsum - Enable or disable H/W UDP and TCP checksumming + * @sc: driver soft context + * + * LOCKING: + * Should be called with the SMSC lock held. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int smsc_sethwcsum(struct smsc_softc *sc) +{ + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + uint32_t val; + int err; + + if (!ifp) + return (-EIO); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + err = smsc_read_reg(sc, SMSC_COE_CTRL, &val); + if (err != 0) { + smsc_warn_printf(sc, "failed to read SMSC_COE_CTRL (err=%d)\n", err); + return (err); + } + + /* Enable/disable the Rx checksum */ + if ((ifp->if_capabilities & ifp->if_capenable) & IFCAP_RXCSUM) + val |= SMSC_COE_CTRL_RX_EN; + else + val &= ~SMSC_COE_CTRL_RX_EN; + + /* Enable/disable the Tx checksum (currently not supported) */ + if ((ifp->if_capabilities & ifp->if_capenable) & IFCAP_TXCSUM) + val |= SMSC_COE_CTRL_TX_EN; + else + val &= ~SMSC_COE_CTRL_TX_EN; + + err = smsc_write_reg(sc, SMSC_COE_CTRL, val); + if (err != 0) { + smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n", err); + return (err); + } + + return (0); +} + +/** + * smsc_setmacaddress - Sets the mac address in the device + * @sc: driver soft context + * @addr: pointer to array contain at least 6 bytes of the mac + * + * Writes the MAC address into the device, usually the MAC is programmed with + * values from the EEPROM. + * + * LOCKING: + * Should be called with the SMSC lock held. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_setmacaddress(struct smsc_softc *sc, const uint8_t *addr) +{ + int err; + uint32_t val; + + smsc_dbg_printf(sc, "setting mac address to %02x:%02x:%02x:%02x:%02x:%02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + if ((err = smsc_write_reg(sc, SMSC_MAC_ADDRL, val)) != 0) + goto done; + + val = (addr[5] << 8) | addr[4]; + err = smsc_write_reg(sc, SMSC_MAC_ADDRH, val); + +done: + return (err); +} + +/** + * smsc_reset - Reset the SMSC chip + * @sc: device soft context + * + * LOCKING: + * Should be called with the SMSC lock held. + */ +static void +smsc_reset(struct smsc_softc *sc) +{ + struct usb_config_descriptor *cd; + usb_error_t err; + + cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); + + err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (err) + smsc_warn_printf(sc, "reset failed (ignored)\n"); + + /* Wait a little while for the chip to get its brains in order. */ + uether_pause(&sc->sc_ue, hz / 100); + + /* Reinitialize controller to achieve full reset. */ + smsc_chip_init(sc); +} + + +/** + * smsc_init - Initialises the LAN95xx chip + * @ue: USB ether interface + * + * Called when the interface is brought up (i.e. ifconfig ue0 up), this + * initialise the interface and the rx/tx pipes. + * + * LOCKING: + * Should be called with the SMSC lock held. + */ +static void +smsc_init(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + if (smsc_setmacaddress(sc, IF_LLADDR(ifp))) + smsc_dbg_printf(sc, "setting MAC address failed\n"); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + /* Cancel pending I/O */ + smsc_stop(ue); + +#if __FreeBSD_version <= 1000000 + /* On earlier versions this was the first place we could tell the system + * that we supported h/w csuming, however this is only called after the + * the interface has been brought up - not ideal. + */ + if (!(ifp->if_capabilities & IFCAP_RXCSUM)) { + ifp->if_capabilities |= IFCAP_RXCSUM; + ifp->if_capenable |= IFCAP_RXCSUM; + ifp->if_hwassist = 0; + } + + /* TX checksuming is disabled for now + ifp->if_capabilities |= IFCAP_TXCSUM; + ifp->if_capenable |= IFCAP_TXCSUM; + ifp->if_hwassist = CSUM_TCP | CSUM_UDP; + */ +#endif + + /* Reset the ethernet interface. */ + smsc_reset(sc); + + /* Load the multicast filter. */ + smsc_setmulti(ue); + + /* TCP/UDP checksum offload engines. */ + smsc_sethwcsum(sc); + + usbd_xfer_set_stall(sc->sc_xfer[SMSC_BULK_DT_WR]); + + /* Indicate we are up and running. */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + /* Switch to selected media. */ + smsc_ifmedia_upd(ifp); + smsc_start(ue); +} + +/** + * smsc_bulk_read_callback - Read callback used to process the USB URB + * @xfer: the USB transfer + * @error: + * + * Reads the URB data which can contain one or more ethernet frames, the + * frames are copyed into a mbuf and given to the system. + * + * LOCKING: + * No locking required, doesn't access internal driver settings. + */ +static void +smsc_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct smsc_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + struct mbuf *m; + struct usb_page_cache *pc; + uint32_t rxhdr; + uint16_t pktlen; + int off; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + smsc_dbg_printf(sc, "rx : actlen %d\n", actlen); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + /* There is always a zero length frame after bringing the IF up */ + if (actlen < (sizeof(rxhdr) + ETHER_CRC_LEN)) + goto tr_setup; + + /* There maybe multiple packets in the USB frame, each will have a + * header and each needs to have it's own mbuf allocated and populated + * for it. + */ + pc = usbd_xfer_get_frame(xfer, 0); + off = 0; + + while (off < actlen) { + + /* The frame header is always aligned on a 4 byte boundary */ + off = ((off + 0x3) & ~0x3); + + usbd_copy_out(pc, off, &rxhdr, sizeof(rxhdr)); + off += (sizeof(rxhdr) + ETHER_ALIGN); + rxhdr = le32toh(rxhdr); + + pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr); + + smsc_dbg_printf(sc, "rx : rxhdr 0x%08x : pktlen %d : actlen %d : " + "off %d\n", rxhdr, pktlen, actlen, off); + + + if (rxhdr & SMSC_RX_STAT_ERROR) { + smsc_dbg_printf(sc, "rx error (hdr 0x%08x)\n", rxhdr); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + if (rxhdr & SMSC_RX_STAT_COLLISION) + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1); + } else { + + /* Check if the ethernet frame is too big or too small */ + if ((pktlen < ETHER_HDR_LEN) || (pktlen > (actlen - off))) + goto tr_setup; + + /* Create a new mbuf to store the packet in */ + m = uether_newbuf(); + if (m == NULL) { + smsc_warn_printf(sc, "failed to create new mbuf\n"); + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + goto tr_setup; + } + + usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen); + + /* Check if RX TCP/UDP checksumming is being offloaded */ + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + + struct ether_header *eh; + + eh = mtod(m, struct ether_header *); + + /* Remove the extra 2 bytes of the csum */ + pktlen -= 2; + + /* The checksum appears to be simplistically calculated + * over the udp/tcp header and data up to the end of the + * eth frame. Which means if the eth frame is padded + * the csum calculation is incorrectly performed over + * the padding bytes as well. Therefore to be safe we + * ignore the H/W csum on frames less than or equal to + * 64 bytes. + * + * Ignore H/W csum for non-IPv4 packets. + */ + if ((be16toh(eh->ether_type) == ETHERTYPE_IP) && + (pktlen > ETHER_MIN_LEN)) { + struct ip *ip; + + ip = (struct ip *)(eh + 1); + if ((ip->ip_v == IPVERSION) && + ((ip->ip_p == IPPROTO_TCP) || + (ip->ip_p == IPPROTO_UDP))) { + /* Indicate the UDP/TCP csum has been calculated */ + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; + + /* Copy the TCP/UDP checksum from the last 2 bytes + * of the transfer and put in the csum_data field. + */ + usbd_copy_out(pc, (off + pktlen), + &m->m_pkthdr.csum_data, 2); + + /* The data is copied in network order, but the + * csum algorithm in the kernel expects it to be + * in host network order. + */ + m->m_pkthdr.csum_data = ntohs(m->m_pkthdr.csum_data); + + smsc_dbg_printf(sc, "RX checksum offloaded (0x%04x)\n", + m->m_pkthdr.csum_data); + } + } + + /* Need to adjust the offset as well or we'll be off + * by 2 because the csum is removed from the packet + * length. + */ + off += 2; + } + + /* Finally enqueue the mbuf on the receive queue */ + /* Remove 4 trailing bytes */ + if (pktlen < (4 + ETHER_HDR_LEN)) { + m_freem(m); + goto tr_setup; + } + uether_rxmbuf(ue, m, pktlen - 4); + } + + /* Update the offset to move to the next potential packet */ + off += pktlen; + } + + /* FALLTHROUGH */ + + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: + if (error != USB_ERR_CANCELLED) { + smsc_warn_printf(sc, "bulk read error, %s\n", usbd_errstr(error)); + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +/** + * smsc_bulk_write_callback - Write callback used to send ethernet frame(s) + * @xfer: the USB transfer + * @error: error code if the transfers is in an errored state + * + * The main write function that pulls ethernet frames off the queue and sends + * them out. + * + * LOCKING: + * + */ +static void +smsc_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct smsc_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + uint32_t txhdr; + uint32_t frm_len = 0; + int nframes; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + /* FALLTHROUGH */ + + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & SMSC_FLAG_LINK) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { + /* Don't send anything if there is no link or controller is busy. */ + return; + } + + for (nframes = 0; nframes < 16 && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES, + nframes); + frm_len = 0; + pc = usbd_xfer_get_frame(xfer, nframes); + + /* Each frame is prefixed with two 32-bit values describing the + * length of the packet and buffer. + */ + txhdr = SMSC_TX_CTRL_0_BUF_SIZE(m->m_pkthdr.len) | + SMSC_TX_CTRL_0_FIRST_SEG | SMSC_TX_CTRL_0_LAST_SEG; + txhdr = htole32(txhdr); + usbd_copy_in(pc, 0, &txhdr, sizeof(txhdr)); + + txhdr = SMSC_TX_CTRL_1_PKT_LENGTH(m->m_pkthdr.len); + txhdr = htole32(txhdr); + usbd_copy_in(pc, 4, &txhdr, sizeof(txhdr)); + + frm_len += 8; + + /* Next copy in the actual packet */ + usbd_m_copy_in(pc, frm_len, m, 0, m->m_pkthdr.len); + frm_len += m->m_pkthdr.len; + + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* If there's a BPF listener, bounce a copy of this frame to him */ + BPF_MTAP(ifp, m); + + m_freem(m); + + /* Set frame length. */ + usbd_xfer_set_frame_len(xfer, nframes, frm_len); + } + if (nframes != 0) { + usbd_xfer_set_frames(xfer, nframes); + usbd_transfer_submit(xfer); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + return; + + default: + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + if (error != USB_ERR_CANCELLED) { + smsc_err_printf(sc, "usb error on tx: %s\n", usbd_errstr(error)); + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +/** + * smsc_tick - Called periodically to monitor the state of the LAN95xx chip + * @ue: USB ether interface + * + * Simply calls the mii status functions to check the state of the link. + * + * LOCKING: + * Should be called with the SMSC lock held. + */ +static void +smsc_tick(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct mii_data *mii = uether_getmii(&sc->sc_ue); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & SMSC_FLAG_LINK) == 0) { + smsc_miibus_statchg(ue->ue_dev); + if ((sc->sc_flags & SMSC_FLAG_LINK) != 0) + smsc_start(ue); + } +} + +/** + * smsc_start - Starts communication with the LAN95xx chip + * @ue: USB ether interface + * + * + * + */ +static void +smsc_start(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[SMSC_BULK_DT_WR]); +} + +/** + * smsc_stop - Stops communication with the LAN95xx chip + * @ue: USB ether interface + * + * + * + */ +static void +smsc_stop(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->sc_flags &= ~SMSC_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[SMSC_BULK_DT_RD]); +} + +/** + * smsc_phy_init - Initialises the in-built SMSC phy + * @sc: driver soft context + * + * Resets the PHY part of the chip and then initialises it to default + * values. The 'link down' and 'auto-negotiation complete' interrupts + * from the PHY are also enabled, however we don't monitor the interrupt + * endpoints for the moment. + * + * RETURNS: + * Returns 0 on success or EIO if failed to reset the PHY. + */ +static int +smsc_phy_init(struct smsc_softc *sc) +{ + int bmcr; + usb_ticks_t start_ticks; + const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); + + SMSC_LOCK_ASSERT(sc, MA_OWNED); + + /* Reset phy and wait for reset to complete */ + smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, BMCR_RESET); + + start_ticks = ticks; + do { + uether_pause(&sc->sc_ue, hz / 100); + bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); + } while ((bmcr & MII_BMCR) && ((ticks - start_ticks) < max_ticks)); + + if (((usb_ticks_t)(ticks - start_ticks)) >= max_ticks) { + smsc_err_printf(sc, "PHY reset timed-out"); + return (EIO); + } + + smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_ANAR, + ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD | /* all modes */ + ANAR_CSMA | + ANAR_FC | + ANAR_PAUSE_ASYM); + + /* Setup the phy to interrupt when the link goes down or autoneg completes */ + smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, SMSC_PHY_INTR_STAT); + smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, SMSC_PHY_INTR_MASK, + (SMSC_PHY_INTR_ANEG_COMP | SMSC_PHY_INTR_LINK_DOWN)); + + /* Restart auto-negotation */ + bmcr = smsc_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); + bmcr |= BMCR_STARTNEG; + smsc_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, bmcr); + + return (0); +} + + +/** + * smsc_chip_init - Initialises the chip after power on + * @sc: driver soft context + * + * This initialisation sequence is modelled on the procedure in the Linux + * driver. + * + * RETURNS: + * Returns 0 on success or an error code on failure. + */ +static int +smsc_chip_init(struct smsc_softc *sc) +{ + int err; + int locked; + uint32_t reg_val; + int burst_cap; + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + SMSC_LOCK(sc); + + /* Enter H/W config mode */ + smsc_write_reg(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST); + + if ((err = smsc_wait_for_bits(sc, SMSC_HW_CFG, SMSC_HW_CFG_LRST)) != 0) { + smsc_warn_printf(sc, "timed-out waiting for reset to complete\n"); + goto init_failed; + } + + /* Reset the PHY */ + smsc_write_reg(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST); + + if ((err = smsc_wait_for_bits(sc, SMSC_PM_CTRL, SMSC_PM_CTRL_PHY_RST)) != 0) { + smsc_warn_printf(sc, "timed-out waiting for phy reset to complete\n"); + goto init_failed; + } + + /* Set the mac address */ + if ((err = smsc_setmacaddress(sc, sc->sc_ue.ue_eaddr)) != 0) { + smsc_warn_printf(sc, "failed to set the MAC address\n"); + goto init_failed; + } + + /* Don't know what the HW_CFG_BIR bit is, but following the reset sequence + * as used in the Linux driver. + */ + if ((err = smsc_read_reg(sc, SMSC_HW_CFG, ®_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, ®_val)) < 0) { + smsc_warn_printf(sc, "failed to read HW_CFG: (err = %d)\n", err); + goto init_failed; + } + + /* Adjust the packet offset in the buffer (designed to try and align IP + * header on 4 byte boundary) + */ + reg_val &= ~SMSC_HW_CFG_RXDOFF; + reg_val |= (ETHER_ALIGN << 9) & SMSC_HW_CFG_RXDOFF; + + /* The following setings are used for 'turbo mode', a.k.a multiple frames + * per Rx transaction (again info taken form Linux driver). + */ + reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE); + + smsc_write_reg(sc, SMSC_HW_CFG, reg_val); + + /* Clear the status register ? */ + smsc_write_reg(sc, SMSC_INTR_STATUS, 0xffffffff); + + /* Read and display the revision register */ + if ((err = smsc_read_reg(sc, SMSC_ID_REV, &sc->sc_rev_id)) < 0) { + smsc_warn_printf(sc, "failed to read ID_REV (err = %d)\n", err); + goto init_failed; + } + + device_printf(sc->sc_ue.ue_dev, "chip 0x%04lx, rev. %04lx\n", + (sc->sc_rev_id & SMSC_ID_REV_CHIP_ID_MASK) >> 16, + (sc->sc_rev_id & SMSC_ID_REV_CHIP_REV_MASK)); + + /* GPIO/LED setup */ + reg_val = SMSC_LED_GPIO_CFG_SPD_LED | SMSC_LED_GPIO_CFG_LNK_LED | + SMSC_LED_GPIO_CFG_FDX_LED; + smsc_write_reg(sc, SMSC_LED_GPIO_CFG, reg_val); + + /* + * Initialise the TX interface + */ + smsc_write_reg(sc, SMSC_FLOW, 0); + + smsc_write_reg(sc, SMSC_AFC_CFG, AFC_CFG_DEFAULT); + + /* Read the current MAC configuration */ + if ((err = smsc_read_reg(sc, SMSC_MAC_CSR, &sc->sc_mac_csr)) < 0) { + smsc_warn_printf(sc, "failed to read MAC_CSR (err=%d)\n", err); + goto init_failed; + } + + /* Vlan */ + smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN); + + /* + * Initialise the PHY + */ + if ((err = smsc_phy_init(sc)) != 0) + goto init_failed; + + + /* + * Start TX + */ + sc->sc_mac_csr |= SMSC_MAC_CSR_TXEN; + smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); + smsc_write_reg(sc, SMSC_TX_CFG, SMSC_TX_CFG_ON); + + /* + * Start RX + */ + sc->sc_mac_csr |= SMSC_MAC_CSR_RXEN; + smsc_write_reg(sc, SMSC_MAC_CSR, sc->sc_mac_csr); + + if (!locked) + SMSC_UNLOCK(sc); + + return (0); + +init_failed: + if (!locked) + SMSC_UNLOCK(sc); + + smsc_err_printf(sc, "smsc_chip_init failed (err=%d)\n", err); + return (err); +} + + +/** + * smsc_ioctl - ioctl function for the device + * @ifp: interface pointer + * @cmd: the ioctl command + * @data: data passed in the ioctl call, typically a pointer to struct ifreq. + * + * The ioctl routine is overridden to detect change requests for the H/W + * checksum capabilities. + * + * RETURNS: + * 0 on success and an error code on failure. + */ +static int +smsc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct usb_ether *ue = ifp->if_softc; + struct smsc_softc *sc; + struct ifreq *ifr; + int rc; + int mask; + int reinit; + + if (cmd == SIOCSIFCAP) { + + sc = uether_getsc(ue); + ifr = (struct ifreq *)data; + + SMSC_LOCK(sc); + + rc = 0; + reinit = 0; + + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + + /* Modify the RX CSUM enable bits */ + if ((mask & IFCAP_RXCSUM) != 0 && + (ifp->if_capabilities & IFCAP_RXCSUM) != 0) { + ifp->if_capenable ^= IFCAP_RXCSUM; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + reinit = 1; + } + } + + SMSC_UNLOCK(sc); + if (reinit) +#if __FreeBSD_version > 1000000 + uether_init(ue); +#else + ifp->if_init(ue); +#endif + + } else { + rc = uether_ioctl(ifp, cmd, data); + } + + return (rc); +} + +#ifdef FDT +/* + * This is FreeBSD-specific compatibility strings for RPi/RPi2 + */ +static phandle_t +smsc_fdt_find_eth_node(phandle_t start) +{ + phandle_t child, node; + + /* Traverse through entire tree to find usb ethernet nodes. */ + for (node = OF_child(start); node != 0; node = OF_peer(node)) { + if (ofw_bus_node_is_compatible(node, "net,ethernet") && + ofw_bus_node_is_compatible(node, "usb,device")) + return (node); + child = smsc_fdt_find_eth_node(node); + if (child != -1) + return (child); + } + + return (-1); +} + +/* + * Check if node's path is <*>/usb/hub/ethernet + */ +static int +smsc_fdt_is_usb_eth(phandle_t node) +{ + char name[16]; + int len; + + memset(name, 0, sizeof(name)); + len = OF_getprop(node, "name", name, sizeof(name)); + if (len <= 0) + return (0); + + if (strcmp(name, "ethernet")) + return (0); + + node = OF_parent(node); + if (node == -1) + return (0); + len = OF_getprop(node, "name", name, sizeof(name)); + if (len <= 0) + return (0); + + if (strcmp(name, "hub")) + return (0); + + node = OF_parent(node); + if (node == -1) + return (0); + len = OF_getprop(node, "name", name, sizeof(name)); + if (len <= 0) + return (0); + + if (strcmp(name, "usb")) + return (0); + + return (1); +} + +static phandle_t +smsc_fdt_find_eth_node_by_path(phandle_t start) +{ + phandle_t child, node; + + /* Traverse through entire tree to find usb ethernet nodes. */ + for (node = OF_child(start); node != 0; node = OF_peer(node)) { + if (smsc_fdt_is_usb_eth(node)) + return (node); + child = smsc_fdt_find_eth_node_by_path(node); + if (child != -1) + return (child); + } + + return (-1); +} + +/** + * Get MAC address from FDT blob. Firmware or loader should fill + * mac-address or local-mac-address property. Returns 0 if MAC address + * obtained, error code otherwise. + */ +static int +smsc_fdt_find_mac(unsigned char *mac) +{ + phandle_t node, root; + int len; + + root = OF_finddevice("/"); + node = smsc_fdt_find_eth_node(root); + /* + * If it's not FreeBSD FDT blob for RPi, try more + * generic .../usb/hub/ethernet + */ + if (node == -1) + node = smsc_fdt_find_eth_node_by_path(root); + + if (node != -1) { + /* Check if there is property */ + if ((len = OF_getproplen(node, "local-mac-address")) > 0) { + if (len != ETHER_ADDR_LEN) + return (EINVAL); + + OF_getprop(node, "local-mac-address", mac, + ETHER_ADDR_LEN); + return (0); + } + + if ((len = OF_getproplen(node, "mac-address")) > 0) { + if (len != ETHER_ADDR_LEN) + return (EINVAL); + + OF_getprop(node, "mac-address", mac, + ETHER_ADDR_LEN); + return (0); + } + } + + return (ENXIO); +} +#endif + +/** + * smsc_attach_post - Called after the driver attached to the USB interface + * @ue: the USB ethernet device + * + * This is where the chip is intialised for the first time. This is different + * from the smsc_init() function in that that one is designed to setup the + * H/W to match the UE settings and can be called after a reset. + * + * + */ +static void +smsc_attach_post(struct usb_ether *ue) +{ + struct smsc_softc *sc = uether_getsc(ue); + uint32_t mac_h, mac_l; + int err; + + smsc_dbg_printf(sc, "smsc_attach_post\n"); + + /* Setup some of the basics */ + sc->sc_phyno = 1; + + + /* Attempt to get the mac address, if an EEPROM is not attached this + * will just return FF:FF:FF:FF:FF:FF, so in such cases we invent a MAC + * address based on urandom. + */ + memset(sc->sc_ue.ue_eaddr, 0xff, ETHER_ADDR_LEN); + + /* Check if there is already a MAC address in the register */ + if ((smsc_read_reg(sc, SMSC_MAC_ADDRL, &mac_l) == 0) && + (smsc_read_reg(sc, SMSC_MAC_ADDRH, &mac_h) == 0)) { + sc->sc_ue.ue_eaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); + sc->sc_ue.ue_eaddr[4] = (uint8_t)((mac_h) & 0xff); + sc->sc_ue.ue_eaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); + sc->sc_ue.ue_eaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); + sc->sc_ue.ue_eaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); + sc->sc_ue.ue_eaddr[0] = (uint8_t)((mac_l) & 0xff); + } + + /* MAC address is not set so try to read from EEPROM, if that fails generate + * a random MAC address. + */ + if (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr)) { + + err = smsc_eeprom_read(sc, 0x01, sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN); +#ifdef FDT + if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr))) + err = smsc_fdt_find_mac(sc->sc_ue.ue_eaddr); +#endif + if ((err != 0) || (!ETHER_IS_VALID(sc->sc_ue.ue_eaddr))) { + read_random(sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN); + sc->sc_ue.ue_eaddr[0] &= ~0x01; /* unicast */ + sc->sc_ue.ue_eaddr[0] |= 0x02; /* locally administered */ + } + } + + /* Initialise the chip for the first time */ + smsc_chip_init(sc); +} + + +/** + * smsc_attach_post_sub - Called after the driver attached to the USB interface + * @ue: the USB ethernet device + * + * Most of this is boilerplate code and copied from the base USB ethernet + * driver. It has been overriden so that we can indicate to the system that + * the chip supports H/W checksumming. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +#if __FreeBSD_version > 1000000 +static int +smsc_attach_post_sub(struct usb_ether *ue) +{ + struct smsc_softc *sc; + struct ifnet *ifp; + int error; + + sc = uether_getsc(ue); + ifp = ue->ue_ifp; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = uether_start; + ifp->if_ioctl = smsc_ioctl; + ifp->if_init = uether_init; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + /* The chip supports TCP/UDP checksum offloading on TX and RX paths, however + * currently only RX checksum is supported in the driver (see top of file). + */ + ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_VLAN_MTU; + ifp->if_hwassist = 0; + + /* TX checksuming is disabled (for now?) + ifp->if_capabilities |= IFCAP_TXCSUM; + ifp->if_capenable |= IFCAP_TXCSUM; + ifp->if_hwassist = CSUM_TCP | CSUM_UDP; + */ + + ifp->if_capenable = ifp->if_capabilities; + + mtx_lock(&Giant); + error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, + uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, + BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, 0); + mtx_unlock(&Giant); + + return (error); +} +#endif /* __FreeBSD_version > 1000000 */ + + +/** + * smsc_probe - Probe the interface. + * @dev: smsc device handle + * + * Checks if the device is a match for this driver. + * + * RETURNS: + * Returns 0 on success or an error code on failure. + */ +static int +smsc_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != SMSC_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != SMSC_IFACE_IDX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(smsc_devs, sizeof(smsc_devs), uaa)); +} + + +/** + * smsc_attach - Attach the interface. + * @dev: smsc device handle + * + * Allocate softc structures, do ifmedia setup and ethernet/BPF attach. + * + * RETURNS: + * Returns 0 on success or a negative error code. + */ +static int +smsc_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct smsc_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int err; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Setup the endpoints for the SMSC LAN95xx device(s) */ + iface_index = SMSC_IFACE_IDX; + err = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + smsc_config, SMSC_N_TRANSFER, sc, &sc->sc_mtx); + if (err) { + device_printf(dev, "error: allocating USB transfers failed\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &smsc_ue_methods; + + err = uether_ifattach(ue); + if (err) { + device_printf(dev, "error: could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + smsc_detach(dev); + return (ENXIO); /* failure */ +} + +/** + * smsc_detach - Detach the interface. + * @dev: smsc device handle + * + * RETURNS: + * Returns 0. + */ +static int +smsc_detach(device_t dev) +{ + struct smsc_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, SMSC_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static device_method_t smsc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, smsc_probe), + DEVMETHOD(device_attach, smsc_attach), + DEVMETHOD(device_detach, smsc_detach), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, smsc_miibus_readreg), + DEVMETHOD(miibus_writereg, smsc_miibus_writereg), + DEVMETHOD(miibus_statchg, smsc_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t smsc_driver = { + .name = "smsc", + .methods = smsc_methods, + .size = sizeof(struct smsc_softc), +}; + +static devclass_t smsc_devclass; + +DRIVER_MODULE(smsc, uhub, smsc_driver, smsc_devclass, NULL, 0); +DRIVER_MODULE(miibus, smsc, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(smsc, uether, 1, 1, 1); +MODULE_DEPEND(smsc, usb, 1, 1, 1); +MODULE_DEPEND(smsc, ether, 1, 1, 1); +MODULE_DEPEND(smsc, miibus, 1, 1, 1); +MODULE_VERSION(smsc, 1); +USB_PNP_HOST_INFO(smsc_devs); diff --git a/freebsd/sys/dev/usb/net/if_smscreg.h b/freebsd/sys/dev/usb/net/if_smscreg.h new file mode 100644 index 00000000..31a63828 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_smscreg.h @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 2012 + * Ben Gray <bgray@freebsd.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _IF_SMSCREG_H_ +#define _IF_SMSCREG_H_ + +/* + * Definitions for the SMSC LAN9514 and LAN9514 USB to ethernet controllers. + * + * This information was gleaned from the SMSC driver in the linux kernel, where + * it is Copyrighted (C) 2007-2008 SMSC. + * + */ + +/** + * TRANSMIT FRAMES + * --------------- + * Tx frames are prefixed with an 8-byte header which describes the frame + * + * 4 bytes 4 bytes variable + * +------------+------------+--- . . . . . . . . . . . . ---+ + * | TX_CTRL_0 | TX_CTRL_1 | Ethernet frame data | + * +------------+------------+--- . . . . . . . . . . . . ---+ + * + * Where the headers have the following fields: + * + * TX_CTRL_0 <20:16> Data offset + * TX_CTRL_0 <13> First segment of frame indicator + * TX_CTRL_0 <12> Last segment of frame indicator + * TX_CTRL_0 <10:0> Buffer size (?) + * + * TX_CTRL_1 <14> Perform H/W checksuming on IP packets + * TX_CTRL_1 <13> Disable automatic ethernet CRC generation + * TX_CTRL_1 <12> Disable padding (?) + * TX_CTRL_1 <10:0> Packet byte length + * + */ +#define SMSC_TX_CTRL_0_OFFSET(x) (((x) & 0x1FUL) << 16) +#define SMSC_TX_CTRL_0_FIRST_SEG (0x1UL << 13) +#define SMSC_TX_CTRL_0_LAST_SEG (0x1UL << 12) +#define SMSC_TX_CTRL_0_BUF_SIZE(x) ((x) & 0x000007FFUL) + +#define SMSC_TX_CTRL_1_CSUM_ENABLE (0x1UL << 14) +#define SMSC_TX_CTRL_1_CRC_DISABLE (0x1UL << 13) +#define SMSC_TX_CTRL_1_PADDING_DISABLE (0x1UL << 12) +#define SMSC_TX_CTRL_1_PKT_LENGTH(x) ((x) & 0x000007FFUL) + +/** + * RECEIVE FRAMES + * -------------- + * Rx frames are prefixed with an 4-byte status header which describes any + * errors with the frame as well as things like the length + * + * 4 bytes variable + * +------------+--- . . . . . . . . . . . . ---+ + * | RX_STAT | Ethernet frame data | + * +------------+--- . . . . . . . . . . . . ---+ + * + * Where the status header has the following fields: + * + * RX_STAT <30> Filter Fail + * RX_STAT <29:16> Frame Length + * RX_STAT <15> Error Summary + * RX_STAT <13> Broadcast Frame + * RX_STAT <12> Length Error + * RX_STAT <11> Runt Frame + * RX_STAT <10> Multicast Frame + * RX_STAT <7> Frame too long + * RX_STAT <6> Collision Seen + * RX_STAT <5> Frame Type + * RX_STAT <4> Receive Watchdog + * RX_STAT <3> Mii Error + * RX_STAT <2> Dribbling + * RX_STAT <1> CRC Error + * + */ +#define SMSC_RX_STAT_FILTER_FAIL (0x1UL << 30) +#define SMSC_RX_STAT_FRM_LENGTH(x) (((x) >> 16) & 0x3FFFUL) +#define SMSC_RX_STAT_ERROR (0x1UL << 15) +#define SMSC_RX_STAT_BROADCAST (0x1UL << 13) +#define SMSC_RX_STAT_LENGTH_ERROR (0x1UL << 12) +#define SMSC_RX_STAT_RUNT (0x1UL << 11) +#define SMSC_RX_STAT_MULTICAST (0x1UL << 10) +#define SMSC_RX_STAT_FRM_TO_LONG (0x1UL << 7) +#define SMSC_RX_STAT_COLLISION (0x1UL << 6) +#define SMSC_RX_STAT_FRM_TYPE (0x1UL << 5) +#define SMSC_RX_STAT_WATCHDOG (0x1UL << 4) +#define SMSC_RX_STAT_MII_ERROR (0x1UL << 3) +#define SMSC_RX_STAT_DRIBBLING (0x1UL << 2) +#define SMSC_RX_STAT_CRC_ERROR (0x1UL << 1) + +/** + * REGISTERS + * + */ +#define SMSC_ID_REV 0x000 +#define SMSC_INTR_STATUS 0x008 +#define SMSC_RX_CFG 0x00C +#define SMSC_TX_CFG 0x010 +#define SMSC_HW_CFG 0x014 +#define SMSC_PM_CTRL 0x020 +#define SMSC_LED_GPIO_CFG 0x024 +#define SMSC_GPIO_CFG 0x028 +#define SMSC_AFC_CFG 0x02C +#define SMSC_EEPROM_CMD 0x030 +#define SMSC_EEPROM_DATA 0x034 +#define SMSC_BURST_CAP 0x038 +#define SMSC_GPIO_WAKE 0x064 +#define SMSC_INTR_CFG 0x068 +#define SMSC_BULK_IN_DLY 0x06C +#define SMSC_MAC_CSR 0x100 +#define SMSC_MAC_ADDRH 0x104 +#define SMSC_MAC_ADDRL 0x108 +#define SMSC_HASHH 0x10C +#define SMSC_HASHL 0x110 +#define SMSC_MII_ADDR 0x114 +#define SMSC_MII_DATA 0x118 +#define SMSC_FLOW 0x11C +#define SMSC_VLAN1 0x120 +#define SMSC_VLAN2 0x124 +#define SMSC_WUFF 0x128 +#define SMSC_WUCSR 0x12C +#define SMSC_COE_CTRL 0x130 + +/* ID / Revision register */ +#define SMSC_ID_REV_CHIP_ID_MASK 0xFFFF0000UL +#define SMSC_ID_REV_CHIP_REV_MASK 0x0000FFFFUL + +#define SMSC_RX_FIFO_FLUSH (0x1UL << 0) + +#define SMSC_TX_CFG_ON (0x1UL << 2) +#define SMSC_TX_CFG_STOP (0x1UL << 1) +#define SMSC_TX_CFG_FIFO_FLUSH (0x1UL << 0) + +#define SMSC_HW_CFG_BIR (0x1UL << 12) +#define SMSC_HW_CFG_LEDB (0x1UL << 11) +#define SMSC_HW_CFG_RXDOFF (0x3UL << 9) /* RX pkt alignment */ +#define SMSC_HW_CFG_DRP (0x1UL << 6) +#define SMSC_HW_CFG_MEF (0x1UL << 5) +#define SMSC_HW_CFG_LRST (0x1UL << 3) /* Lite reset */ +#define SMSC_HW_CFG_PSEL (0x1UL << 2) +#define SMSC_HW_CFG_BCE (0x1UL << 1) +#define SMSC_HW_CFG_SRST (0x1UL << 0) + +#define SMSC_PM_CTRL_PHY_RST (0x1UL << 4) /* PHY reset */ + +#define SMSC_LED_GPIO_CFG_SPD_LED (0x1UL << 24) +#define SMSC_LED_GPIO_CFG_LNK_LED (0x1UL << 20) +#define SMSC_LED_GPIO_CFG_FDX_LED (0x1UL << 16) + +/* Hi watermark = 15.5Kb (~10 mtu pkts) */ +/* low watermark = 3k (~2 mtu pkts) */ +/* backpressure duration = ~ 350us */ +/* Apply FC on any frame. */ +#define AFC_CFG_DEFAULT (0x00F830A1) + +#define SMSC_EEPROM_CMD_BUSY (0x1UL << 31) +#define SMSC_EEPROM_CMD_MASK (0x7UL << 28) +#define SMSC_EEPROM_CMD_READ (0x0UL << 28) +#define SMSC_EEPROM_CMD_WRITE (0x3UL << 28) +#define SMSC_EEPROM_CMD_ERASE (0x5UL << 28) +#define SMSC_EEPROM_CMD_RELOAD (0x7UL << 28) +#define SMSC_EEPROM_CMD_TIMEOUT (0x1UL << 10) +#define SMSC_EEPROM_CMD_ADDR_MASK 0x000001FFUL + +/* MAC Control and Status Register */ +#define SMSC_MAC_CSR_RCVOWN (0x1UL << 23) /* Half duplex */ +#define SMSC_MAC_CSR_LOOPBK (0x1UL << 21) /* Loopback */ +#define SMSC_MAC_CSR_FDPX (0x1UL << 20) /* Full duplex */ +#define SMSC_MAC_CSR_MCPAS (0x1UL << 19) /* Multicast mode */ +#define SMSC_MAC_CSR_PRMS (0x1UL << 18) /* Promiscuous mode */ +#define SMSC_MAC_CSR_INVFILT (0x1UL << 17) /* Inverse filtering */ +#define SMSC_MAC_CSR_PASSBAD (0x1UL << 16) /* Pass on bad frames */ +#define SMSC_MAC_CSR_HPFILT (0x1UL << 13) /* Hash filtering */ +#define SMSC_MAC_CSR_BCAST (0x1UL << 11) /* Broadcast */ +#define SMSC_MAC_CSR_TXEN (0x1UL << 3) /* TX enable */ +#define SMSC_MAC_CSR_RXEN (0x1UL << 2) /* RX enable */ + +/* Interrupt control register */ +#define SMSC_INTR_NTEP (0x1UL << 31) +#define SMSC_INTR_MACRTO (0x1UL << 19) +#define SMSC_INTR_TX_STOP (0x1UL << 17) +#define SMSC_INTR_RX_STOP (0x1UL << 16) +#define SMSC_INTR_PHY_INT (0x1UL << 15) +#define SMSC_INTR_TXE (0x1UL << 14) +#define SMSC_INTR_TDFU (0x1UL << 13) +#define SMSC_INTR_TDFO (0x1UL << 12) +#define SMSC_INTR_RXDF (0x1UL << 11) +#define SMSC_INTR_GPIOS 0x000007FFUL + +/* Phy MII interface register */ +#define SMSC_MII_WRITE (0x1UL << 1) +#define SMSC_MII_READ (0x0UL << 1) +#define SMSC_MII_BUSY (0x1UL << 0) + +/* H/W checksum register */ +#define SMSC_COE_CTRL_TX_EN (0x1UL << 16) /* Tx H/W csum enable */ +#define SMSC_COE_CTRL_RX_MODE (0x1UL << 1) +#define SMSC_COE_CTRL_RX_EN (0x1UL << 0) /* Rx H/W csum enable */ + +/* Registers on the phy, accessed via MII/MDIO */ +#define SMSC_PHY_INTR_STAT (29) +#define SMSC_PHY_INTR_MASK (30) + +#define SMSC_PHY_INTR_ENERGY_ON (0x1U << 7) +#define SMSC_PHY_INTR_ANEG_COMP (0x1U << 6) +#define SMSC_PHY_INTR_REMOTE_FAULT (0x1U << 5) +#define SMSC_PHY_INTR_LINK_DOWN (0x1U << 4) + +/* USB Vendor Requests */ +#define SMSC_UR_WRITE_REG 0xA0 +#define SMSC_UR_READ_REG 0xA1 +#define SMSC_UR_GET_STATS 0xA2 + +#define SMSC_CONFIG_INDEX 0 /* config number 1 */ +#define SMSC_IFACE_IDX 0 + +/* + * USB endpoints. + */ +enum { + SMSC_BULK_DT_RD, + SMSC_BULK_DT_WR, + /* the LAN9514 device does support interrupt endpoints, however I couldn't + * get then to work reliably and since they are unneeded (poll the mii + * status) they are unused. + * SMSC_INTR_DT_WR, + * SMSC_INTR_DT_RD, + */ + SMSC_N_TRANSFER, +}; + +struct smsc_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[SMSC_N_TRANSFER]; + int sc_phyno; + + /* The following stores the settings in the mac control (MAC_CSR) register */ + uint32_t sc_mac_csr; + uint32_t sc_rev_id; + + uint32_t sc_flags; +#define SMSC_FLAG_LINK 0x0001 +#define SMSC_FLAG_LAN9514 0x1000 /* LAN9514 */ +}; + +#define SMSC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define SMSC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define SMSC_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) + +#endif /* _IF_SMSCREG_H_ */ diff --git a/freebsd/sys/dev/usb/net/if_udav.c b/freebsd/sys/dev/usb/net/if_udav.c new file mode 100644 index 00000000..f4fcc4a5 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_udav.c @@ -0,0 +1,883 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */ +/* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2003 + * Shingo WATANABE <nabe@nabechan.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY) + * The spec can be found at the following url. + * http://ptm2.cc.utu.fi/ftp/network/cards/DM9601/From_NET/DM9601-DS-P01-930914.pdf + */ + +/* + * TODO: + * Interrupt Endpoint support + * External PHYs + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/stdint.h> +#include <sys/stddef.h> +#include <rtems/bsd/sys/param.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/sysctl.h> +#include <sys/sx.h> +#include <rtems/bsd/sys/unistd.h> +#include <sys/callout.h> +#include <sys/malloc.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR udav_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_udavreg.h> + +/* prototypes */ + +static device_probe_t udav_probe; +static device_attach_t udav_attach; +static device_detach_t udav_detach; + +static usb_callback_t udav_bulk_write_callback; +static usb_callback_t udav_bulk_read_callback; +static usb_callback_t udav_intr_callback; + +static uether_fn_t udav_attach_post; +static uether_fn_t udav_init; +static uether_fn_t udav_stop; +static uether_fn_t udav_start; +static uether_fn_t udav_tick; +static uether_fn_t udav_setmulti; +static uether_fn_t udav_setpromisc; + +static int udav_csr_read(struct udav_softc *, uint16_t, void *, int); +static int udav_csr_write(struct udav_softc *, uint16_t, void *, int); +static uint8_t udav_csr_read1(struct udav_softc *, uint16_t); +static int udav_csr_write1(struct udav_softc *, uint16_t, uint8_t); +static void udav_reset(struct udav_softc *); +static int udav_ifmedia_upd(struct ifnet *); +static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *); + +static miibus_readreg_t udav_miibus_readreg; +static miibus_writereg_t udav_miibus_writereg; +static miibus_statchg_t udav_miibus_statchg; + +static const struct usb_config udav_config[UDAV_N_TRANSFER] = { + + [UDAV_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (MCLBYTES + 2), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = udav_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + + [UDAV_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (MCLBYTES + 3), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = udav_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, + + [UDAV_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .bufsize = 0, /* use wMaxPacketSize */ + .callback = udav_intr_callback, + }, +}; + +static device_method_t udav_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, udav_probe), + DEVMETHOD(device_attach, udav_attach), + DEVMETHOD(device_detach, udav_detach), + + /* MII interface */ + DEVMETHOD(miibus_readreg, udav_miibus_readreg), + DEVMETHOD(miibus_writereg, udav_miibus_writereg), + DEVMETHOD(miibus_statchg, udav_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t udav_driver = { + .name = "udav", + .methods = udav_methods, + .size = sizeof(struct udav_softc), +}; + +static devclass_t udav_devclass; + +static const STRUCT_USB_HOST_ID udav_devs[] = { + /* ShanTou DM9601 USB NIC */ + {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601, 0)}, + /* ShanTou ST268 USB NIC */ + {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268, 0)}, + /* Corega USB-TXC */ + {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC, 0)}, + /* ShanTou AMD8515 USB NIC */ + {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ADM8515, 0)}, + /* Kontron AG USB Ethernet */ + {USB_VPI(USB_VENDOR_KONTRON, USB_PRODUCT_KONTRON_DM9601, 0)}, + {USB_VPI(USB_VENDOR_KONTRON, USB_PRODUCT_KONTRON_JP1082, + UDAV_FLAG_NO_PHY)}, +}; + +DRIVER_MODULE(udav, uhub, udav_driver, udav_devclass, NULL, 0); +DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(udav, uether, 1, 1, 1); +MODULE_DEPEND(udav, usb, 1, 1, 1); +MODULE_DEPEND(udav, ether, 1, 1, 1); +MODULE_DEPEND(udav, miibus, 1, 1, 1); +MODULE_VERSION(udav, 1); +USB_PNP_HOST_INFO(udav_devs); + +static const struct usb_ether_methods udav_ue_methods = { + .ue_attach_post = udav_attach_post, + .ue_start = udav_start, + .ue_init = udav_init, + .ue_stop = udav_stop, + .ue_tick = udav_tick, + .ue_setmulti = udav_setmulti, + .ue_setpromisc = udav_setpromisc, + .ue_mii_upd = udav_ifmedia_upd, + .ue_mii_sts = udav_ifmedia_status, +}; + +static const struct usb_ether_methods udav_ue_methods_nophy = { + .ue_attach_post = udav_attach_post, + .ue_start = udav_start, + .ue_init = udav_init, + .ue_stop = udav_stop, + .ue_setmulti = udav_setmulti, + .ue_setpromisc = udav_setpromisc, +}; + +#ifdef USB_DEBUG +static int udav_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav"); +SYSCTL_INT(_hw_usb_udav, OID_AUTO, debug, CTLFLAG_RWTUN, &udav_debug, 0, + "Debug level"); +#endif + +#define UDAV_SETBIT(sc, reg, x) \ + udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x)) + +#define UDAV_CLRBIT(sc, reg, x) \ + udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x)) + +static void +udav_attach_post(struct usb_ether *ue) +{ + struct udav_softc *sc = uether_getsc(ue); + + /* reset the adapter */ + udav_reset(sc); + + /* Get Ethernet Address */ + udav_csr_read(sc, UDAV_PAR, ue->ue_eaddr, ETHER_ADDR_LEN); +} + +static int +udav_probe(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != UDAV_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != UDAV_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(udav_devs, sizeof(udav_devs), uaa)); +} + +static int +udav_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct udav_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = UDAV_IFACE_INDEX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + + /* + * The JP1082 has an unusable PHY and provides no link information. + */ + if (sc->sc_flags & UDAV_FLAG_NO_PHY) { + ue->ue_methods = &udav_ue_methods_nophy; + sc->sc_flags |= UDAV_FLAG_LINK; + } else { + ue->ue_methods = &udav_ue_methods; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + + error = uether_ifattach(ue); + if (error) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + + return (0); /* success */ + +detach: + udav_detach(dev); + return (ENXIO); /* failure */ +} + +static int +udav_detach(device_t dev) +{ + struct udav_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +#if 0 +static int +udav_mem_read(struct udav_softc *sc, uint16_t offset, void *buf, + int len) +{ + struct usb_device_request req; + + len &= 0xff; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = UDAV_REQ_MEM_READ; + USETW(req.wValue, 0x0000); + USETW(req.wIndex, offset); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +udav_mem_write(struct udav_softc *sc, uint16_t offset, void *buf, + int len) +{ + struct usb_device_request req; + + len &= 0xff; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UDAV_REQ_MEM_WRITE; + USETW(req.wValue, 0x0000); + USETW(req.wIndex, offset); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +udav_mem_write1(struct udav_softc *sc, uint16_t offset, + uint8_t ch) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UDAV_REQ_MEM_WRITE1; + USETW(req.wValue, ch); + USETW(req.wIndex, offset); + USETW(req.wLength, 0x0000); + + return (uether_do_request(&sc->sc_ue, &req, NULL, 1000)); +} +#endif + +static int +udav_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, int len) +{ + struct usb_device_request req; + + len &= 0xff; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = UDAV_REQ_REG_READ; + USETW(req.wValue, 0x0000); + USETW(req.wIndex, offset); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +udav_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, int len) +{ + struct usb_device_request req; + + offset &= 0xff; + len &= 0xff; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UDAV_REQ_REG_WRITE; + USETW(req.wValue, 0x0000); + USETW(req.wIndex, offset); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static uint8_t +udav_csr_read1(struct udav_softc *sc, uint16_t offset) +{ + uint8_t val; + + udav_csr_read(sc, offset, &val, 1); + return (val); +} + +static int +udav_csr_write1(struct udav_softc *sc, uint16_t offset, + uint8_t ch) +{ + struct usb_device_request req; + + offset &= 0xff; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = UDAV_REQ_REG_WRITE1; + USETW(req.wValue, ch); + USETW(req.wIndex, offset); + USETW(req.wLength, 0x0000); + + return (uether_do_request(&sc->sc_ue, &req, NULL, 1000)); +} + +static void +udav_init(struct usb_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Cancel pending I/O + */ + udav_stop(ue); + + /* set MAC address */ + udav_csr_write(sc, UDAV_PAR, IF_LLADDR(ifp), ETHER_ADDR_LEN); + + /* initialize network control register */ + + /* disable loopback */ + UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1); + + /* Initialize RX control register */ + UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC); + + /* load multicast filter and update promiscious mode bit */ + udav_setpromisc(ue); + + /* enable RX */ + UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN); + + /* clear POWER_DOWN state of internal PHY */ + UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0); + UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0); + + usbd_xfer_set_stall(sc->sc_xfer[UDAV_BULK_DT_WR]); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + udav_start(ue); +} + +static void +udav_reset(struct udav_softc *sc) +{ + int i; + + /* Select PHY */ +#if 1 + /* + * XXX: force select internal phy. + * external phy routines are not tested. + */ + UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); +#else + if (sc->sc_flags & UDAV_EXT_PHY) + UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); + else + UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); +#endif + + UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST); + + for (i = 0; i < UDAV_TX_TIMEOUT; i++) { + if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST)) + break; + if (uether_pause(&sc->sc_ue, hz / 100)) + break; + } + + uether_pause(&sc->sc_ue, hz / 100); +} + +#define UDAV_BITS 6 +static void +udav_setmulti(struct usb_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct ifmultiaddr *ifma; + uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int h = 0; + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC); + return; + } + + /* first, zot all the existing hash bits */ + memset(hashtbl, 0x00, sizeof(hashtbl)); + hashtbl[7] |= 0x80; /* broadcast address */ + udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl)); + + /* now program new ones */ + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) + { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + hashtbl[h / 8] |= 1 << (h % 8); + } + if_maddr_runlock(ifp); + + /* disable all multicast */ + UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL); + + /* write hash value to the register */ + udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl)); +} + +static void +udav_setpromisc(struct usb_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + uint8_t rxmode; + + rxmode = udav_csr_read1(sc, UDAV_RCR); + rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC); + + if (ifp->if_flags & IFF_PROMISC) + rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC; + else if (ifp->if_flags & IFF_ALLMULTI) + rxmode |= UDAV_RCR_ALL; + + /* write new mode bits */ + udav_csr_write1(sc, UDAV_RCR, rxmode); +} + +static void +udav_start(struct usb_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[UDAV_INTR_DT_RD]); + usbd_transfer_start(sc->sc_xfer[UDAV_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[UDAV_BULK_DT_WR]); +} + +static void +udav_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct udav_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + int extra_len; + int temp_len; + uint8_t buf[2]; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & UDAV_FLAG_LINK) == 0) { + /* + * don't send anything if there is no link ! + */ + return; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + + if (m == NULL) + return; + if (m->m_pkthdr.len > MCLBYTES) + m->m_pkthdr.len = MCLBYTES; + if (m->m_pkthdr.len < UDAV_MIN_FRAME_LEN) { + extra_len = UDAV_MIN_FRAME_LEN - m->m_pkthdr.len; + } else { + extra_len = 0; + } + + temp_len = (m->m_pkthdr.len + extra_len); + + /* + * the frame length is specified in the first 2 bytes of the + * buffer + */ + buf[0] = (uint8_t)(temp_len); + buf[1] = (uint8_t)(temp_len >> 8); + + temp_len += 2; + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, buf, 2); + usbd_m_copy_in(pc, 2, m, 0, m->m_pkthdr.len); + + if (extra_len) + usbd_frame_zero(pc, temp_len - extra_len, extra_len); + /* + * if there's a BPF listener, bounce a copy + * of this frame to him: + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + usbd_xfer_set_frame_len(xfer, 0, temp_len); + usbd_transfer_submit(xfer); + return; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +udav_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct udav_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + struct usb_page_cache *pc; + struct udav_rxpkt stat; + int len; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + if (actlen < (int)(sizeof(stat) + ETHER_CRC_LEN)) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &stat, sizeof(stat)); + actlen -= sizeof(stat); + len = min(actlen, le16toh(stat.pktlen)); + len -= ETHER_CRC_LEN; + + if (stat.rxstat & UDAV_RSR_LCS) { + if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1); + goto tr_setup; + } + if (stat.rxstat & UDAV_RSR_ERR) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + uether_rxbuf(ue, pc, sizeof(stat), len); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read error, %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +udav_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + return; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +udav_stop(struct usb_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + if (!(sc->sc_flags & UDAV_FLAG_NO_PHY)) + sc->sc_flags &= ~UDAV_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_RD]); + usbd_transfer_stop(sc->sc_xfer[UDAV_INTR_DT_RD]); + + udav_reset(sc); +} + +static int +udav_ifmedia_upd(struct ifnet *ifp) +{ + struct udav_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + struct mii_softc *miisc; + int error; + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_flags &= ~UDAV_FLAG_LINK; + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + return (error); +} + +static void +udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct udav_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + + UDAV_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + UDAV_UNLOCK(sc); +} + +static void +udav_tick(struct usb_ether *ue) +{ + struct udav_softc *sc = ue->ue_sc; + struct mii_data *mii = GET_MII(sc); + + UDAV_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & UDAV_FLAG_LINK) == 0 + && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sc_flags |= UDAV_FLAG_LINK; + udav_start(ue); + } +} + +static int +udav_miibus_readreg(device_t dev, int phy, int reg) +{ + struct udav_softc *sc = device_get_softc(dev); + uint16_t data16; + uint8_t val[2]; + int locked; + + /* XXX: one PHY only for the internal PHY */ + if (phy != 0) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + UDAV_LOCK(sc); + + /* select internal PHY and set PHY register address */ + udav_csr_write1(sc, UDAV_EPAR, + UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); + + /* select PHY operation and start read command */ + udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR); + + /* XXX: should we wait? */ + + /* end read command */ + UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR); + + /* retrieve the result from data registers */ + udav_csr_read(sc, UDAV_EPDRL, val, 2); + + data16 = (val[0] | (val[1] << 8)); + + DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n", + phy, reg, data16); + + if (!locked) + UDAV_UNLOCK(sc); + return (data16); +} + +static int +udav_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct udav_softc *sc = device_get_softc(dev); + uint8_t val[2]; + int locked; + + /* XXX: one PHY only for the internal PHY */ + if (phy != 0) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + UDAV_LOCK(sc); + + /* select internal PHY and set PHY register address */ + udav_csr_write1(sc, UDAV_EPAR, + UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); + + /* put the value to the data registers */ + val[0] = (data & 0xff); + val[1] = (data >> 8) & 0xff; + udav_csr_write(sc, UDAV_EPDRL, val, 2); + + /* select PHY operation and start write command */ + udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW); + + /* XXX: should we wait? */ + + /* end write command */ + UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW); + + if (!locked) + UDAV_UNLOCK(sc); + return (0); +} + +static void +udav_miibus_statchg(device_t dev) +{ + /* nothing to do */ +} diff --git a/freebsd/sys/dev/usb/net/if_udavreg.h b/freebsd/sys/dev/usb/net/if_udavreg.h new file mode 100644 index 00000000..7c35829b --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_udavreg.h @@ -0,0 +1,167 @@ +/* $NetBSD: if_udavreg.h,v 1.2 2003/09/04 15:17:39 tsutsui Exp $ */ +/* $nabe: if_udavreg.h,v 1.2 2003/08/21 16:26:40 nabe Exp $ */ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2003 + * Shingo WATANABE <nabe@nabechan.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#define UDAV_IFACE_INDEX 0 +#define UDAV_CONFIG_INDEX 0 /* config number 1 */ + +#define UDAV_TX_TIMEOUT 1000 +#define UDAV_TIMEOUT 10000 + +#define UDAV_TX_TIMEOUT 1000 +#define UDAV_TIMEOUT 10000 + +/* Packet length */ +#define UDAV_MIN_FRAME_LEN 60 + +/* Request */ +#define UDAV_REQ_REG_READ 0x00 /* Read from register(s) */ +#define UDAV_REQ_REG_WRITE 0x01 /* Write to register(s) */ +#define UDAV_REQ_REG_WRITE1 0x03 /* Write to a register */ + +#define UDAV_REQ_MEM_READ 0x02 /* Read from memory */ +#define UDAV_REQ_MEM_WRITE 0x05 /* Write to memory */ +#define UDAV_REQ_MEM_WRITE1 0x07 /* Write a byte to memory */ + +/* Registers */ +#define UDAV_NCR 0x00 /* Network Control Register */ +#define UDAV_NCR_EXT_PHY (1<<7) /* Select External PHY */ +#define UDAV_NCR_WAKEEN (1<<6) /* Wakeup Event Enable */ +#define UDAV_NCR_FCOL (1<<4) /* Force Collision Mode */ +#define UDAV_NCR_FDX (1<<3) /* Full-Duplex Mode (RO on Int. PHY) */ +#define UDAV_NCR_LBK1 (1<<2) /* Lookback Mode */ +#define UDAV_NCR_LBK0 (1<<1) /* Lookback Mode */ +#define UDAV_NCR_RST (1<<0) /* Software reset */ + +#define UDAV_RCR 0x05 /* RX Control Register */ +#define UDAV_RCR_WTDIS (1<<6) /* Watchdog Timer Disable */ +#define UDAV_RCR_DIS_LONG (1<<5) /* Discard Long Packet(over 1522Byte) */ +#define UDAV_RCR_DIS_CRC (1<<4) /* Discard CRC Error Packet */ +#define UDAV_RCR_ALL (1<<3) /* Pass All Multicast */ +#define UDAV_RCR_RUNT (1<<2) /* Pass Runt Packet */ +#define UDAV_RCR_PRMSC (1<<1) /* Promiscuous Mode */ +#define UDAV_RCR_RXEN (1<<0) /* RX Enable */ + +#define UDAV_RSR 0x06 /* RX Status Register */ +#define UDAV_RSR_RF (1<<7) /* Runt Frame */ +#define UDAV_RSR_MF (1<<6) /* Multicast Frame */ +#define UDAV_RSR_LCS (1<<5) /* Late Collision Seen */ +#define UDAV_RSR_RWTO (1<<4) /* Receive Watchdog Time-Out */ +#define UDAV_RSR_PLE (1<<3) /* Physical Layer Error */ +#define UDAV_RSR_AE (1<<2) /* Alignment Error */ +#define UDAV_RSR_CE (1<<1) /* CRC Error */ +#define UDAV_RSR_FOE (1<<0) /* FIFO Overflow Error */ +#define UDAV_RSR_ERR (UDAV_RSR_RF | UDAV_RSR_LCS | \ + UDAV_RSR_RWTO | UDAV_RSR_PLE | \ + UDAV_RSR_AE | UDAV_RSR_CE | UDAV_RSR_FOE) + +#define UDAV_EPCR 0x0b /* EEPROM & PHY Control Register */ +#define UDAV_EPCR_REEP (1<<5) /* Reload EEPROM */ +#define UDAV_EPCR_WEP (1<<4) /* Write EEPROM enable */ +#define UDAV_EPCR_EPOS (1<<3) /* EEPROM or PHY Operation Select */ +#define UDAV_EPCR_ERPRR (1<<2) /* EEPROM/PHY Register Read Command */ +#define UDAV_EPCR_ERPRW (1<<1) /* EEPROM/PHY Register Write Command */ +#define UDAV_EPCR_ERRE (1<<0) /* EEPROM/PHY Access Status */ + +#define UDAV_EPAR 0x0c /* EEPROM & PHY Control Register */ +#define UDAV_EPAR_PHY_ADR1 (1<<7) /* PHY Address bit 1 */ +#define UDAV_EPAR_PHY_ADR0 (1<<6) /* PHY Address bit 0 */ +#define UDAV_EPAR_EROA (1<<0) /* EEPROM Word/PHY Register Address */ +#define UDAV_EPAR_EROA_MASK (0x1f) /* [5:0] */ + +#define UDAV_EPDRL 0x0d /* EEPROM & PHY Data Register */ +#define UDAV_EPDRH 0x0e /* EEPROM & PHY Data Register */ + +#define UDAV_PAR0 0x10 /* Ethernet Address, load from EEPROM */ +#define UDAV_PAR1 0x11 /* Ethernet Address, load from EEPROM */ +#define UDAV_PAR2 0x12 /* Ethernet Address, load from EEPROM */ +#define UDAV_PAR3 0x13 /* Ethernet Address, load from EEPROM */ +#define UDAV_PAR4 0x14 /* Ethernet Address, load from EEPROM */ +#define UDAV_PAR5 0x15 /* Ethernet Address, load from EEPROM */ +#define UDAV_PAR UDAV_PAR0 + +#define UDAV_MAR0 0x16 /* Multicast Register */ +#define UDAV_MAR1 0x17 /* Multicast Register */ +#define UDAV_MAR2 0x18 /* Multicast Register */ +#define UDAV_MAR3 0x19 /* Multicast Register */ +#define UDAV_MAR4 0x1a /* Multicast Register */ +#define UDAV_MAR5 0x1b /* Multicast Register */ +#define UDAV_MAR6 0x1c /* Multicast Register */ +#define UDAV_MAR7 0x1d /* Multicast Register */ +#define UDAV_MAR UDAV_MAR0 + +#define UDAV_GPCR 0x1e /* General purpose control register */ +#define UDAV_GPCR_GEP_CNTL6 (1<<6) /* General purpose control 6 */ +#define UDAV_GPCR_GEP_CNTL5 (1<<5) /* General purpose control 5 */ +#define UDAV_GPCR_GEP_CNTL4 (1<<4) /* General purpose control 4 */ +#define UDAV_GPCR_GEP_CNTL3 (1<<3) /* General purpose control 3 */ +#define UDAV_GPCR_GEP_CNTL2 (1<<2) /* General purpose control 2 */ +#define UDAV_GPCR_GEP_CNTL1 (1<<1) /* General purpose control 1 */ +#define UDAV_GPCR_GEP_CNTL0 (1<<0) /* General purpose control 0 */ + +#define UDAV_GPR 0x1f /* General purpose register */ +#define UDAV_GPR_GEPIO6 (1<<6) /* General purpose 6 */ +#define UDAV_GPR_GEPIO5 (1<<5) /* General purpose 5 */ +#define UDAV_GPR_GEPIO4 (1<<4) /* General purpose 4 */ +#define UDAV_GPR_GEPIO3 (1<<3) /* General purpose 3 */ +#define UDAV_GPR_GEPIO2 (1<<2) /* General purpose 2 */ +#define UDAV_GPR_GEPIO1 (1<<1) /* General purpose 1 */ +#define UDAV_GPR_GEPIO0 (1<<0) /* General purpose 0 */ + +#define GET_MII(sc) uether_getmii(&(sc)->sc_ue) + +struct udav_rxpkt { + uint8_t rxstat; + uint16_t pktlen; +} __packed; + +enum { + UDAV_BULK_DT_WR, + UDAV_BULK_DT_RD, + UDAV_INTR_DT_RD, + UDAV_N_TRANSFER, +}; + +struct udav_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[UDAV_N_TRANSFER]; + + int sc_flags; +#define UDAV_FLAG_LINK 0x0001 +#define UDAV_FLAG_EXT_PHY 0x0040 +#define UDAV_FLAG_NO_PHY 0x0080 +}; + +#define UDAV_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define UDAV_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define UDAV_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/if_ure.c b/freebsd/sys/dev/usb/net/if_ure.c new file mode 100644 index 00000000..d5778ce4 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_ure.c @@ -0,0 +1,1259 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2015-2016 Kevin Lo <kevlo@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/kernel.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/unistd.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR ure_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_process.h> + +#include <dev/usb/net/usb_ethernet.h> +#include <dev/usb/net/if_urereg.h> + +#ifdef USB_DEBUG +static int ure_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, ure, CTLFLAG_RW, 0, "USB ure"); +SYSCTL_INT(_hw_usb_ure, OID_AUTO, debug, CTLFLAG_RWTUN, &ure_debug, 0, + "Debug level"); +#endif + +/* + * Various supported device vendors/products. + */ +static const STRUCT_USB_HOST_ID ure_devs[] = { +#define URE_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } + URE_DEV(REALTEK, RTL8152, URE_FLAG_8152), + URE_DEV(REALTEK, RTL8153, 0), +#undef URE_DEV +}; + +static device_probe_t ure_probe; +static device_attach_t ure_attach; +static device_detach_t ure_detach; + +static usb_callback_t ure_bulk_read_callback; +static usb_callback_t ure_bulk_write_callback; + +static miibus_readreg_t ure_miibus_readreg; +static miibus_writereg_t ure_miibus_writereg; +static miibus_statchg_t ure_miibus_statchg; + +static uether_fn_t ure_attach_post; +static uether_fn_t ure_init; +static uether_fn_t ure_stop; +static uether_fn_t ure_start; +static uether_fn_t ure_tick; +static uether_fn_t ure_rxfilter; + +static int ure_ctl(struct ure_softc *, uint8_t, uint16_t, uint16_t, + void *, int); +static int ure_read_mem(struct ure_softc *, uint16_t, uint16_t, void *, + int); +static int ure_write_mem(struct ure_softc *, uint16_t, uint16_t, void *, + int); +static uint8_t ure_read_1(struct ure_softc *, uint16_t, uint16_t); +static uint16_t ure_read_2(struct ure_softc *, uint16_t, uint16_t); +static uint32_t ure_read_4(struct ure_softc *, uint16_t, uint16_t); +static int ure_write_1(struct ure_softc *, uint16_t, uint16_t, uint32_t); +static int ure_write_2(struct ure_softc *, uint16_t, uint16_t, uint32_t); +static int ure_write_4(struct ure_softc *, uint16_t, uint16_t, uint32_t); +static uint16_t ure_ocp_reg_read(struct ure_softc *, uint16_t); +static void ure_ocp_reg_write(struct ure_softc *, uint16_t, uint16_t); + +static void ure_read_chipver(struct ure_softc *); +static int ure_attach_post_sub(struct usb_ether *); +static void ure_reset(struct ure_softc *); +static int ure_ifmedia_upd(struct ifnet *); +static void ure_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int ure_ioctl(struct ifnet *, u_long, caddr_t); +static void ure_rtl8152_init(struct ure_softc *); +static void ure_rtl8153_init(struct ure_softc *); +static void ure_disable_teredo(struct ure_softc *); +static void ure_init_fifo(struct ure_softc *); + +static const struct usb_config ure_config[URE_N_TRANSFER] = { + [URE_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MCLBYTES, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = ure_bulk_write_callback, + .timeout = 10000, /* 10 seconds */ + }, + [URE_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = 16384, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = ure_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, +}; + +static device_method_t ure_methods[] = { + /* Device interface. */ + DEVMETHOD(device_probe, ure_probe), + DEVMETHOD(device_attach, ure_attach), + DEVMETHOD(device_detach, ure_detach), + + /* MII interface. */ + DEVMETHOD(miibus_readreg, ure_miibus_readreg), + DEVMETHOD(miibus_writereg, ure_miibus_writereg), + DEVMETHOD(miibus_statchg, ure_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t ure_driver = { + .name = "ure", + .methods = ure_methods, + .size = sizeof(struct ure_softc), +}; + +static devclass_t ure_devclass; + +DRIVER_MODULE(ure, uhub, ure_driver, ure_devclass, NULL, NULL); +DRIVER_MODULE(miibus, ure, miibus_driver, miibus_devclass, NULL, NULL); +MODULE_DEPEND(ure, uether, 1, 1, 1); +MODULE_DEPEND(ure, usb, 1, 1, 1); +MODULE_DEPEND(ure, ether, 1, 1, 1); +MODULE_DEPEND(ure, miibus, 1, 1, 1); +MODULE_VERSION(ure, 1); + +static const struct usb_ether_methods ure_ue_methods = { + .ue_attach_post = ure_attach_post, + .ue_attach_post_sub = ure_attach_post_sub, + .ue_start = ure_start, + .ue_init = ure_init, + .ue_stop = ure_stop, + .ue_tick = ure_tick, + .ue_setmulti = ure_rxfilter, + .ue_setpromisc = ure_rxfilter, + .ue_mii_upd = ure_ifmedia_upd, + .ue_mii_sts = ure_ifmedia_sts, +}; + +static int +ure_ctl(struct ure_softc *sc, uint8_t rw, uint16_t val, uint16_t index, + void *buf, int len) +{ + struct usb_device_request req; + + URE_LOCK_ASSERT(sc, MA_OWNED); + + if (rw == URE_CTL_WRITE) + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + else + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = UR_SET_ADDRESS; + USETW(req.wValue, val); + USETW(req.wIndex, index); + USETW(req.wLength, len); + + return (uether_do_request(&sc->sc_ue, &req, buf, 1000)); +} + +static int +ure_read_mem(struct ure_softc *sc, uint16_t addr, uint16_t index, + void *buf, int len) +{ + + return (ure_ctl(sc, URE_CTL_READ, addr, index, buf, len)); +} + +static int +ure_write_mem(struct ure_softc *sc, uint16_t addr, uint16_t index, + void *buf, int len) +{ + + return (ure_ctl(sc, URE_CTL_WRITE, addr, index, buf, len)); +} + +static uint8_t +ure_read_1(struct ure_softc *sc, uint16_t reg, uint16_t index) +{ + uint32_t val; + uint8_t temp[4]; + uint8_t shift; + + shift = (reg & 3) << 3; + reg &= ~3; + + ure_read_mem(sc, reg, index, &temp, 4); + val = UGETDW(temp); + val >>= shift; + + return (val & 0xff); +} + +static uint16_t +ure_read_2(struct ure_softc *sc, uint16_t reg, uint16_t index) +{ + uint32_t val; + uint8_t temp[4]; + uint8_t shift; + + shift = (reg & 2) << 3; + reg &= ~3; + + ure_read_mem(sc, reg, index, &temp, 4); + val = UGETDW(temp); + val >>= shift; + + return (val & 0xffff); +} + +static uint32_t +ure_read_4(struct ure_softc *sc, uint16_t reg, uint16_t index) +{ + uint8_t temp[4]; + + ure_read_mem(sc, reg, index, &temp, 4); + return (UGETDW(temp)); +} + +static int +ure_write_1(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val) +{ + uint16_t byen; + uint8_t temp[4]; + uint8_t shift; + + byen = URE_BYTE_EN_BYTE; + shift = reg & 3; + val &= 0xff; + + if (reg & 3) { + byen <<= shift; + val <<= (shift << 3); + reg &= ~3; + } + + USETDW(temp, val); + return (ure_write_mem(sc, reg, index | byen, &temp, 4)); +} + +static int +ure_write_2(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val) +{ + uint16_t byen; + uint8_t temp[4]; + uint8_t shift; + + byen = URE_BYTE_EN_WORD; + shift = reg & 2; + val &= 0xffff; + + if (reg & 2) { + byen <<= shift; + val <<= (shift << 3); + reg &= ~3; + } + + USETDW(temp, val); + return (ure_write_mem(sc, reg, index | byen, &temp, 4)); +} + +static int +ure_write_4(struct ure_softc *sc, uint16_t reg, uint16_t index, uint32_t val) +{ + uint8_t temp[4]; + + USETDW(temp, val); + return (ure_write_mem(sc, reg, index | URE_BYTE_EN_DWORD, &temp, 4)); +} + +static uint16_t +ure_ocp_reg_read(struct ure_softc *sc, uint16_t addr) +{ + uint16_t reg; + + ure_write_2(sc, URE_PLA_OCP_GPHY_BASE, URE_MCU_TYPE_PLA, addr & 0xf000); + reg = (addr & 0x0fff) | 0xb000; + + return (ure_read_2(sc, reg, URE_MCU_TYPE_PLA)); +} + +static void +ure_ocp_reg_write(struct ure_softc *sc, uint16_t addr, uint16_t data) +{ + uint16_t reg; + + ure_write_2(sc, URE_PLA_OCP_GPHY_BASE, URE_MCU_TYPE_PLA, addr & 0xf000); + reg = (addr & 0x0fff) | 0xb000; + + ure_write_2(sc, reg, URE_MCU_TYPE_PLA, data); +} + +static int +ure_miibus_readreg(device_t dev, int phy, int reg) +{ + struct ure_softc *sc; + uint16_t val; + int locked; + + sc = device_get_softc(dev); + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + URE_LOCK(sc); + + /* Let the rgephy driver read the URE_GMEDIASTAT register. */ + if (reg == URE_GMEDIASTAT) { + if (!locked) + URE_UNLOCK(sc); + return (ure_read_1(sc, URE_GMEDIASTAT, URE_MCU_TYPE_PLA)); + } + + val = ure_ocp_reg_read(sc, URE_OCP_BASE_MII + reg * 2); + + if (!locked) + URE_UNLOCK(sc); + return (val); +} + +static int +ure_miibus_writereg(device_t dev, int phy, int reg, int val) +{ + struct ure_softc *sc; + int locked; + + sc = device_get_softc(dev); + if (sc->sc_phyno != phy) + return (0); + + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + URE_LOCK(sc); + + ure_ocp_reg_write(sc, URE_OCP_BASE_MII + reg * 2, val); + + if (!locked) + URE_UNLOCK(sc); + return (0); +} + +static void +ure_miibus_statchg(device_t dev) +{ + struct ure_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + int locked; + + sc = device_get_softc(dev); + mii = GET_MII(sc); + locked = mtx_owned(&sc->sc_mtx); + if (!locked) + URE_LOCK(sc); + + ifp = uether_getifp(&sc->sc_ue); + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done; + + sc->sc_flags &= ~URE_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->sc_flags |= URE_FLAG_LINK; + break; + case IFM_1000_T: + if ((sc->sc_flags & URE_FLAG_8152) != 0) + break; + sc->sc_flags |= URE_FLAG_LINK; + break; + default: + break; + } + } + + /* Lost link, do nothing. */ + if ((sc->sc_flags & URE_FLAG_LINK) == 0) + goto done; +done: + if (!locked) + URE_UNLOCK(sc); +} + +/* + * Probe for a RTL8152/RTL8153 chip. + */ +static int +ure_probe(device_t dev) +{ + struct usb_attach_arg *uaa; + + uaa = device_get_ivars(dev); + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != URE_CONFIG_IDX) + return (ENXIO); + if (uaa->info.bIfaceIndex != URE_IFACE_IDX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(ure_devs, sizeof(ure_devs), uaa)); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +ure_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct ure_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + uint8_t iface_index; + int error; + + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); + device_set_usb_desc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + iface_index = URE_IFACE_IDX; + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + ure_config, URE_N_TRANSFER, sc, &sc->sc_mtx); + if (error != 0) { + device_printf(dev, "allocating USB transfers failed\n"); + goto detach; + } + + ue->ue_sc = sc; + ue->ue_dev = dev; + ue->ue_udev = uaa->device; + ue->ue_mtx = &sc->sc_mtx; + ue->ue_methods = &ure_ue_methods; + + error = uether_ifattach(ue); + if (error != 0) { + device_printf(dev, "could not attach interface\n"); + goto detach; + } + return (0); /* success */ + +detach: + ure_detach(dev); + return (ENXIO); /* failure */ +} + +static int +ure_detach(device_t dev) +{ + struct ure_softc *sc = device_get_softc(dev); + struct usb_ether *ue = &sc->sc_ue; + + usbd_transfer_unsetup(sc->sc_xfer, URE_N_TRANSFER); + uether_ifdetach(ue); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +ure_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ure_softc *sc = usbd_xfer_softc(xfer); + struct usb_ether *ue = &sc->sc_ue; + struct ifnet *ifp = uether_getifp(ue); + struct usb_page_cache *pc; + struct ure_rxpkt pkt; + int actlen, len; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (actlen < (int)(sizeof(pkt))) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &pkt, sizeof(pkt)); + len = le32toh(pkt.ure_pktlen) & URE_RXPKT_LEN_MASK; + len -= ETHER_CRC_LEN; + if (actlen < (int)(len + sizeof(pkt))) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + goto tr_setup; + } + + uether_rxbuf(ue, pc, sizeof(pkt), len); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + uether_rxflush(ue); + return; + + default: /* Error */ + DPRINTF("bulk read error, %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +ure_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ure_softc *sc = usbd_xfer_softc(xfer); + struct ifnet *ifp = uether_getifp(&sc->sc_ue); + struct usb_page_cache *pc; + struct mbuf *m; + struct ure_txpkt txpkt; + int len, pos; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete\n"); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if ((sc->sc_flags & URE_FLAG_LINK) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { + /* + * don't send anything if there is no link ! + */ + return; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + pos = 0; + len = m->m_pkthdr.len; + pc = usbd_xfer_get_frame(xfer, 0); + memset(&txpkt, 0, sizeof(txpkt)); + txpkt.ure_pktlen = htole32((len & URE_TXPKT_LEN_MASK) | + URE_TKPKT_TX_FS | URE_TKPKT_TX_LS); + usbd_copy_in(pc, pos, &txpkt, sizeof(txpkt)); + pos += sizeof(txpkt); + usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len); + pos += m->m_pkthdr.len; + + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + + /* + * If there's a BPF listener, bounce a copy + * of this frame to him. + */ + BPF_MTAP(ifp, m); + + m_freem(m); + + /* Set frame length. */ + usbd_xfer_set_frame_len(xfer, 0, pos); + + usbd_transfer_submit(xfer); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + return; + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +ure_read_chipver(struct ure_softc *sc) +{ + uint16_t ver; + + ver = ure_read_2(sc, URE_PLA_TCR1, URE_MCU_TYPE_PLA) & URE_VERSION_MASK; + switch (ver) { + case 0x4c00: + sc->sc_chip |= URE_CHIP_VER_4C00; + break; + case 0x4c10: + sc->sc_chip |= URE_CHIP_VER_4C10; + break; + case 0x5c00: + sc->sc_chip |= URE_CHIP_VER_5C00; + break; + case 0x5c10: + sc->sc_chip |= URE_CHIP_VER_5C10; + break; + case 0x5c20: + sc->sc_chip |= URE_CHIP_VER_5C20; + break; + case 0x5c30: + sc->sc_chip |= URE_CHIP_VER_5C30; + break; + default: + device_printf(sc->sc_ue.ue_dev, + "unknown version 0x%04x\n", ver); + break; + } +} + +static void +ure_attach_post(struct usb_ether *ue) +{ + struct ure_softc *sc = uether_getsc(ue); + + sc->sc_phyno = 0; + + /* Determine the chip version. */ + ure_read_chipver(sc); + + /* Initialize controller and get station address. */ + if (sc->sc_flags & URE_FLAG_8152) + ure_rtl8152_init(sc); + else + ure_rtl8153_init(sc); + + if (sc->sc_chip & URE_CHIP_VER_4C00) + ure_read_mem(sc, URE_PLA_IDR, URE_MCU_TYPE_PLA, + ue->ue_eaddr, 8); + else + ure_read_mem(sc, URE_PLA_BACKUP, URE_MCU_TYPE_PLA, + ue->ue_eaddr, 8); +} + +static int +ure_attach_post_sub(struct usb_ether *ue) +{ + struct ure_softc *sc; + struct ifnet *ifp; + int error; + + sc = uether_getsc(ue); + ifp = ue->ue_ifp; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = uether_start; + ifp->if_ioctl = ure_ioctl; + ifp->if_init = uether_init; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + mtx_lock(&Giant); + error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, + uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, + BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, 0); + mtx_unlock(&Giant); + + return (error); +} + +static void +ure_init(struct usb_ether *ue) +{ + struct ure_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + URE_LOCK_ASSERT(sc, MA_OWNED); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + /* Cancel pending I/O. */ + ure_stop(ue); + + ure_reset(sc); + + /* Set MAC address. */ + ure_write_mem(sc, URE_PLA_IDR, URE_MCU_TYPE_PLA | URE_BYTE_EN_SIX_BYTES, + IF_LLADDR(ifp), 8); + + /* Reset the packet filter. */ + ure_write_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA) & + ~URE_FMC_FCR_MCU_EN); + ure_write_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_FMC, URE_MCU_TYPE_PLA) | + URE_FMC_FCR_MCU_EN); + + /* Enable transmit and receive. */ + ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA, + ure_read_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA) | URE_CR_RE | + URE_CR_TE); + + ure_write_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) & + ~URE_RXDY_GATED_EN); + + /* Configure RX filters. */ + ure_rxfilter(ue); + + usbd_xfer_set_stall(sc->sc_xfer[URE_BULK_DT_WR]); + + /* Indicate we are up and running. */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + /* Switch to selected media. */ + ure_ifmedia_upd(ifp); +} + +static void +ure_tick(struct usb_ether *ue) +{ + struct ure_softc *sc = uether_getsc(ue); + struct mii_data *mii = GET_MII(sc); + + URE_LOCK_ASSERT(sc, MA_OWNED); + + mii_tick(mii); + if ((sc->sc_flags & URE_FLAG_LINK) == 0 + && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->sc_flags |= URE_FLAG_LINK; + ure_start(ue); + } +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void +ure_rxfilter(struct usb_ether *ue) +{ + struct ure_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + struct ifmultiaddr *ifma; + uint32_t h, rxmode; + uint32_t hashes[2] = { 0, 0 }; + + URE_LOCK_ASSERT(sc, MA_OWNED); + + rxmode = URE_RCR_APM; + if (ifp->if_flags & IFF_BROADCAST) + rxmode |= URE_RCR_AB; + if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { + if (ifp->if_flags & IFF_PROMISC) + rxmode |= URE_RCR_AAP; + rxmode |= URE_RCR_AM; + hashes[0] = hashes[1] = 0xffffffff; + goto done; + } + + rxmode |= URE_RCR_AM; + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + if_maddr_runlock(ifp); + + h = bswap32(hashes[0]); + hashes[0] = bswap32(hashes[1]); + hashes[1] = h; + rxmode |= URE_RCR_AM; + +done: + ure_write_4(sc, URE_PLA_MAR0, URE_MCU_TYPE_PLA, hashes[0]); + ure_write_4(sc, URE_PLA_MAR4, URE_MCU_TYPE_PLA, hashes[1]); + ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA, rxmode); +} + +static void +ure_start(struct usb_ether *ue) +{ + struct ure_softc *sc = uether_getsc(ue); + + /* + * start the USB transfers, if not already started: + */ + usbd_transfer_start(sc->sc_xfer[URE_BULK_DT_RD]); + usbd_transfer_start(sc->sc_xfer[URE_BULK_DT_WR]); +} + +static void +ure_reset(struct ure_softc *sc) +{ + int i; + + ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA, URE_CR_RST); + + for (i = 0; i < URE_TIMEOUT; i++) { + if (!(ure_read_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA) & + URE_CR_RST)) + break; + uether_pause(&sc->sc_ue, hz / 100); + } + if (i == URE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, "reset never completed\n"); +} + +/* + * Set media options. + */ +static int +ure_ifmedia_upd(struct ifnet *ifp) +{ + struct ure_softc *sc = ifp->if_softc; + struct mii_data *mii = GET_MII(sc); + struct mii_softc *miisc; + int error; + + URE_LOCK_ASSERT(sc, MA_OWNED); + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + return (error); +} + +/* + * Report current media status. + */ +static void +ure_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct ure_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = GET_MII(sc); + + URE_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + URE_UNLOCK(sc); +} + +static int +ure_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct usb_ether *ue = ifp->if_softc; + struct ure_softc *sc; + struct ifreq *ifr; + int error, mask, reinit; + + sc = uether_getsc(ue); + ifr = (struct ifreq *)data; + error = 0; + reinit = 0; + if (cmd == SIOCSIFCAP) { + URE_LOCK(sc); + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING) + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + else + reinit = 0; + URE_UNLOCK(sc); + if (reinit > 0) + uether_init(ue); + } else + error = uether_ioctl(ifp, cmd, data); + + return (error); +} + +static void +ure_rtl8152_init(struct ure_softc *sc) +{ + uint32_t pwrctrl; + + /* Disable ALDPS. */ + ure_ocp_reg_write(sc, URE_OCP_ALDPS_CONFIG, URE_ENPDNPS | URE_LINKENA | + URE_DIS_SDSAVE); + uether_pause(&sc->sc_ue, hz / 50); + + if (sc->sc_chip & URE_CHIP_VER_4C00) { + ure_write_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA) & + ~URE_LED_MODE_MASK); + } + + ure_write_2(sc, URE_USB_UPS_CTRL, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_UPS_CTRL, URE_MCU_TYPE_USB) & + ~URE_POWER_CUT); + ure_write_2(sc, URE_USB_PM_CTRL_STATUS, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_PM_CTRL_STATUS, URE_MCU_TYPE_USB) & + ~URE_RESUME_INDICATE); + + ure_write_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA) | + URE_TX_10M_IDLE_EN | URE_PFM_PWM_SWITCH); + pwrctrl = ure_read_4(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA); + pwrctrl &= ~URE_MCU_CLK_RATIO_MASK; + pwrctrl |= URE_MCU_CLK_RATIO | URE_D3_CLK_GATED_EN; + ure_write_4(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA, pwrctrl); + ure_write_2(sc, URE_PLA_GPHY_INTR_IMR, URE_MCU_TYPE_PLA, + URE_GPHY_STS_MSK | URE_SPEED_DOWN_MSK | URE_SPDWN_RXDV_MSK | + URE_SPDWN_LINKCHG_MSK); + + /* Disable Rx aggregation. */ + ure_write_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB) | + URE_RX_AGG_DISABLE); + + /* Disable ALDPS. */ + ure_ocp_reg_write(sc, URE_OCP_ALDPS_CONFIG, URE_ENPDNPS | URE_LINKENA | + URE_DIS_SDSAVE); + uether_pause(&sc->sc_ue, hz / 50); + + ure_init_fifo(sc); + + ure_write_1(sc, URE_USB_TX_AGG, URE_MCU_TYPE_USB, + URE_TX_AGG_MAX_THRESHOLD); + ure_write_4(sc, URE_USB_RX_BUF_TH, URE_MCU_TYPE_USB, URE_RX_THR_HIGH); + ure_write_4(sc, URE_USB_TX_DMA, URE_MCU_TYPE_USB, + URE_TEST_MODE_DISABLE | URE_TX_SIZE_ADJUST1); +} + +static void +ure_rtl8153_init(struct ure_softc *sc) +{ + uint16_t val; + uint8_t u1u2[8]; + int i; + + /* Disable ALDPS. */ + ure_ocp_reg_write(sc, URE_OCP_POWER_CFG, + ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) & ~URE_EN_ALDPS); + uether_pause(&sc->sc_ue, hz / 50); + + memset(u1u2, 0x00, sizeof(u1u2)); + ure_write_mem(sc, URE_USB_TOLERANCE, + URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2)); + + for (i = 0; i < URE_TIMEOUT; i++) { + if (ure_read_2(sc, URE_PLA_BOOT_CTRL, URE_MCU_TYPE_PLA) & + URE_AUTOLOAD_DONE) + break; + uether_pause(&sc->sc_ue, hz / 100); + } + if (i == URE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, + "timeout waiting for chip autoload\n"); + + for (i = 0; i < URE_TIMEOUT; i++) { + val = ure_ocp_reg_read(sc, URE_OCP_PHY_STATUS) & + URE_PHY_STAT_MASK; + if (val == URE_PHY_STAT_LAN_ON || val == URE_PHY_STAT_PWRDN) + break; + uether_pause(&sc->sc_ue, hz / 100); + } + if (i == URE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, + "timeout waiting for phy to stabilize\n"); + + ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB) & + ~URE_U2P3_ENABLE); + + if (sc->sc_chip & URE_CHIP_VER_5C10) { + val = ure_read_2(sc, URE_USB_SSPHYLINK2, URE_MCU_TYPE_USB); + val &= ~URE_PWD_DN_SCALE_MASK; + val |= URE_PWD_DN_SCALE(96); + ure_write_2(sc, URE_USB_SSPHYLINK2, URE_MCU_TYPE_USB, val); + + ure_write_1(sc, URE_USB_USB2PHY, URE_MCU_TYPE_USB, + ure_read_1(sc, URE_USB_USB2PHY, URE_MCU_TYPE_USB) | + URE_USB2PHY_L1 | URE_USB2PHY_SUSPEND); + } else if (sc->sc_chip & URE_CHIP_VER_5C20) { + ure_write_1(sc, URE_PLA_DMY_REG0, URE_MCU_TYPE_PLA, + ure_read_1(sc, URE_PLA_DMY_REG0, URE_MCU_TYPE_PLA) & + ~URE_ECM_ALDPS); + } + if (sc->sc_chip & (URE_CHIP_VER_5C20 | URE_CHIP_VER_5C30)) { + val = ure_read_1(sc, URE_USB_CSR_DUMMY1, URE_MCU_TYPE_USB); + if (ure_read_2(sc, URE_USB_BURST_SIZE, URE_MCU_TYPE_USB) == + 0) + val &= ~URE_DYNAMIC_BURST; + else + val |= URE_DYNAMIC_BURST; + ure_write_1(sc, URE_USB_CSR_DUMMY1, URE_MCU_TYPE_USB, val); + } + + ure_write_1(sc, URE_USB_CSR_DUMMY2, URE_MCU_TYPE_USB, + ure_read_1(sc, URE_USB_CSR_DUMMY2, URE_MCU_TYPE_USB) | + URE_EP4_FULL_FC); + + ure_write_2(sc, URE_USB_WDT11_CTRL, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_WDT11_CTRL, URE_MCU_TYPE_USB) & + ~URE_TIMER11_EN); + + ure_write_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA) & + ~URE_LED_MODE_MASK); + + if ((sc->sc_chip & URE_CHIP_VER_5C10) && + usbd_get_speed(sc->sc_ue.ue_udev) != USB_SPEED_SUPER) + val = URE_LPM_TIMER_500MS; + else + val = URE_LPM_TIMER_500US; + ure_write_1(sc, URE_USB_LPM_CTRL, URE_MCU_TYPE_USB, + val | URE_FIFO_EMPTY_1FB | URE_ROK_EXIT_LPM); + + val = ure_read_2(sc, URE_USB_AFE_CTRL2, URE_MCU_TYPE_USB); + val &= ~URE_SEN_VAL_MASK; + val |= URE_SEN_VAL_NORMAL | URE_SEL_RXIDLE; + ure_write_2(sc, URE_USB_AFE_CTRL2, URE_MCU_TYPE_USB, val); + + ure_write_2(sc, URE_USB_CONNECT_TIMER, URE_MCU_TYPE_USB, 0x0001); + + ure_write_2(sc, URE_USB_POWER_CUT, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_POWER_CUT, URE_MCU_TYPE_USB) & + ~(URE_PWR_EN | URE_PHASE2_EN)); + ure_write_2(sc, URE_USB_MISC_0, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_MISC_0, URE_MCU_TYPE_USB) & + ~URE_PCUT_STATUS); + + memset(u1u2, 0xff, sizeof(u1u2)); + ure_write_mem(sc, URE_USB_TOLERANCE, + URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2)); + + ure_write_2(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA, + URE_ALDPS_SPDWN_RATIO); + ure_write_2(sc, URE_PLA_MAC_PWR_CTRL2, URE_MCU_TYPE_PLA, + URE_EEE_SPDWN_RATIO); + ure_write_2(sc, URE_PLA_MAC_PWR_CTRL3, URE_MCU_TYPE_PLA, + URE_PKT_AVAIL_SPDWN_EN | URE_SUSPEND_SPDWN_EN | + URE_U1U2_SPDWN_EN | URE_L1_SPDWN_EN); + ure_write_2(sc, URE_PLA_MAC_PWR_CTRL4, URE_MCU_TYPE_PLA, + URE_PWRSAVE_SPDWN_EN | URE_RXDV_SPDWN_EN | URE_TX10MIDLE_EN | + URE_TP100_SPDWN_EN | URE_TP500_SPDWN_EN | URE_TP1000_SPDWN_EN | + URE_EEE_SPDWN_EN); + + val = ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB); + if (!(sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10))) + val |= URE_U2P3_ENABLE; + else + val &= ~URE_U2P3_ENABLE; + ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB, val); + + memset(u1u2, 0x00, sizeof(u1u2)); + ure_write_mem(sc, URE_USB_TOLERANCE, + URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2)); + + /* Disable ALDPS. */ + ure_ocp_reg_write(sc, URE_OCP_POWER_CFG, + ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) & ~URE_EN_ALDPS); + uether_pause(&sc->sc_ue, hz / 50); + + ure_init_fifo(sc); + + /* Disable Rx aggregation. */ + ure_write_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB) | + URE_RX_AGG_DISABLE); + + val = ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB); + if (!(sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10))) + val |= URE_U2P3_ENABLE; + else + val &= ~URE_U2P3_ENABLE; + ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB, val); + + memset(u1u2, 0xff, sizeof(u1u2)); + ure_write_mem(sc, URE_USB_TOLERANCE, + URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2)); +} + +static void +ure_stop(struct usb_ether *ue) +{ + struct ure_softc *sc = uether_getsc(ue); + struct ifnet *ifp = uether_getifp(ue); + + URE_LOCK_ASSERT(sc, MA_OWNED); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->sc_flags &= ~URE_FLAG_LINK; + + /* + * stop all the transfers, if not already stopped: + */ + usbd_transfer_stop(sc->sc_xfer[URE_BULK_DT_WR]); + usbd_transfer_stop(sc->sc_xfer[URE_BULK_DT_RD]); +} + +static void +ure_disable_teredo(struct ure_softc *sc) +{ + + ure_write_4(sc, URE_PLA_TEREDO_CFG, URE_MCU_TYPE_PLA, + ure_read_4(sc, URE_PLA_TEREDO_CFG, URE_MCU_TYPE_PLA) & + ~(URE_TEREDO_SEL | URE_TEREDO_RS_EVENT_MASK | URE_OOB_TEREDO_EN)); + ure_write_2(sc, URE_PLA_WDT6_CTRL, URE_MCU_TYPE_PLA, + URE_WDT6_SET_MODE); + ure_write_2(sc, URE_PLA_REALWOW_TIMER, URE_MCU_TYPE_PLA, 0); + ure_write_4(sc, URE_PLA_TEREDO_TIMER, URE_MCU_TYPE_PLA, 0); +} + +static void +ure_init_fifo(struct ure_softc *sc) +{ + uint32_t rx_fifo1, rx_fifo2; + int i; + + ure_write_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) | + URE_RXDY_GATED_EN); + + ure_disable_teredo(sc); + + ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA, + ure_read_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA) & + ~URE_RCR_ACPT_ALL); + + if (!(sc->sc_flags & URE_FLAG_8152)) { + if (sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10 | + URE_CHIP_VER_5C20)) { + ure_ocp_reg_write(sc, URE_OCP_ADC_CFG, + URE_CKADSEL_L | URE_ADC_EN | URE_EN_EMI_L); + } + if (sc->sc_chip & URE_CHIP_VER_5C00) { + ure_ocp_reg_write(sc, URE_OCP_EEE_CFG, + ure_ocp_reg_read(sc, URE_OCP_EEE_CFG) & + ~URE_CTAP_SHORT_EN); + } + ure_ocp_reg_write(sc, URE_OCP_POWER_CFG, + ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) | + URE_EEE_CLKDIV_EN); + ure_ocp_reg_write(sc, URE_OCP_DOWN_SPEED, + ure_ocp_reg_read(sc, URE_OCP_DOWN_SPEED) | + URE_EN_10M_BGOFF); + ure_ocp_reg_write(sc, URE_OCP_POWER_CFG, + ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) | + URE_EN_10M_PLLOFF); + ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_IMPEDANCE); + ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x0b13); + ure_write_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA) | + URE_PFM_PWM_SWITCH); + + /* Enable LPF corner auto tune. */ + ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_LPF_CFG); + ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0xf70f); + + /* Adjust 10M amplitude. */ + ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_10M_AMP1); + ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x00af); + ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_10M_AMP2); + ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x0208); + } + + ure_reset(sc); + + ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA, 0); + + ure_write_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA, + ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) & + ~URE_NOW_IS_OOB); + + ure_write_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) & + ~URE_MCU_BORW_EN); + for (i = 0; i < URE_TIMEOUT; i++) { + if (ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) & + URE_LINK_LIST_READY) + break; + uether_pause(&sc->sc_ue, hz / 100); + } + if (i == URE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, + "timeout waiting for OOB control\n"); + ure_write_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_SFF_STS_7, URE_MCU_TYPE_PLA) | + URE_RE_INIT_LL); + for (i = 0; i < URE_TIMEOUT; i++) { + if (ure_read_1(sc, URE_PLA_OOB_CTRL, URE_MCU_TYPE_PLA) & + URE_LINK_LIST_READY) + break; + uether_pause(&sc->sc_ue, hz / 100); + } + if (i == URE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, + "timeout waiting for OOB control\n"); + + ure_write_2(sc, URE_PLA_CPCR, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_CPCR, URE_MCU_TYPE_PLA) & + ~URE_CPCR_RX_VLAN); + ure_write_2(sc, URE_PLA_TCR0, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_TCR0, URE_MCU_TYPE_PLA) | + URE_TCR0_AUTO_FIFO); + + /* Configure Rx FIFO threshold. */ + ure_write_4(sc, URE_PLA_RXFIFO_CTRL0, URE_MCU_TYPE_PLA, + URE_RXFIFO_THR1_NORMAL); + if (usbd_get_speed(sc->sc_ue.ue_udev) == USB_SPEED_FULL) { + rx_fifo1 = URE_RXFIFO_THR2_FULL; + rx_fifo2 = URE_RXFIFO_THR3_FULL; + } else { + rx_fifo1 = URE_RXFIFO_THR2_HIGH; + rx_fifo2 = URE_RXFIFO_THR3_HIGH; + } + ure_write_4(sc, URE_PLA_RXFIFO_CTRL1, URE_MCU_TYPE_PLA, rx_fifo1); + ure_write_4(sc, URE_PLA_RXFIFO_CTRL2, URE_MCU_TYPE_PLA, rx_fifo2); + + /* Configure Tx FIFO threshold. */ + ure_write_4(sc, URE_PLA_TXFIFO_CTRL, URE_MCU_TYPE_PLA, + URE_TXFIFO_THR_NORMAL); +} diff --git a/freebsd/sys/dev/usb/net/if_urereg.h b/freebsd/sys/dev/usb/net/if_urereg.h new file mode 100644 index 00000000..8eff1c25 --- /dev/null +++ b/freebsd/sys/dev/usb/net/if_urereg.h @@ -0,0 +1,440 @@ +/*- + * Copyright (c) 2015-2016 Kevin Lo <kevlo@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define URE_CONFIG_IDX 0 /* config number 1 */ +#define URE_IFACE_IDX 0 + +#define URE_CTL_READ 0x01 +#define URE_CTL_WRITE 0x02 + +#define URE_TIMEOUT 1000 +#define URE_PHY_TIMEOUT 2000 + +#define URE_BYTE_EN_DWORD 0xff +#define URE_BYTE_EN_WORD 0x33 +#define URE_BYTE_EN_BYTE 0x11 +#define URE_BYTE_EN_SIX_BYTES 0x3f + +#define URE_MAX_FRAMELEN (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) + +#define URE_PLA_IDR 0xc000 +#define URE_PLA_RCR 0xc010 +#define URE_PLA_RMS 0xc016 +#define URE_PLA_RXFIFO_CTRL0 0xc0a0 +#define URE_PLA_RXFIFO_CTRL1 0xc0a4 +#define URE_PLA_RXFIFO_CTRL2 0xc0a8 +#define URE_PLA_DMY_REG0 0xc0b0 +#define URE_PLA_FMC 0xc0b4 +#define URE_PLA_CFG_WOL 0xc0b6 +#define URE_PLA_TEREDO_CFG 0xc0bc +#define URE_PLA_MAR0 0xcd00 +#define URE_PLA_MAR4 0xcd04 +#define URE_PLA_BACKUP 0xd000 +#define URE_PAL_BDC_CR 0xd1a0 +#define URE_PLA_TEREDO_TIMER 0xd2cc +#define URE_PLA_REALWOW_TIMER 0xd2e8 +#define URE_PLA_LEDSEL 0xdd90 +#define URE_PLA_LED_FEATURE 0xdd92 +#define URE_PLA_PHYAR 0xde00 +#define URE_PLA_BOOT_CTRL 0xe004 +#define URE_PLA_GPHY_INTR_IMR 0xe022 +#define URE_PLA_EEE_CR 0xe040 +#define URE_PLA_EEEP_CR 0xe080 +#define URE_PLA_MAC_PWR_CTRL 0xe0c0 +#define URE_PLA_MAC_PWR_CTRL2 0xe0ca +#define URE_PLA_MAC_PWR_CTRL3 0xe0cc +#define URE_PLA_MAC_PWR_CTRL4 0xe0ce +#define URE_PLA_WDT6_CTRL 0xe428 +#define URE_PLA_TCR0 0xe610 +#define URE_PLA_TCR1 0xe612 +#define URE_PLA_MTPS 0xe615 +#define URE_PLA_TXFIFO_CTRL 0xe618 +#define URE_PLA_RSTTELLY 0xe800 +#define URE_PLA_CR 0xe813 +#define URE_PLA_CRWECR 0xe81c +#define URE_PLA_CONFIG5 0xe822 +#define URE_PLA_PHY_PWR 0xe84c +#define URE_PLA_OOB_CTRL 0xe84f +#define URE_PLA_CPCR 0xe854 +#define URE_PLA_MISC_0 0xe858 +#define URE_PLA_MISC_1 0xe85a +#define URE_PLA_OCP_GPHY_BASE 0xe86c +#define URE_PLA_TELLYCNT 0xe890 +#define URE_PLA_SFF_STS_7 0xe8de +#define URE_GMEDIASTAT 0xe908 + +#define URE_USB_USB2PHY 0xb41e +#define URE_USB_SSPHYLINK2 0xb428 +#define URE_USB_U2P3_CTRL 0xb460 +#define URE_USB_CSR_DUMMY1 0xb464 +#define URE_USB_CSR_DUMMY2 0xb466 +#define URE_USB_DEV_STAT 0xb808 +#define URE_USB_CONNECT_TIMER 0xcbf8 +#define URE_USB_BURST_SIZE 0xcfc0 +#define URE_USB_USB_CTRL 0xd406 +#define URE_USB_PHY_CTRL 0xd408 +#define URE_USB_TX_AGG 0xd40a +#define URE_USB_RX_BUF_TH 0xd40c +#define URE_USB_USB_TIMER 0xd428 +#define URE_USB_RX_EARLY_AGG 0xd42c +#define URE_USB_PM_CTRL_STATUS 0xd432 +#define URE_USB_TX_DMA 0xd434 +#define URE_USB_TOLERANCE 0xd490 +#define URE_USB_LPM_CTRL 0xd41a +#define URE_USB_UPS_CTRL 0xd800 +#define URE_USB_MISC_0 0xd81a +#define URE_USB_POWER_CUT 0xd80a +#define URE_USB_AFE_CTRL2 0xd824 +#define URE_USB_WDT11_CTRL 0xe43c + +/* OCP Registers. */ +#define URE_OCP_ALDPS_CONFIG 0x2010 +#define URE_OCP_EEE_CONFIG1 0x2080 +#define URE_OCP_EEE_CONFIG2 0x2092 +#define URE_OCP_EEE_CONFIG3 0x2094 +#define URE_OCP_BASE_MII 0xa400 +#define URE_OCP_EEE_AR 0xa41a +#define URE_OCP_EEE_DATA 0xa41c +#define URE_OCP_PHY_STATUS 0xa420 +#define URE_OCP_POWER_CFG 0xa430 +#define URE_OCP_EEE_CFG 0xa432 +#define URE_OCP_SRAM_ADDR 0xa436 +#define URE_OCP_SRAM_DATA 0xa438 +#define URE_OCP_DOWN_SPEED 0xa442 +#define URE_OCP_EEE_ABLE 0xa5c4 +#define URE_OCP_EEE_ADV 0xa5d0 +#define URE_OCP_EEE_LPABLE 0xa5d2 +#define URE_OCP_PHY_STATE 0xa708 +#define URE_OCP_ADC_CFG 0xbc06 + +/* SRAM Register. */ +#define URE_SRAM_LPF_CFG 0x8012 +#define URE_SRAM_10M_AMP1 0x8080 +#define URE_SRAM_10M_AMP2 0x8082 +#define URE_SRAM_IMPEDANCE 0x8084 + +/* PLA_RCR */ +#define URE_RCR_AAP 0x00000001 +#define URE_RCR_APM 0x00000002 +#define URE_RCR_AM 0x00000004 +#define URE_RCR_AB 0x00000008 +#define URE_RCR_ACPT_ALL \ + (URE_RCR_AAP | URE_RCR_APM | URE_RCR_AM | URE_RCR_AB) + +/* PLA_RXFIFO_CTRL0 */ +#define URE_RXFIFO_THR1_NORMAL 0x00080002 +#define URE_RXFIFO_THR1_OOB 0x01800003 + +/* PLA_RXFIFO_CTRL1 */ +#define URE_RXFIFO_THR2_FULL 0x00000060 +#define URE_RXFIFO_THR2_HIGH 0x00000038 +#define URE_RXFIFO_THR2_OOB 0x0000004a +#define URE_RXFIFO_THR2_NORMAL 0x00a0 + +/* PLA_RXFIFO_CTRL2 */ +#define URE_RXFIFO_THR3_FULL 0x00000078 +#define URE_RXFIFO_THR3_HIGH 0x00000048 +#define URE_RXFIFO_THR3_OOB 0x0000005a +#define URE_RXFIFO_THR3_NORMAL 0x0110 + +/* PLA_TXFIFO_CTRL */ +#define URE_TXFIFO_THR_NORMAL 0x00400008 +#define URE_TXFIFO_THR_NORMAL2 0x01000008 + +/* PLA_DMY_REG0 */ +#define URE_ECM_ALDPS 0x0002 + +/* PLA_FMC */ +#define URE_FMC_FCR_MCU_EN 0x0001 + +/* PLA_EEEP_CR */ +#define URE_EEEP_CR_EEEP_TX 0x0002 + +/* PLA_WDT6_CTRL */ +#define URE_WDT6_SET_MODE 0x001 + +/* PLA_TCR0 */ +#define URE_TCR0_TX_EMPTY 0x0800 +#define URE_TCR0_AUTO_FIFO 0x0080 + +/* PLA_TCR1 */ +#define URE_VERSION_MASK 0x7cf0 + +/* PLA_CR */ +#define URE_CR_RST 0x10 +#define URE_CR_RE 0x08 +#define URE_CR_TE 0x04 + +/* PLA_CRWECR */ +#define URE_CRWECR_NORAML 0x00 +#define URE_CRWECR_CONFIG 0xc0 + +/* PLA_OOB_CTRL */ +#define URE_NOW_IS_OOB 0x80 +#define URE_TXFIFO_EMPTY 0x20 +#define URE_RXFIFO_EMPTY 0x10 +#define URE_LINK_LIST_READY 0x02 +#define URE_DIS_MCU_CLROOB 0x01 +#define URE_FIFO_EMPTY (URE_TXFIFO_EMPTY | URE_RXFIFO_EMPTY) + +/* PLA_MISC_1 */ +#define URE_RXDY_GATED_EN 0x0008 + +/* PLA_SFF_STS_7 */ +#define URE_RE_INIT_LL 0x8000 +#define URE_MCU_BORW_EN 0x4000 + +/* PLA_CPCR */ +#define URE_CPCR_RX_VLAN 0x0040 + +/* PLA_TEREDO_CFG */ +#define URE_TEREDO_SEL 0x8000 +#define URE_TEREDO_WAKE_MASK 0x7f00 +#define URE_TEREDO_RS_EVENT_MASK 0x00fe +#define URE_OOB_TEREDO_EN 0x0001 + +/* PAL_BDC_CR */ +#define URE_ALDPS_PROXY_MODE 0x0001 + +/* PLA_CONFIG5 */ +#define URE_LAN_WAKE_EN 0x0002 + +/* PLA_LED_FEATURE */ +#define URE_LED_MODE_MASK 0x0700 + +/* PLA_PHY_PWR */ +#define URE_TX_10M_IDLE_EN 0x0080 +#define URE_PFM_PWM_SWITCH 0x0040 + +/* PLA_MAC_PWR_CTRL */ +#define URE_D3_CLK_GATED_EN 0x00004000 +#define URE_MCU_CLK_RATIO 0x07010f07 +#define URE_MCU_CLK_RATIO_MASK 0x0f0f0f0f +#define URE_ALDPS_SPDWN_RATIO 0x0f87 + +/* PLA_MAC_PWR_CTRL2 */ +#define URE_EEE_SPDWN_RATIO 0x8007 + +/* PLA_MAC_PWR_CTRL3 */ +#define URE_PKT_AVAIL_SPDWN_EN 0x0100 +#define URE_SUSPEND_SPDWN_EN 0x0004 +#define URE_U1U2_SPDWN_EN 0x0002 +#define URE_L1_SPDWN_EN 0x0001 + +/* PLA_MAC_PWR_CTRL4 */ +#define URE_PWRSAVE_SPDWN_EN 0x1000 +#define URE_RXDV_SPDWN_EN 0x0800 +#define URE_TX10MIDLE_EN 0x0100 +#define URE_TP100_SPDWN_EN 0x0020 +#define URE_TP500_SPDWN_EN 0x0010 +#define URE_TP1000_SPDWN_EN 0x0008 +#define URE_EEE_SPDWN_EN 0x0001 + +/* PLA_GPHY_INTR_IMR */ +#define URE_GPHY_STS_MSK 0x0001 +#define URE_SPEED_DOWN_MSK 0x0002 +#define URE_SPDWN_RXDV_MSK 0x0004 +#define URE_SPDWN_LINKCHG_MSK 0x0008 + +/* PLA_PHYAR */ +#define URE_PHYAR_PHYDATA 0x0000ffff +#define URE_PHYAR_BUSY 0x80000000 + +/* PLA_EEE_CR */ +#define URE_EEE_RX_EN 0x0001 +#define URE_EEE_TX_EN 0x0002 + +/* PLA_BOOT_CTRL */ +#define URE_AUTOLOAD_DONE 0x0002 + +/* USB_USB2PHY */ +#define URE_USB2PHY_SUSPEND 0x0001 +#define URE_USB2PHY_L1 0x0002 + +/* USB_SSPHYLINK2 */ +#define URE_PWD_DN_SCALE_MASK 0x3ffe +#define URE_PWD_DN_SCALE(x) ((x) << 1) + +/* USB_CSR_DUMMY1 */ +#define URE_DYNAMIC_BURST 0x0001 + +/* USB_CSR_DUMMY2 */ +#define URE_EP4_FULL_FC 0x0001 + +/* USB_DEV_STAT */ +#define URE_STAT_SPEED_MASK 0x0006 +#define URE_STAT_SPEED_HIGH 0x0000 +#define URE_STAT_SPEED_FULL 0x0001 + +/* USB_TX_AGG */ +#define URE_TX_AGG_MAX_THRESHOLD 0x03 + +/* USB_RX_BUF_TH */ +#define URE_RX_THR_SUPER 0x0c350180 +#define URE_RX_THR_HIGH 0x7a120180 +#define URE_RX_THR_SLOW 0xffff0180 + +/* USB_TX_DMA */ +#define URE_TEST_MODE_DISABLE 0x00000001 +#define URE_TX_SIZE_ADJUST1 0x00000100 + +/* USB_UPS_CTRL */ +#define URE_POWER_CUT 0x0100 + +/* USB_PM_CTRL_STATUS */ +#define URE_RESUME_INDICATE 0x0001 + +/* USB_USB_CTRL */ +#define URE_RX_AGG_DISABLE 0x0010 +#define URE_RX_ZERO_EN 0x0080 + +/* USB_U2P3_CTRL */ +#define URE_U2P3_ENABLE 0x0001 + +/* USB_POWER_CUT */ +#define URE_PWR_EN 0x0001 +#define URE_PHASE2_EN 0x0008 + +/* USB_MISC_0 */ +#define URE_PCUT_STATUS 0x0001 + +/* USB_RX_EARLY_TIMEOUT */ +#define URE_COALESCE_SUPER 85000U +#define URE_COALESCE_HIGH 250000U +#define URE_COALESCE_SLOW 524280U + +/* USB_WDT11_CTRL */ +#define URE_TIMER11_EN 0x0001 + +/* USB_LPM_CTRL */ +#define URE_FIFO_EMPTY_1FB 0x30 +#define URE_LPM_TIMER_MASK 0x0c +#define URE_LPM_TIMER_500MS 0x04 +#define URE_LPM_TIMER_500US 0x0c +#define URE_ROK_EXIT_LPM 0x02 + +/* USB_AFE_CTRL2 */ +#define URE_SEN_VAL_MASK 0xf800 +#define URE_SEN_VAL_NORMAL 0xa000 +#define URE_SEL_RXIDLE 0x0100 + +/* OCP_ALDPS_CONFIG */ +#define URE_ENPWRSAVE 0x8000 +#define URE_ENPDNPS 0x0200 +#define URE_LINKENA 0x0100 +#define URE_DIS_SDSAVE 0x0010 + +/* OCP_PHY_STATUS */ +#define URE_PHY_STAT_MASK 0x0007 +#define URE_PHY_STAT_LAN_ON 3 +#define URE_PHY_STAT_PWRDN 5 + +/* OCP_POWER_CFG */ +#define URE_EEE_CLKDIV_EN 0x8000 +#define URE_EN_ALDPS 0x0004 +#define URE_EN_10M_PLLOFF 0x0001 + +/* OCP_EEE_CFG */ +#define URE_CTAP_SHORT_EN 0x0040 +#define URE_EEE10_EN 0x0010 + +/* OCP_DOWN_SPEED */ +#define URE_EN_10M_BGOFF 0x0080 + +/* OCP_PHY_STATE */ +#define URE_TXDIS_STATE 0x01 +#define URE_ABD_STATE 0x02 + +/* OCP_ADC_CFG */ +#define URE_CKADSEL_L 0x0100 +#define URE_ADC_EN 0x0080 +#define URE_EN_EMI_L 0x0040 + +#define URE_MCU_TYPE_PLA 0x0100 +#define URE_MCU_TYPE_USB 0x0000 + +#define GET_MII(sc) uether_getmii(&(sc)->sc_ue) + +struct ure_intrpkt { + uint8_t ure_tsr; + uint8_t ure_rsr; + uint8_t ure_gep_msr; + uint8_t ure_waksr; + uint8_t ure_txok_cnt; + uint8_t ure_rxlost_cnt; + uint8_t ure_crcerr_cnt; + uint8_t ure_col_cnt; +} __packed; + +struct ure_rxpkt { + uint32_t ure_pktlen; +#define URE_RXPKT_LEN_MASK 0x7fff + uint32_t ure_rsvd0; + uint32_t ure_rsvd1; + uint32_t ure_rsvd2; + uint32_t ure_rsvd3; + uint32_t ure_rsvd4; +} __packed; + +struct ure_txpkt { + uint32_t ure_pktlen; +#define URE_TKPKT_TX_FS (1 << 31) +#define URE_TKPKT_TX_LS (1 << 30) +#define URE_TXPKT_LEN_MASK 0xffff + uint32_t ure_rsvd0; +} __packed; + +enum { + URE_BULK_DT_WR, + URE_BULK_DT_RD, + URE_N_TRANSFER, +}; + +struct ure_softc { + struct usb_ether sc_ue; + struct mtx sc_mtx; + struct usb_xfer *sc_xfer[URE_N_TRANSFER]; + + int sc_phyno; + + u_int sc_flags; +#define URE_FLAG_LINK 0x0001 +#define URE_FLAG_8152 0x1000 /* RTL8152 */ + + u_int sc_chip; +#define URE_CHIP_VER_4C00 0x01 +#define URE_CHIP_VER_4C10 0x02 +#define URE_CHIP_VER_5C00 0x04 +#define URE_CHIP_VER_5C10 0x08 +#define URE_CHIP_VER_5C20 0x10 +#define URE_CHIP_VER_5C30 0x20 +}; + +#define URE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define URE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define URE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/net/ruephy.c b/freebsd/sys/dev/usb/net/ruephy.c new file mode 100644 index 00000000..d6db6a04 --- /dev/null +++ b/freebsd/sys/dev/usb/net/ruephy.c @@ -0,0 +1,222 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * driver for RealTek RTL8150 internal PHY + */ + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/bus.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <rtems/bsd/local/miidevs.h> + +#include <dev/usb/net/ruephyreg.h> + +#include <rtems/bsd/local/miibus_if.h> + +static int ruephy_probe(device_t); +static int ruephy_attach(device_t); + +static device_method_t ruephy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ruephy_probe), + DEVMETHOD(device_attach, ruephy_attach), + DEVMETHOD(device_detach, mii_phy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD_END +}; + +static devclass_t ruephy_devclass; + +static driver_t ruephy_driver = { + .name = "ruephy", + .methods = ruephy_methods, + .size = sizeof(struct mii_softc) +}; + +DRIVER_MODULE(ruephy, miibus, ruephy_driver, ruephy_devclass, 0, 0); + +static int ruephy_service(struct mii_softc *, struct mii_data *, int); +static void ruephy_reset(struct mii_softc *); +static void ruephy_status(struct mii_softc *); + +/* + * The RealTek RTL8150 internal PHY doesn't have vendor/device ID + * registers; rue(4) fakes up a return value of all zeros. + */ +static const struct mii_phydesc ruephys[] = { + { 0, 0, "RealTek RTL8150 internal media interface" }, + MII_PHY_END +}; + +static const struct mii_phy_funcs ruephy_funcs = { + ruephy_service, + ruephy_status, + ruephy_reset +}; + +static int +ruephy_probe(device_t dev) +{ + + if (strcmp(device_get_name(device_get_parent(device_get_parent(dev))), + "rue") == 0) + return (mii_phy_dev_probe(dev, ruephys, BUS_PROBE_DEFAULT)); + return (ENXIO); +} + +static int +ruephy_attach(device_t dev) +{ + + mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, + &ruephy_funcs, 1); + return (0); +} + +static int +ruephy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) +{ + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int reg; + + switch (cmd) { + case MII_POLLSTAT: + break; + + case MII_MEDIACHG: + mii_phy_setmedia(sc); + break; + + case MII_TICK: + /* + * Only used for autonegotiation. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) + break; + + /* + * Check to see if we have link. If we do, we don't + * need to restart the autonegotiation process. Read + * the MSR twice in case it's latched. + */ + reg = PHY_READ(sc, RUEPHY_MII_MSR) | + PHY_READ(sc, RUEPHY_MII_MSR); + if (reg & RUEPHY_MSR_LINK) + break; + + /* Only retry autonegotiation every mii_anegticks seconds. */ + if (sc->mii_ticks <= sc->mii_anegticks) + break; + + sc->mii_ticks = 0; + PHY_RESET(sc); + if (mii_phy_auto(sc) == EJUSTRETURN) + return (0); + break; + } + + /* Update the media status. */ + PHY_STATUS(sc); + + /* Callback if something changed. */ + mii_phy_update(sc, cmd); + + return (0); +} + +static void +ruephy_reset(struct mii_softc *sc) +{ + + mii_phy_reset(sc); + + /* + * XXX RealTek RTL8150 PHY doesn't set the BMCR properly after + * XXX reset, which breaks autonegotiation. + */ + PHY_WRITE(sc, MII_BMCR, (BMCR_S100 | BMCR_AUTOEN | BMCR_FDX)); +} + +static void +ruephy_status(struct mii_softc *phy) +{ + struct mii_data *mii = phy->mii_pdata; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int bmsr, bmcr, msr; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + msr = PHY_READ(phy, RUEPHY_MII_MSR) | PHY_READ(phy, RUEPHY_MII_MSR); + if (msr & RUEPHY_MSR_LINK) + mii->mii_media_status |= IFM_ACTIVE; + + bmcr = PHY_READ(phy, MII_BMCR); + if (bmcr & BMCR_ISO) { + mii->mii_media_active |= IFM_NONE; + mii->mii_media_status = 0; + return; + } + + bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); + if (bmcr & BMCR_AUTOEN) { + if ((bmsr & BMSR_ACOMP) == 0) { + /* Erg, still trying, I guess... */ + mii->mii_media_active |= IFM_NONE; + return; + } + + if (msr & RUEPHY_MSR_SPEED100) + mii->mii_media_active |= IFM_100_TX; + else + mii->mii_media_active |= IFM_10_T; + + if (msr & RUEPHY_MSR_DUPLEX) + mii->mii_media_active |= + IFM_FDX | mii_phy_flowstatus(phy); + else + mii->mii_media_active |= IFM_HDX; + } else + mii->mii_media_active = ife->ifm_media; +} diff --git a/freebsd/sys/dev/usb/net/ruephyreg.h b/freebsd/sys/dev/usb/net/ruephyreg.h new file mode 100644 index 00000000..01d3cc17 --- /dev/null +++ b/freebsd/sys/dev/usb/net/ruephyreg.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _RUEPHYREG_H_ +#define _RUEPHYREG_H_ + +#define RUEPHY_MII_MSR 0x0137 /* B, R/W */ +#define RUEPHY_MSR_RXFCE 0x40 +#define RUEPHY_MSR_DUPLEX 0x10 +#define RUEPHY_MSR_SPEED100 0x08 +#define RUEPHY_MSR_LINK 0x04 + +#endif /* _RUEPHYREG_H_ */ diff --git a/freebsd/sys/dev/usb/net/usb_ethernet.c b/freebsd/sys/dev/usb/net/usb_ethernet.c new file mode 100644 index 00000000..7b9bfa19 --- /dev/null +++ b/freebsd/sys/dev/usb/net/usb_ethernet.c @@ -0,0 +1,651 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $FreeBSD$ */ +/*- + * Copyright (c) 2009 Andrew Thompson (thompsa@FreeBSD.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/condvar.h> +#include <sys/kernel.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/sx.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/ethernet.h> +#include <net/if_types.h> +#include <net/if_media.h> +#include <net/if_vlan_var.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + +#include <dev/usb/usb_process.h> +#include <dev/usb/net/usb_ethernet.h> + +static SYSCTL_NODE(_net, OID_AUTO, ue, CTLFLAG_RD, 0, + "USB Ethernet parameters"); + +#define UE_LOCK(_ue) mtx_lock((_ue)->ue_mtx) +#define UE_UNLOCK(_ue) mtx_unlock((_ue)->ue_mtx) +#define UE_LOCK_ASSERT(_ue, t) mtx_assert((_ue)->ue_mtx, t) + +MODULE_DEPEND(uether, usb, 1, 1, 1); +MODULE_DEPEND(uether, miibus, 1, 1, 1); + +static struct unrhdr *ueunit; + +static usb_proc_callback_t ue_attach_post_task; +static usb_proc_callback_t ue_promisc_task; +static usb_proc_callback_t ue_setmulti_task; +static usb_proc_callback_t ue_ifmedia_task; +static usb_proc_callback_t ue_tick_task; +static usb_proc_callback_t ue_start_task; +static usb_proc_callback_t ue_stop_task; + +static void ue_init(void *); +static void ue_start(struct ifnet *); +static int ue_ifmedia_upd(struct ifnet *); +static void ue_watchdog(void *); + +/* + * Return values: + * 0: success + * Else: device has been detached + */ +uint8_t +uether_pause(struct usb_ether *ue, unsigned int _ticks) +{ + if (usb_proc_is_gone(&ue->ue_tq)) { + /* nothing to do */ + return (1); + } + usb_pause_mtx(ue->ue_mtx, _ticks); + return (0); +} + +static void +ue_queue_command(struct usb_ether *ue, + usb_proc_callback_t *fn, + struct usb_proc_msg *t0, struct usb_proc_msg *t1) +{ + struct usb_ether_cfg_task *task; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + if (usb_proc_is_gone(&ue->ue_tq)) { + return; /* nothing to do */ + } + /* + * NOTE: The task cannot get executed before we drop the + * "sc_mtx" mutex. It is safe to update fields in the message + * structure after that the message got queued. + */ + task = (struct usb_ether_cfg_task *) + usb_proc_msignal(&ue->ue_tq, t0, t1); + + /* Setup callback and self pointers */ + task->hdr.pm_callback = fn; + task->ue = ue; + + /* + * Start and stop must be synchronous! + */ + if ((fn == ue_start_task) || (fn == ue_stop_task)) + usb_proc_mwait(&ue->ue_tq, t0, t1); +} + +struct ifnet * +uether_getifp(struct usb_ether *ue) +{ + return (ue->ue_ifp); +} + +struct mii_data * +uether_getmii(struct usb_ether *ue) +{ + return (device_get_softc(ue->ue_miibus)); +} + +void * +uether_getsc(struct usb_ether *ue) +{ + return (ue->ue_sc); +} + +static int +ue_sysctl_parent(SYSCTL_HANDLER_ARGS) +{ + struct usb_ether *ue = arg1; + const char *name; + + name = device_get_nameunit(ue->ue_dev); + return SYSCTL_OUT_STR(req, name); +} + +int +uether_ifattach(struct usb_ether *ue) +{ + int error; + + /* check some critical parameters */ + if ((ue->ue_dev == NULL) || + (ue->ue_udev == NULL) || + (ue->ue_mtx == NULL) || + (ue->ue_methods == NULL)) + return (EINVAL); + + error = usb_proc_create(&ue->ue_tq, ue->ue_mtx, + device_get_nameunit(ue->ue_dev), USB_PRI_MED); + if (error) { + device_printf(ue->ue_dev, "could not setup taskqueue\n"); + goto error; + } + + /* fork rest of the attach code */ + UE_LOCK(ue); + ue_queue_command(ue, ue_attach_post_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + UE_UNLOCK(ue); + +error: + return (error); +} + +static void +ue_attach_post_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + struct ifnet *ifp; + int error; + char num[14]; /* sufficient for 32 bits */ + + /* first call driver's post attach routine */ + ue->ue_methods->ue_attach_post(ue); + + UE_UNLOCK(ue); + + ue->ue_unit = alloc_unr(ueunit); + usb_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0); + sysctl_ctx_init(&ue->ue_sysctl_ctx); + + error = 0; + CURVNET_SET_QUIET(vnet0); + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(ue->ue_dev, "could not allocate ifnet\n"); + goto fail; + } + + ifp->if_softc = ue; + if_initname(ifp, "ue", ue->ue_unit); + if (ue->ue_methods->ue_attach_post_sub != NULL) { + ue->ue_ifp = ifp; + error = ue->ue_methods->ue_attach_post_sub(ue); + } else { + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + if (ue->ue_methods->ue_ioctl != NULL) + ifp->if_ioctl = ue->ue_methods->ue_ioctl; + else + ifp->if_ioctl = uether_ioctl; + ifp->if_start = ue_start; + ifp->if_init = ue_init; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + ue->ue_ifp = ifp; + + if (ue->ue_methods->ue_mii_upd != NULL && + ue->ue_methods->ue_mii_sts != NULL) { + /* device_xxx() depends on this */ + mtx_lock(&Giant); + error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, + ue_ifmedia_upd, ue->ue_methods->ue_mii_sts, + BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); + mtx_unlock(&Giant); + } + } + + if (error) { + device_printf(ue->ue_dev, "attaching PHYs failed\n"); + goto fail; + } + + if_printf(ifp, "<USB Ethernet> on %s\n", device_get_nameunit(ue->ue_dev)); + ether_ifattach(ifp, ue->ue_eaddr); + /* Tell upper layer we support VLAN oversized frames. */ + if (ifp->if_capabilities & IFCAP_VLAN_MTU) + ifp->if_hdrlen = sizeof(struct ether_vlan_header); + + CURVNET_RESTORE(); + + snprintf(num, sizeof(num), "%u", ue->ue_unit); + ue->ue_sysctl_oid = SYSCTL_ADD_NODE(&ue->ue_sysctl_ctx, + &SYSCTL_NODE_CHILDREN(_net, ue), + OID_AUTO, num, CTLFLAG_RD, NULL, ""); + SYSCTL_ADD_PROC(&ue->ue_sysctl_ctx, + SYSCTL_CHILDREN(ue->ue_sysctl_oid), OID_AUTO, + "%parent", CTLTYPE_STRING | CTLFLAG_RD, ue, 0, + ue_sysctl_parent, "A", "parent device"); + + UE_LOCK(ue); + return; + +fail: + CURVNET_RESTORE(); + free_unr(ueunit, ue->ue_unit); + if (ue->ue_ifp != NULL) { + if_free(ue->ue_ifp); + ue->ue_ifp = NULL; + } + UE_LOCK(ue); + return; +} + +void +uether_ifdetach(struct usb_ether *ue) +{ + struct ifnet *ifp; + + /* wait for any post attach or other command to complete */ + usb_proc_drain(&ue->ue_tq); + + /* read "ifnet" pointer after taskqueue drain */ + ifp = ue->ue_ifp; + + if (ifp != NULL) { + + /* we are not running any more */ + UE_LOCK(ue); + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + UE_UNLOCK(ue); + + /* drain any callouts */ + usb_callout_drain(&ue->ue_watchdog); + + /* detach miibus */ + if (ue->ue_miibus != NULL) { + mtx_lock(&Giant); /* device_xxx() depends on this */ + device_delete_child(ue->ue_dev, ue->ue_miibus); + mtx_unlock(&Giant); + } + + /* detach ethernet */ + ether_ifdetach(ifp); + + /* free interface instance */ + if_free(ifp); + + /* free sysctl */ + sysctl_ctx_free(&ue->ue_sysctl_ctx); + + /* free unit */ + free_unr(ueunit, ue->ue_unit); + } + + /* free taskqueue, if any */ + usb_proc_free(&ue->ue_tq); +} + +uint8_t +uether_is_gone(struct usb_ether *ue) +{ + return (usb_proc_is_gone(&ue->ue_tq)); +} + +void +uether_init(void *arg) +{ + + ue_init(arg); +} + +static void +ue_init(void *arg) +{ + struct usb_ether *ue = arg; + + UE_LOCK(ue); + ue_queue_command(ue, ue_start_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + UE_UNLOCK(ue); +} + +static void +ue_start_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + struct ifnet *ifp = ue->ue_ifp; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + ue->ue_methods->ue_init(ue); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + if (ue->ue_methods->ue_tick != NULL) + usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); +} + +static void +ue_stop_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + usb_callout_stop(&ue->ue_watchdog); + + ue->ue_methods->ue_stop(ue); +} + +void +uether_start(struct ifnet *ifp) +{ + + ue_start(ifp); +} + +static void +ue_start(struct ifnet *ifp) +{ + struct usb_ether *ue = ifp->if_softc; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + UE_LOCK(ue); + ue->ue_methods->ue_start(ue); + UE_UNLOCK(ue); +} + +static void +ue_promisc_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + + ue->ue_methods->ue_setpromisc(ue); +} + +static void +ue_setmulti_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + + ue->ue_methods->ue_setmulti(ue); +} + +int +uether_ifmedia_upd(struct ifnet *ifp) +{ + + return (ue_ifmedia_upd(ifp)); +} + +static int +ue_ifmedia_upd(struct ifnet *ifp) +{ + struct usb_ether *ue = ifp->if_softc; + + /* Defer to process context */ + UE_LOCK(ue); + ue_queue_command(ue, ue_ifmedia_task, + &ue->ue_media_task[0].hdr, + &ue->ue_media_task[1].hdr); + UE_UNLOCK(ue); + + return (0); +} + +static void +ue_ifmedia_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + struct ifnet *ifp = ue->ue_ifp; + + ue->ue_methods->ue_mii_upd(ifp); +} + +static void +ue_watchdog(void *arg) +{ + struct usb_ether *ue = arg; + struct ifnet *ifp = ue->ue_ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + ue_queue_command(ue, ue_tick_task, + &ue->ue_tick_task[0].hdr, + &ue->ue_tick_task[1].hdr); + + usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); +} + +static void +ue_tick_task(struct usb_proc_msg *_task) +{ + struct usb_ether_cfg_task *task = + (struct usb_ether_cfg_task *)_task; + struct usb_ether *ue = task->ue; + struct ifnet *ifp = ue->ue_ifp; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + ue->ue_methods->ue_tick(ue); +} + +int +uether_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct usb_ether *ue = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct mii_data *mii; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + UE_LOCK(ue); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ue_queue_command(ue, ue_promisc_task, + &ue->ue_promisc_task[0].hdr, + &ue->ue_promisc_task[1].hdr); + else + ue_queue_command(ue, ue_start_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + } else { + ue_queue_command(ue, ue_stop_task, + &ue->ue_sync_task[0].hdr, + &ue->ue_sync_task[1].hdr); + } + UE_UNLOCK(ue); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + UE_LOCK(ue); + ue_queue_command(ue, ue_setmulti_task, + &ue->ue_multi_task[0].hdr, + &ue->ue_multi_task[1].hdr); + UE_UNLOCK(ue); + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + if (ue->ue_miibus != NULL) { + mii = device_get_softc(ue->ue_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + } else + error = ether_ioctl(ifp, command, data); + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + return (error); +} + +static int +uether_modevent(module_t mod, int type, void *data) +{ + + switch (type) { + case MOD_LOAD: + ueunit = new_unrhdr(0, INT_MAX, NULL); + break; + case MOD_UNLOAD: + break; + default: + return (EOPNOTSUPP); + } + return (0); +} +static moduledata_t uether_mod = { + "uether", + uether_modevent, + 0 +}; + +struct mbuf * +uether_newbuf(void) +{ + struct mbuf *m_new; + + m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m_new == NULL) + return (NULL); + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + + m_adj(m_new, ETHER_ALIGN); + return (m_new); +} + +int +uether_rxmbuf(struct usb_ether *ue, struct mbuf *m, + unsigned int len) +{ + struct ifnet *ifp = ue->ue_ifp; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + /* finalize mbuf */ + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + /* enqueue for later when the lock can be released */ + _IF_ENQUEUE(&ue->ue_rxq, m); + return (0); +} + +int +uether_rxbuf(struct usb_ether *ue, struct usb_page_cache *pc, + unsigned int offset, unsigned int len) +{ + struct ifnet *ifp = ue->ue_ifp; + struct mbuf *m; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) + return (1); + + m = uether_newbuf(); + if (m == NULL) { + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + return (ENOMEM); + } + + usbd_copy_out(pc, offset, mtod(m, uint8_t *), len); + + /* finalize mbuf */ + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + /* enqueue for later when the lock can be released */ + _IF_ENQUEUE(&ue->ue_rxq, m); + return (0); +} + +void +uether_rxflush(struct usb_ether *ue) +{ + struct ifnet *ifp = ue->ue_ifp; + struct mbuf *m; + + UE_LOCK_ASSERT(ue, MA_OWNED); + + for (;;) { + _IF_DEQUEUE(&ue->ue_rxq, m); + if (m == NULL) + break; + + /* + * The USB xfer has been resubmitted so its safe to unlock now. + */ + UE_UNLOCK(ue); + ifp->if_input(ifp, m); + UE_LOCK(ue); + } +} + +/* + * USB net drivers are run by DRIVER_MODULE() thus SI_SUB_DRIVERS, + * SI_ORDER_MIDDLE. Run uether after that. + */ +DECLARE_MODULE(uether, uether_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); +MODULE_VERSION(uether, 1); diff --git a/freebsd/sys/dev/usb/net/usb_ethernet.h b/freebsd/sys/dev/usb/net/usb_ethernet.h new file mode 100644 index 00000000..09a48e27 --- /dev/null +++ b/freebsd/sys/dev/usb/net/usb_ethernet.h @@ -0,0 +1,127 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _USB_ETHERNET_H_ +#define _USB_ETHERNET_H_ + +#include <rtems/bsd/local/opt_inet.h> + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/limits.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/bpf.h> +#include <net/ethernet.h> + +#include <rtems/bsd/local/miibus_if.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +struct usb_ether; +struct usb_device_request; + +typedef void (uether_fn_t)(struct usb_ether *); + +struct usb_ether_methods { + uether_fn_t *ue_attach_post; + uether_fn_t *ue_start; + uether_fn_t *ue_init; + uether_fn_t *ue_stop; + uether_fn_t *ue_setmulti; + uether_fn_t *ue_setpromisc; + uether_fn_t *ue_tick; + int (*ue_mii_upd)(struct ifnet *); + void (*ue_mii_sts)(struct ifnet *, + struct ifmediareq *); + int (*ue_ioctl)(struct ifnet *, u_long, caddr_t); + int (*ue_attach_post_sub)(struct usb_ether *); +}; + +struct usb_ether_cfg_task { + struct usb_proc_msg hdr; + struct usb_ether *ue; +}; + +struct usb_ether { + /* NOTE: the "ue_ifp" pointer must be first --hps */ + struct ifnet *ue_ifp; + struct mtx *ue_mtx; + const struct usb_ether_methods *ue_methods; + struct sysctl_oid *ue_sysctl_oid; + void *ue_sc; + struct usb_device *ue_udev; /* used by uether_do_request() */ + device_t ue_dev; + device_t ue_miibus; + + struct usb_process ue_tq; + struct sysctl_ctx_list ue_sysctl_ctx; + struct ifqueue ue_rxq; + struct usb_callout ue_watchdog; + struct usb_ether_cfg_task ue_sync_task[2]; + struct usb_ether_cfg_task ue_media_task[2]; + struct usb_ether_cfg_task ue_multi_task[2]; + struct usb_ether_cfg_task ue_promisc_task[2]; + struct usb_ether_cfg_task ue_tick_task[2]; + + int ue_unit; + + /* ethernet address from eeprom */ + uint8_t ue_eaddr[ETHER_ADDR_LEN]; +}; + +#define uether_do_request(ue,req,data,timo) \ + usbd_do_request_proc((ue)->ue_udev,&(ue)->ue_tq,req,data,0,NULL,timo) + +uint8_t uether_pause(struct usb_ether *, unsigned int); +struct ifnet *uether_getifp(struct usb_ether *); +struct mii_data *uether_getmii(struct usb_ether *); +void *uether_getsc(struct usb_ether *); +int uether_ifattach(struct usb_ether *); +void uether_ifdetach(struct usb_ether *); +int uether_ifmedia_upd(struct ifnet *); +void uether_init(void *); +int uether_ioctl(struct ifnet *, u_long, caddr_t); +struct mbuf *uether_newbuf(void); +int uether_rxmbuf(struct usb_ether *, struct mbuf *, + unsigned int); +int uether_rxbuf(struct usb_ether *, + struct usb_page_cache *, + unsigned int, unsigned int); +void uether_rxflush(struct usb_ether *); +uint8_t uether_is_gone(struct usb_ether *); +void uether_start(struct ifnet *); +#endif /* _USB_ETHERNET_H_ */ diff --git a/freebsd/sys/dev/usb/wlan/if_rsu.c b/freebsd/sys/dev/usb/wlan/if_rsu.c new file mode 100644 index 00000000..b429a082 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_rsu.c @@ -0,0 +1,3796 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $OpenBSD: if_rsu.c,v 1.17 2013/04/15 09:23:01 mglocker Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU. + * + * TODO: + * o tx a-mpdu + * o hostap / ibss / mesh + * o power-save operation + */ + +#include <rtems/bsd/local/opt_wlan.h> + +#include <rtems/bsd/sys/param.h> +#include <sys/endian.h> +#include <sys/sockio.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/firmware.h> +#include <sys/module.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_radiotap.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <rtems/bsd/local/usbdevs.h> + +#include <dev/usb/wlan/if_rsureg.h> + +#ifdef USB_DEBUG +static int rsu_debug = 0; +SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu"); +SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RWTUN, &rsu_debug, 0, + "Debug level"); +#define RSU_DPRINTF(_sc, _flg, ...) \ + do \ + if (((_flg) == (RSU_DEBUG_ANY)) || (rsu_debug & (_flg))) \ + device_printf((_sc)->sc_dev, __VA_ARGS__); \ + while (0) +#else +#define RSU_DPRINTF(_sc, _flg, ...) +#endif + +static int rsu_enable_11n = 1; +TUNABLE_INT("hw.usb.rsu.enable_11n", &rsu_enable_11n); + +#define RSU_DEBUG_ANY 0xffffffff +#define RSU_DEBUG_TX 0x00000001 +#define RSU_DEBUG_RX 0x00000002 +#define RSU_DEBUG_RESET 0x00000004 +#define RSU_DEBUG_CALIB 0x00000008 +#define RSU_DEBUG_STATE 0x00000010 +#define RSU_DEBUG_SCAN 0x00000020 +#define RSU_DEBUG_FWCMD 0x00000040 +#define RSU_DEBUG_TXDONE 0x00000080 +#define RSU_DEBUG_FW 0x00000100 +#define RSU_DEBUG_FWDBG 0x00000200 +#define RSU_DEBUG_AMPDU 0x00000400 +#define RSU_DEBUG_KEY 0x00000800 +#define RSU_DEBUG_USB 0x00001000 + +static const STRUCT_USB_HOST_ID rsu_devs[] = { +#define RSU_HT_NOT_SUPPORTED 0 +#define RSU_HT_SUPPORTED 1 +#define RSU_DEV_HT(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \ + RSU_HT_SUPPORTED) } +#define RSU_DEV(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \ + RSU_HT_NOT_SUPPORTED) } + RSU_DEV(ASUS, RTL8192SU), + RSU_DEV(AZUREWAVE, RTL8192SU_4), + RSU_DEV_HT(ACCTON, RTL8192SU), + RSU_DEV_HT(ASUS, USBN10), + RSU_DEV_HT(AZUREWAVE, RTL8192SU_1), + RSU_DEV_HT(AZUREWAVE, RTL8192SU_2), + RSU_DEV_HT(AZUREWAVE, RTL8192SU_3), + RSU_DEV_HT(AZUREWAVE, RTL8192SU_5), + RSU_DEV_HT(BELKIN, RTL8192SU_1), + RSU_DEV_HT(BELKIN, RTL8192SU_2), + RSU_DEV_HT(BELKIN, RTL8192SU_3), + RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_1), + RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_2), + RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_3), + RSU_DEV_HT(COREGA, RTL8192SU), + RSU_DEV_HT(DLINK2, DWA131A1), + RSU_DEV_HT(DLINK2, RTL8192SU_1), + RSU_DEV_HT(DLINK2, RTL8192SU_2), + RSU_DEV_HT(EDIMAX, RTL8192SU_1), + RSU_DEV_HT(EDIMAX, RTL8192SU_2), + RSU_DEV_HT(EDIMAX, EW7622UMN), + RSU_DEV_HT(GUILLEMOT, HWGUN54), + RSU_DEV_HT(GUILLEMOT, HWNUM300), + RSU_DEV_HT(HAWKING, RTL8192SU_1), + RSU_DEV_HT(HAWKING, RTL8192SU_2), + RSU_DEV_HT(PLANEX2, GWUSNANO), + RSU_DEV_HT(REALTEK, RTL8171), + RSU_DEV_HT(REALTEK, RTL8172), + RSU_DEV_HT(REALTEK, RTL8173), + RSU_DEV_HT(REALTEK, RTL8174), + RSU_DEV_HT(REALTEK, RTL8192SU), + RSU_DEV_HT(REALTEK, RTL8712), + RSU_DEV_HT(REALTEK, RTL8713), + RSU_DEV_HT(SENAO, RTL8192SU_1), + RSU_DEV_HT(SENAO, RTL8192SU_2), + RSU_DEV_HT(SITECOMEU, WL349V1), + RSU_DEV_HT(SITECOMEU, WL353), + RSU_DEV_HT(SWEEX2, LW154), + RSU_DEV_HT(TRENDNET, TEW646UBH), +#undef RSU_DEV_HT +#undef RSU_DEV +}; + +static device_probe_t rsu_match; +static device_attach_t rsu_attach; +static device_detach_t rsu_detach; +static usb_callback_t rsu_bulk_tx_callback_be_bk; +static usb_callback_t rsu_bulk_tx_callback_vi_vo; +static usb_callback_t rsu_bulk_tx_callback_h2c; +static usb_callback_t rsu_bulk_rx_callback; +static usb_error_t rsu_do_request(struct rsu_softc *, + struct usb_device_request *, void *); +static struct ieee80211vap * + rsu_vap_create(struct ieee80211com *, const char name[], + int, enum ieee80211_opmode, int, const uint8_t bssid[], + const uint8_t mac[]); +static void rsu_vap_delete(struct ieee80211vap *); +static void rsu_scan_start(struct ieee80211com *); +static void rsu_scan_end(struct ieee80211com *); +static void rsu_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static void rsu_set_channel(struct ieee80211com *); +static void rsu_scan_curchan(struct ieee80211_scan_state *, unsigned long); +static void rsu_scan_mindwell(struct ieee80211_scan_state *); +static void rsu_update_promisc(struct ieee80211com *); +static uint8_t rsu_get_multi_pos(const uint8_t[]); +static void rsu_set_multi(struct rsu_softc *); +static void rsu_update_mcast(struct ieee80211com *); +static int rsu_alloc_rx_list(struct rsu_softc *); +static void rsu_free_rx_list(struct rsu_softc *); +static int rsu_alloc_tx_list(struct rsu_softc *); +static void rsu_free_tx_list(struct rsu_softc *); +static void rsu_free_list(struct rsu_softc *, struct rsu_data [], int); +static struct rsu_data *_rsu_getbuf(struct rsu_softc *); +static struct rsu_data *rsu_getbuf(struct rsu_softc *); +static void rsu_freebuf(struct rsu_softc *, struct rsu_data *); +static int rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *, + int); +static void rsu_write_1(struct rsu_softc *, uint16_t, uint8_t); +static void rsu_write_2(struct rsu_softc *, uint16_t, uint16_t); +static void rsu_write_4(struct rsu_softc *, uint16_t, uint32_t); +static int rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *, + int); +static uint8_t rsu_read_1(struct rsu_softc *, uint16_t); +static uint16_t rsu_read_2(struct rsu_softc *, uint16_t); +static uint32_t rsu_read_4(struct rsu_softc *, uint16_t); +static int rsu_fw_iocmd(struct rsu_softc *, uint32_t); +static uint8_t rsu_efuse_read_1(struct rsu_softc *, uint16_t); +static int rsu_read_rom(struct rsu_softc *); +static int rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int); +static void rsu_calib_task(void *, int); +static void rsu_tx_task(void *, int); +static void rsu_set_led(struct rsu_softc *, int); +static int rsu_monitor_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static int rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int rsu_key_alloc(struct ieee80211vap *, struct ieee80211_key *, + ieee80211_keyix *, ieee80211_keyix *); +static int rsu_process_key(struct ieee80211vap *, + const struct ieee80211_key *, int); +static int rsu_key_set(struct ieee80211vap *, + const struct ieee80211_key *); +static int rsu_key_delete(struct ieee80211vap *, + const struct ieee80211_key *); +static int rsu_cam_read(struct rsu_softc *, uint8_t, uint32_t *); +static void rsu_cam_write(struct rsu_softc *, uint8_t, uint32_t); +static int rsu_key_check(struct rsu_softc *, ieee80211_keyix, int); +static uint8_t rsu_crypto_mode(struct rsu_softc *, u_int, int); +static int rsu_set_key_group(struct rsu_softc *, + const struct ieee80211_key *); +static int rsu_set_key_pair(struct rsu_softc *, + const struct ieee80211_key *); +static int rsu_reinit_static_keys(struct rsu_softc *); +static int rsu_delete_key(struct rsu_softc *sc, ieee80211_keyix); +static void rsu_delete_key_pair_cb(void *, int); +static int rsu_site_survey(struct rsu_softc *, + struct ieee80211_scan_ssid *); +static int rsu_join_bss(struct rsu_softc *, struct ieee80211_node *); +static int rsu_disconnect(struct rsu_softc *); +static int rsu_hwrssi_to_rssi(struct rsu_softc *, int hw_rssi); +static void rsu_event_survey(struct rsu_softc *, uint8_t *, int); +static void rsu_event_join_bss(struct rsu_softc *, uint8_t *, int); +static void rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int); +static void rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int); +static int8_t rsu_get_rssi(struct rsu_softc *, int, void *); +static struct mbuf * rsu_rx_copy_to_mbuf(struct rsu_softc *, + struct r92s_rx_stat *, int); +static uint32_t rsu_get_tsf_low(struct rsu_softc *); +static uint32_t rsu_get_tsf_high(struct rsu_softc *); +static struct ieee80211_node * rsu_rx_frame(struct rsu_softc *, struct mbuf *); +static struct mbuf * rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int); +static struct mbuf * + rsu_rxeof(struct usb_xfer *, struct rsu_data *); +static void rsu_txeof(struct usb_xfer *, struct rsu_data *); +static int rsu_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void rsu_rxfilter_init(struct rsu_softc *); +static void rsu_rxfilter_set(struct rsu_softc *, uint32_t, uint32_t); +static void rsu_rxfilter_refresh(struct rsu_softc *); +static int rsu_init(struct rsu_softc *); +static int rsu_tx_start(struct rsu_softc *, struct ieee80211_node *, + struct mbuf *, struct rsu_data *); +static int rsu_transmit(struct ieee80211com *, struct mbuf *); +static void rsu_start(struct rsu_softc *); +static void _rsu_start(struct rsu_softc *); +static int rsu_ioctl_net(struct ieee80211com *, u_long, void *); +static void rsu_parent(struct ieee80211com *); +static void rsu_stop(struct rsu_softc *); +static void rsu_ms_delay(struct rsu_softc *, int); + +static device_method_t rsu_methods[] = { + DEVMETHOD(device_probe, rsu_match), + DEVMETHOD(device_attach, rsu_attach), + DEVMETHOD(device_detach, rsu_detach), + + DEVMETHOD_END +}; + +static driver_t rsu_driver = { + .name = "rsu", + .methods = rsu_methods, + .size = sizeof(struct rsu_softc) +}; + +static devclass_t rsu_devclass; + +DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0); +MODULE_DEPEND(rsu, wlan, 1, 1, 1); +MODULE_DEPEND(rsu, usb, 1, 1, 1); +MODULE_DEPEND(rsu, firmware, 1, 1, 1); +MODULE_VERSION(rsu, 1); +USB_PNP_HOST_INFO(rsu_devs); + +static const uint8_t rsu_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + +static uint8_t rsu_wme_ac_xfer_map[4] = { + [WME_AC_BE] = RSU_BULK_TX_BE_BK, + [WME_AC_BK] = RSU_BULK_TX_BE_BK, + [WME_AC_VI] = RSU_BULK_TX_VI_VO, + [WME_AC_VO] = RSU_BULK_TX_VI_VO, +}; + +/* XXX hard-coded */ +#define RSU_H2C_ENDPOINT 3 + +static const struct usb_config rsu_config[RSU_N_TRANSFER] = { + [RSU_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = RSU_RXBUFSZ, + .flags = { + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = rsu_bulk_rx_callback + }, + [RSU_BULK_TX_BE_BK] = { + .type = UE_BULK, + .endpoint = 0x06, + .direction = UE_DIR_OUT, + .bufsize = RSU_TXBUFSZ, + .flags = { + .ext_buffer = 1, + .pipe_bof = 1, + .force_short_xfer = 1 + }, + .callback = rsu_bulk_tx_callback_be_bk, + .timeout = RSU_TX_TIMEOUT + }, + [RSU_BULK_TX_VI_VO] = { + .type = UE_BULK, + .endpoint = 0x04, + .direction = UE_DIR_OUT, + .bufsize = RSU_TXBUFSZ, + .flags = { + .ext_buffer = 1, + .pipe_bof = 1, + .force_short_xfer = 1 + }, + .callback = rsu_bulk_tx_callback_vi_vo, + .timeout = RSU_TX_TIMEOUT + }, + [RSU_BULK_TX_H2C] = { + .type = UE_BULK, + .endpoint = 0x0d, + .direction = UE_DIR_OUT, + .bufsize = RSU_TXBUFSZ, + .flags = { + .ext_buffer = 1, + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = rsu_bulk_tx_callback_h2c, + .timeout = RSU_TX_TIMEOUT + }, +}; + +static int +rsu_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST || + uaa->info.bIfaceIndex != 0 || + uaa->info.bConfigIndex != 0) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa)); +} + +static int +rsu_send_mgmt(struct ieee80211_node *ni, int type, int arg) +{ + + return (ENOTSUP); +} + +static void +rsu_update_chw(struct ieee80211com *ic) +{ + +} + +/* + * notification from net80211 that it'd like to do A-MPDU on the given TID. + * + * Note: this actually hangs traffic at the present moment, so don't use it. + * The firmware debug does indiciate it's sending and establishing a TX AMPDU + * session, but then no traffic flows. + */ +static int +rsu_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ +#if 0 + struct rsu_softc *sc = ni->ni_ic->ic_softc; + struct r92s_add_ba_req req; + + /* Don't enable if it's requested or running */ + if (IEEE80211_AMPDU_REQUESTED(tap)) + return (0); + if (IEEE80211_AMPDU_RUNNING(tap)) + return (0); + + /* We've decided to send addba; so send it */ + req.tid = htole32(tap->txa_tid); + + /* Attempt net80211 state */ + if (ieee80211_ampdu_tx_request_ext(ni, tap->txa_tid) != 1) + return (0); + + /* Send the firmware command */ + RSU_DPRINTF(sc, RSU_DEBUG_AMPDU, "%s: establishing AMPDU TX for TID %d\n", + __func__, + tap->txa_tid); + + RSU_LOCK(sc); + if (rsu_fw_cmd(sc, R92S_CMD_ADDBA_REQ, &req, sizeof(req)) != 1) { + RSU_UNLOCK(sc); + /* Mark failure */ + (void) ieee80211_ampdu_tx_request_active_ext(ni, tap->txa_tid, 0); + return (0); + } + RSU_UNLOCK(sc); + + /* Mark success; we don't get any further notifications */ + (void) ieee80211_ampdu_tx_request_active_ext(ni, tap->txa_tid, 1); +#endif + /* Return 0, we're driving this ourselves */ + return (0); +} + +static int +rsu_wme_update(struct ieee80211com *ic) +{ + + /* Firmware handles this; not our problem */ + return (0); +} + +static int +rsu_attach(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + struct rsu_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + int error; + uint8_t iface_index; + struct usb_interface *iface; + const char *rft; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + sc->sc_rx_checksum_enable = 1; + if (rsu_enable_11n) + sc->sc_ht = !! (USB_GET_DRIVER_INFO(uaa) & RSU_HT_SUPPORTED); + + /* Get number of endpoints */ + iface = usbd_get_iface(sc->sc_udev, 0); + sc->sc_nendpoints = iface->idesc->bNumEndpoints; + + /* Endpoints are hard-coded for now, so enforce 4-endpoint only */ + if (sc->sc_nendpoints != 4) { + device_printf(sc->sc_dev, + "the driver currently only supports 4-endpoint devices\n"); + return (ENXIO); + } + + mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, + MTX_DEF); + RSU_DELKEY_BMAP_LOCK_INIT(sc); + TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0, + rsu_calib_task, sc); + TASK_INIT(&sc->del_key_task, 0, rsu_delete_key_pair_cb, sc); + TASK_INIT(&sc->tx_task, 0, rsu_tx_task, sc); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + /* Allocate Tx/Rx buffers. */ + error = rsu_alloc_rx_list(sc); + if (error != 0) { + device_printf(sc->sc_dev, "could not allocate Rx buffers\n"); + goto fail_usb; + } + + error = rsu_alloc_tx_list(sc); + if (error != 0) { + device_printf(sc->sc_dev, "could not allocate Tx buffers\n"); + rsu_free_rx_list(sc); + goto fail_usb; + } + + iface_index = 0; + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(sc->sc_dev, + "could not allocate USB transfers, err=%s\n", + usbd_errstr(error)); + goto fail_usb; + } + RSU_LOCK(sc); + /* Read chip revision. */ + sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT); + if (sc->cut != 3) + sc->cut = (sc->cut >> 1) + 1; + error = rsu_read_rom(sc); + RSU_UNLOCK(sc); + if (error != 0) { + device_printf(self, "could not read ROM\n"); + goto fail_rom; + } + + /* Figure out TX/RX streams */ + switch (sc->rom[84]) { + case 0x0: + sc->sc_rftype = RTL8712_RFCONFIG_1T1R; + sc->sc_nrxstream = 1; + sc->sc_ntxstream = 1; + rft = "1T1R"; + break; + case 0x1: + sc->sc_rftype = RTL8712_RFCONFIG_1T2R; + sc->sc_nrxstream = 2; + sc->sc_ntxstream = 1; + rft = "1T2R"; + break; + case 0x2: + sc->sc_rftype = RTL8712_RFCONFIG_2T2R; + sc->sc_nrxstream = 2; + sc->sc_ntxstream = 2; + rft = "2T2R"; + break; + default: + device_printf(sc->sc_dev, + "%s: unknown board type (rfconfig=0x%02x)\n", + __func__, + sc->rom[84]); + goto fail_rom; + } + + IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->rom[0x12]); + device_printf(self, "MAC/BB RTL8712 cut %d %s\n", sc->cut, rft); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(self); + ic->ic_phytype = IEEE80211_T_OFDM; /* Not only, but not used. */ + ic->ic_opmode = IEEE80211_M_STA; /* Default to BSS mode. */ + + /* Set device capabilities. */ + ic->ic_caps = + IEEE80211_C_STA | /* station mode */ + IEEE80211_C_MONITOR | /* monitor mode supported */ +#if 0 + IEEE80211_C_BGSCAN | /* Background scan. */ +#endif + IEEE80211_C_SHPREAMBLE | /* Short preamble supported. */ + IEEE80211_C_WME | /* WME/QoS */ + IEEE80211_C_SHSLOT | /* Short slot time supported. */ + IEEE80211_C_WPA; /* WPA/RSN. */ + + ic->ic_cryptocaps = + IEEE80211_CRYPTO_WEP | + IEEE80211_CRYPTO_TKIP | + IEEE80211_CRYPTO_AES_CCM; + + /* Check if HT support is present. */ + if (sc->sc_ht) { + device_printf(sc->sc_dev, "%s: enabling 11n\n", __func__); + + /* Enable basic HT */ + ic->ic_htcaps = IEEE80211_HTC_HT | +#if 0 + IEEE80211_HTC_AMPDU | +#endif + IEEE80211_HTC_AMSDU | + IEEE80211_HTCAP_MAXAMSDU_3839 | + IEEE80211_HTCAP_SMPS_OFF; + ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40; + + /* set number of spatial streams */ + ic->ic_txstream = sc->sc_ntxstream; + ic->ic_rxstream = sc->sc_nrxstream; + } + ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD; + + rsu_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + ic->ic_raw_xmit = rsu_raw_xmit; + ic->ic_scan_start = rsu_scan_start; + ic->ic_scan_end = rsu_scan_end; + ic->ic_getradiocaps = rsu_getradiocaps; + ic->ic_set_channel = rsu_set_channel; + ic->ic_scan_curchan = rsu_scan_curchan; + ic->ic_scan_mindwell = rsu_scan_mindwell; + ic->ic_vap_create = rsu_vap_create; + ic->ic_vap_delete = rsu_vap_delete; + ic->ic_update_promisc = rsu_update_promisc; + ic->ic_update_mcast = rsu_update_mcast; + ic->ic_ioctl = rsu_ioctl_net; + ic->ic_parent = rsu_parent; + ic->ic_transmit = rsu_transmit; + ic->ic_send_mgmt = rsu_send_mgmt; + ic->ic_update_chw = rsu_update_chw; + ic->ic_ampdu_enable = rsu_ampdu_enable; + ic->ic_wme.wme_update = rsu_wme_update; + + ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, + sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RSU_RX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +fail_rom: + usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER); +fail_usb: + mtx_destroy(&sc->sc_mtx); + return (ENXIO); +} + +static int +rsu_detach(device_t self) +{ + struct rsu_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + + rsu_stop(sc); + + usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER); + + /* + * Free buffers /before/ we detach from net80211, else node + * references to destroyed vaps will lead to a panic. + */ + /* Free Tx/Rx buffers. */ + RSU_LOCK(sc); + rsu_free_tx_list(sc); + rsu_free_rx_list(sc); + RSU_UNLOCK(sc); + + /* Frames are freed; detach from net80211 */ + ieee80211_ifdetach(ic); + + taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task); + taskqueue_drain(taskqueue_thread, &sc->del_key_task); + taskqueue_drain(taskqueue_thread, &sc->tx_task); + + RSU_DELKEY_BMAP_LOCK_DESTROY(sc); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static usb_error_t +rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req, + void *data) +{ + usb_error_t err; + int ntries = 10; + + RSU_ASSERT_LOCKED(sc); + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0 || err == USB_ERR_NOT_CONFIGURED) + break; + RSU_DPRINTF(sc, RSU_DEBUG_USB, + "Control request failed, %s (retries left: %d)\n", + usbd_errstr(err), ntries); + rsu_ms_delay(sc, 10); + } + + return (err); +} + +static struct ieee80211vap * +rsu_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct rsu_softc *sc = ic->ic_softc; + struct rsu_vap *uvp; + struct ieee80211vap *vap; + struct ifnet *ifp; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return (NULL); + + uvp = malloc(sizeof(struct rsu_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &uvp->vap; + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags, bssid) != 0) { + /* out of memory */ + free(uvp, M_80211_VAP); + return (NULL); + } + + ifp = vap->iv_ifp; + ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; + RSU_LOCK(sc); + if (sc->sc_rx_checksum_enable) + ifp->if_capenable |= IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; + RSU_UNLOCK(sc); + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + if (opmode == IEEE80211_M_MONITOR) + vap->iv_newstate = rsu_monitor_newstate; + else + vap->iv_newstate = rsu_newstate; + vap->iv_key_alloc = rsu_key_alloc; + vap->iv_key_set = rsu_key_set; + vap->iv_key_delete = rsu_key_delete; + + /* Limits from the r92su driver */ + vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16; + vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K; + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + + return (vap); +} + +static void +rsu_vap_delete(struct ieee80211vap *vap) +{ + struct rsu_vap *uvp = RSU_VAP(vap); + + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static void +rsu_scan_start(struct ieee80211com *ic) +{ + struct rsu_softc *sc = ic->ic_softc; + struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + int error; + + /* Scanning is done by the firmware. */ + RSU_LOCK(sc); + sc->sc_active_scan = !!(ss->ss_flags & IEEE80211_SCAN_ACTIVE); + /* XXX TODO: force awake if in network-sleep? */ + error = rsu_site_survey(sc, ss->ss_nssid > 0 ? &ss->ss_ssid[0] : NULL); + RSU_UNLOCK(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "could not send site survey command\n"); + ieee80211_cancel_scan(vap); + } +} + +static void +rsu_scan_end(struct ieee80211com *ic) +{ + /* Nothing to do here. */ +} + +static void +rsu_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct rsu_softc *sc = ic->ic_softc; + uint8_t bands[IEEE80211_MODE_BYTES]; + + /* Set supported .11b and .11g rates. */ + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + if (sc->sc_ht) + setbit(bands, IEEE80211_MODE_11NG); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + rsu_chan_2ghz, nitems(rsu_chan_2ghz), bands, + (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) != 0); +} + +static void +rsu_set_channel(struct ieee80211com *ic) +{ + struct rsu_softc *sc = ic->ic_softc; + + /* + * Only need to set the channel in Monitor mode. AP scanning and auth + * are already taken care of by their respective firmware commands. + */ + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + struct r92s_set_channel cmd; + int error; + + cmd.channel = IEEE80211_CHAN2IEEE(ic->ic_curchan); + + RSU_LOCK(sc); + error = rsu_fw_cmd(sc, R92S_CMD_SET_CHANNEL, &cmd, + sizeof(cmd)); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: error %d setting channel\n", __func__, + error); + } + RSU_UNLOCK(sc); + } +} + +static void +rsu_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) +{ + /* Scan is done in rsu_scan_start(). */ +} + +/** + * Called by the net80211 framework to indicate + * the minimum dwell time has been met, terminate the scan. + * We don't actually terminate the scan as the firmware will notify + * us when it's finished and we have no way to interrupt it. + */ +static void +rsu_scan_mindwell(struct ieee80211_scan_state *ss) +{ + /* NB: don't try to abort scan; wait for firmware to finish */ +} + +static void +rsu_update_promisc(struct ieee80211com *ic) +{ + struct rsu_softc *sc = ic->ic_softc; + + RSU_LOCK(sc); + if (sc->sc_running) + rsu_rxfilter_refresh(sc); + RSU_UNLOCK(sc); +} + +/* + * The same as rtwn_get_multi_pos() / rtwn_set_multi(). + */ +static uint8_t +rsu_get_multi_pos(const uint8_t maddr[]) +{ + uint64_t mask = 0x00004d101df481b4; + uint8_t pos = 0x27; /* initial value */ + int i, j; + + for (i = 0; i < IEEE80211_ADDR_LEN; i++) + for (j = (i == 0) ? 1 : 0; j < 8; j++) + if ((maddr[i] >> j) & 1) + pos ^= (mask >> (i * 8 + j - 1)); + + pos &= 0x3f; + + return (pos); +} + +static void +rsu_set_multi(struct rsu_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t mfilt[2]; + + RSU_ASSERT_LOCKED(sc); + + /* general structure was copied from ath(4). */ + if (ic->ic_allmulti == 0) { + struct ieee80211vap *vap; + struct ifnet *ifp; + struct ifmultiaddr *ifma; + + /* + * Merge multicast addresses to form the hardware filter. + */ + mfilt[0] = mfilt[1] = 0; + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + ifp = vap->iv_ifp; + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + caddr_t dl; + uint8_t pos; + + dl = LLADDR((struct sockaddr_dl *) + ifma->ifma_addr); + pos = rsu_get_multi_pos(dl); + + mfilt[pos / 32] |= (1 << (pos % 32)); + } + if_maddr_runlock(ifp); + } + } else + mfilt[0] = mfilt[1] = ~0; + + rsu_write_4(sc, R92S_MAR + 0, mfilt[0]); + rsu_write_4(sc, R92S_MAR + 4, mfilt[1]); + + RSU_DPRINTF(sc, RSU_DEBUG_STATE, "%s: MC filter %08x:%08x\n", + __func__, mfilt[0], mfilt[1]); +} + +static void +rsu_update_mcast(struct ieee80211com *ic) +{ + struct rsu_softc *sc = ic->ic_softc; + + RSU_LOCK(sc); + if (sc->sc_running) + rsu_set_multi(sc); + RSU_UNLOCK(sc); +} + +static int +rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[], + int ndata, int maxsz) +{ + int i, error; + + for (i = 0; i < ndata; i++) { + struct rsu_data *dp = &data[i]; + dp->sc = sc; + dp->m = NULL; + dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); + if (dp->buf == NULL) { + device_printf(sc->sc_dev, + "could not allocate buffer\n"); + error = ENOMEM; + goto fail; + } + dp->ni = NULL; + } + + return (0); +fail: + rsu_free_list(sc, data, ndata); + return (error); +} + +static int +rsu_alloc_rx_list(struct rsu_softc *sc) +{ + int error, i; + + error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT, + RSU_RXBUFSZ); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + + for (i = 0; i < RSU_RX_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); + + return (0); +} + +static int +rsu_alloc_tx_list(struct rsu_softc *sc) +{ + int error, i; + + error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT, + RSU_TXBUFSZ); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_tx_inactive); + + for (i = 0; i != RSU_N_TRANSFER; i++) { + STAILQ_INIT(&sc->sc_tx_active[i]); + STAILQ_INIT(&sc->sc_tx_pending[i]); + } + + for (i = 0; i < RSU_TX_LIST_COUNT; i++) { + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); + } + + return (0); +} + +static void +rsu_free_tx_list(struct rsu_softc *sc) +{ + int i; + + /* prevent further allocations from TX list(s) */ + STAILQ_INIT(&sc->sc_tx_inactive); + + for (i = 0; i != RSU_N_TRANSFER; i++) { + STAILQ_INIT(&sc->sc_tx_active[i]); + STAILQ_INIT(&sc->sc_tx_pending[i]); + } + + rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT); +} + +static void +rsu_free_rx_list(struct rsu_softc *sc) +{ + /* prevent further allocations from RX list(s) */ + STAILQ_INIT(&sc->sc_rx_inactive); + STAILQ_INIT(&sc->sc_rx_active); + + rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT); +} + +static void +rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata) +{ + int i; + + for (i = 0; i < ndata; i++) { + struct rsu_data *dp = &data[i]; + + if (dp->buf != NULL) { + free(dp->buf, M_USBDEV); + dp->buf = NULL; + } + if (dp->ni != NULL) { + ieee80211_free_node(dp->ni); + dp->ni = NULL; + } + } +} + +static struct rsu_data * +_rsu_getbuf(struct rsu_softc *sc) +{ + struct rsu_data *bf; + + bf = STAILQ_FIRST(&sc->sc_tx_inactive); + if (bf != NULL) + STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); + else + bf = NULL; + return (bf); +} + +static struct rsu_data * +rsu_getbuf(struct rsu_softc *sc) +{ + struct rsu_data *bf; + + RSU_ASSERT_LOCKED(sc); + + bf = _rsu_getbuf(sc); + if (bf == NULL) { + RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: no buffers\n", __func__); + } + return (bf); +} + +static void +rsu_freebuf(struct rsu_softc *sc, struct rsu_data *bf) +{ + + RSU_ASSERT_LOCKED(sc); + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, bf, next); +} + +static int +rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf, + int len) +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = R92S_REQ_REGS; + USETW(req.wValue, addr); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + return (rsu_do_request(sc, &req, buf)); +} + +static void +rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val) +{ + rsu_write_region_1(sc, addr, &val, 1); +} + +static void +rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val) +{ + val = htole16(val); + rsu_write_region_1(sc, addr, (uint8_t *)&val, 2); +} + +static void +rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val) +{ + val = htole32(val); + rsu_write_region_1(sc, addr, (uint8_t *)&val, 4); +} + +static int +rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf, + int len) +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = R92S_REQ_REGS; + USETW(req.wValue, addr); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + return (rsu_do_request(sc, &req, buf)); +} + +static uint8_t +rsu_read_1(struct rsu_softc *sc, uint16_t addr) +{ + uint8_t val; + + if (rsu_read_region_1(sc, addr, &val, 1) != 0) + return (0xff); + return (val); +} + +static uint16_t +rsu_read_2(struct rsu_softc *sc, uint16_t addr) +{ + uint16_t val; + + if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0) + return (0xffff); + return (le16toh(val)); +} + +static uint32_t +rsu_read_4(struct rsu_softc *sc, uint16_t addr) +{ + uint32_t val; + + if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0) + return (0xffffffff); + return (le32toh(val)); +} + +static int +rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd) +{ + int ntries; + + rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd); + rsu_ms_delay(sc, 1); + for (ntries = 0; ntries < 50; ntries++) { + if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0) + return (0); + rsu_ms_delay(sc, 1); + } + return (ETIMEDOUT); +} + +static uint8_t +rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr) +{ + uint32_t reg; + int ntries; + + reg = rsu_read_4(sc, R92S_EFUSE_CTRL); + reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr); + reg &= ~R92S_EFUSE_CTRL_VALID; + rsu_write_4(sc, R92S_EFUSE_CTRL, reg); + /* Wait for read operation to complete. */ + for (ntries = 0; ntries < 100; ntries++) { + reg = rsu_read_4(sc, R92S_EFUSE_CTRL); + if (reg & R92S_EFUSE_CTRL_VALID) + return (MS(reg, R92S_EFUSE_CTRL_DATA)); + rsu_ms_delay(sc, 1); + } + device_printf(sc->sc_dev, + "could not read efuse byte at address 0x%x\n", addr); + return (0xff); +} + +static int +rsu_read_rom(struct rsu_softc *sc) +{ + uint8_t *rom = sc->rom; + uint16_t addr = 0; + uint32_t reg; + uint8_t off, msk; + int i; + + /* Make sure that ROM type is eFuse and that autoload succeeded. */ + reg = rsu_read_1(sc, R92S_EE_9346CR); + if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN) + return (EIO); + + /* Turn on 2.5V to prevent eFuse leakage. */ + reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3); + rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80); + rsu_ms_delay(sc, 1); + rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80); + + /* Read full ROM image. */ + memset(&sc->rom, 0xff, sizeof(sc->rom)); + while (addr < 512) { + reg = rsu_efuse_read_1(sc, addr); + if (reg == 0xff) + break; + addr++; + off = reg >> 4; + msk = reg & 0xf; + for (i = 0; i < 4; i++) { + if (msk & (1 << i)) + continue; + rom[off * 8 + i * 2 + 0] = + rsu_efuse_read_1(sc, addr); + addr++; + rom[off * 8 + i * 2 + 1] = + rsu_efuse_read_1(sc, addr); + addr++; + } + } +#ifdef USB_DEBUG + if (rsu_debug & RSU_DEBUG_RESET) { + /* Dump ROM content. */ + printf("\n"); + for (i = 0; i < sizeof(sc->rom); i++) + printf("%02x:", rom[i]); + printf("\n"); + } +#endif + return (0); +} + +static int +rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len) +{ + const uint8_t which = RSU_H2C_ENDPOINT; + struct rsu_data *data; + struct r92s_tx_desc *txd; + struct r92s_fw_cmd_hdr *cmd; + int cmdsz; + int xferlen; + + RSU_ASSERT_LOCKED(sc); + + data = rsu_getbuf(sc); + if (data == NULL) + return (ENOMEM); + + /* Blank the entire payload, just to be safe */ + memset(data->buf, '\0', RSU_TXBUFSZ); + + /* Round-up command length to a multiple of 8 bytes. */ + /* XXX TODO: is this required? */ + cmdsz = (len + 7) & ~7; + + xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz; + KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__)); + memset(data->buf, 0, xferlen); + + /* Setup Tx descriptor. */ + txd = (struct r92s_tx_desc *)data->buf; + txd->txdw0 = htole32( + SM(R92S_TXDW0_OFFSET, sizeof(*txd)) | + SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) | + R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG); + txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C)); + + /* Setup command header. */ + cmd = (struct r92s_fw_cmd_hdr *)&txd[1]; + cmd->len = htole16(cmdsz); + cmd->code = code; + cmd->seq = sc->cmd_seq; + sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f; + + /* Copy command payload. */ + memcpy(&cmd[1], buf, len); + + RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FWCMD, + "%s: Tx cmd code=0x%x len=0x%x\n", + __func__, code, cmdsz); + data->buflen = xferlen; + STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); + usbd_transfer_start(sc->sc_xfer[which]); + + return (0); +} + +/* ARGSUSED */ +static void +rsu_calib_task(void *arg, int pending __unused) +{ + struct rsu_softc *sc = arg; +#ifdef notyet + uint32_t reg; +#endif + + RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: running calibration task\n", + __func__); + + RSU_LOCK(sc); +#ifdef notyet + /* Read WPS PBC status. */ + rsu_write_1(sc, R92S_MAC_PINMUX_CTRL, + R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG)); + rsu_write_1(sc, R92S_GPIO_IO_SEL, + rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS); + reg = rsu_read_1(sc, R92S_GPIO_CTRL); + if (reg != 0xff && (reg & R92S_GPIO_WPS)) + RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "WPS PBC is pushed\n"); +#endif + /* Read current signal level. */ + if (rsu_fw_iocmd(sc, 0xf4000001) == 0) { + sc->sc_currssi = rsu_read_4(sc, R92S_IOCMD_DATA); + RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: RSSI=%d (%d)\n", + __func__, sc->sc_currssi, + rsu_hwrssi_to_rssi(sc, sc->sc_currssi)); + } + if (sc->sc_calibrating) + taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz); + RSU_UNLOCK(sc); +} + +static void +rsu_tx_task(void *arg, int pending __unused) +{ + struct rsu_softc *sc = arg; + + RSU_LOCK(sc); + _rsu_start(sc); + RSU_UNLOCK(sc); +} + +#define RSU_PWR_UNKNOWN 0x0 +#define RSU_PWR_ACTIVE 0x1 +#define RSU_PWR_OFF 0x2 +#define RSU_PWR_SLEEP 0x3 + +/* + * Set the current power state. + * + * The rtlwifi code doesn't do this so aggressively; it + * waits for an idle period after association with + * no traffic before doing this. + * + * For now - it's on in all states except RUN, and + * in RUN it'll transition to allow sleep. + */ + +struct r92s_pwr_cmd { + uint8_t mode; + uint8_t smart_ps; + uint8_t bcn_pass_time; +}; + +static int +rsu_set_fw_power_state(struct rsu_softc *sc, int state) +{ + struct r92s_set_pwr_mode cmd; + //struct r92s_pwr_cmd cmd; + int error; + + RSU_ASSERT_LOCKED(sc); + + /* only change state if required */ + if (sc->sc_curpwrstate == state) + return (0); + + memset(&cmd, 0, sizeof(cmd)); + + switch (state) { + case RSU_PWR_ACTIVE: + /* Force the hardware awake */ + rsu_write_1(sc, R92S_USB_HRPWM, + R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON); + cmd.mode = R92S_PS_MODE_ACTIVE; + break; + case RSU_PWR_SLEEP: + cmd.mode = R92S_PS_MODE_DTIM; /* XXX configurable? */ + cmd.smart_ps = 1; /* XXX 2 if doing p2p */ + cmd.bcn_pass_time = 5; /* in 100mS usb.c, linux/rtlwifi */ + break; + case RSU_PWR_OFF: + cmd.mode = R92S_PS_MODE_RADIOOFF; + break; + default: + device_printf(sc->sc_dev, "%s: unknown ps mode (%d)\n", + __func__, + state); + return (ENXIO); + } + + RSU_DPRINTF(sc, RSU_DEBUG_RESET, + "%s: setting ps mode to %d (mode %d)\n", + __func__, state, cmd.mode); + error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd)); + if (error == 0) + sc->sc_curpwrstate = state; + + return (error); +} + +static void +rsu_set_led(struct rsu_softc *sc, int on) +{ + rsu_write_1(sc, R92S_LEDCFG, + (rsu_read_1(sc, R92S_LEDCFG) & 0xf0) | (!on << 3)); +} + +static int +rsu_monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, + int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + struct rsu_softc *sc = ic->ic_softc; + struct rsu_vap *uvp = RSU_VAP(vap); + + if (vap->iv_state != nstate) { + IEEE80211_UNLOCK(ic); + RSU_LOCK(sc); + + switch (nstate) { + case IEEE80211_S_INIT: + sc->sc_vap_is_running = 0; + rsu_set_led(sc, 0); + break; + case IEEE80211_S_RUN: + sc->sc_vap_is_running = 1; + rsu_set_led(sc, 1); + break; + default: + /* NOTREACHED */ + break; + } + rsu_rxfilter_refresh(sc); + + RSU_UNLOCK(sc); + IEEE80211_LOCK(ic); + } + + return (uvp->newstate(vap, nstate, arg)); +} + +static int +rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct rsu_vap *uvp = RSU_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rsu_softc *sc = ic->ic_softc; + struct ieee80211_node *ni; + struct ieee80211_rateset *rs; + enum ieee80211_state ostate; + int error, startcal = 0; + + ostate = vap->iv_state; + RSU_DPRINTF(sc, RSU_DEBUG_STATE, "%s: %s -> %s\n", + __func__, + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + if (ostate == IEEE80211_S_RUN) { + RSU_LOCK(sc); + /* Stop calibration. */ + sc->sc_calibrating = 0; + + /* Pause Tx for AC queues. */ + rsu_write_1(sc, R92S_TXPAUSE, R92S_TXPAUSE_AC); + usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10)); + + RSU_UNLOCK(sc); + taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task); + taskqueue_drain(taskqueue_thread, &sc->tx_task); + RSU_LOCK(sc); + /* Disassociate from our current BSS. */ + rsu_disconnect(sc); + usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10)); + + /* Refresh Rx filter (may be modified by firmware). */ + sc->sc_vap_is_running = 0; + rsu_rxfilter_refresh(sc); + + /* Reinstall static keys. */ + if (sc->sc_running) + rsu_reinit_static_keys(sc); + } else + RSU_LOCK(sc); + switch (nstate) { + case IEEE80211_S_INIT: + (void) rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); + break; + case IEEE80211_S_AUTH: + ni = ieee80211_ref_node(vap->iv_bss); + (void) rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); + error = rsu_join_bss(sc, ni); + ieee80211_free_node(ni); + if (error != 0) { + device_printf(sc->sc_dev, + "could not send join command\n"); + } + break; + case IEEE80211_S_RUN: + /* Flush all AC queues. */ + rsu_write_1(sc, R92S_TXPAUSE, 0); + + ni = ieee80211_ref_node(vap->iv_bss); + rs = &ni->ni_rates; + /* Indicate highest supported rate. */ + ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; + (void) rsu_set_fw_power_state(sc, RSU_PWR_SLEEP); + ieee80211_free_node(ni); + startcal = 1; + break; + default: + break; + } + if (startcal != 0) { + sc->sc_calibrating = 1; + /* Start periodic calibration. */ + taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, + hz); + } + RSU_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); +} + +static int +rsu_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) +{ + struct rsu_softc *sc = vap->iv_ic->ic_softc; + int is_checked = 0; + + if (&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + *keyix = ieee80211_crypto_get_key_wepidx(vap, k); + } else { + if (vap->iv_opmode != IEEE80211_M_STA) { + *keyix = 0; + /* TODO: obtain keyix from node id */ + is_checked = 1; + k->wk_flags |= IEEE80211_KEY_SWCRYPT; + } else + *keyix = R92S_MACID_BSS; + } + + if (!is_checked) { + RSU_LOCK(sc); + if (isset(sc->keys_bmap, *keyix)) { + device_printf(sc->sc_dev, + "%s: key slot %d is already used!\n", + __func__, *keyix); + RSU_UNLOCK(sc); + return (0); + } + setbit(sc->keys_bmap, *keyix); + RSU_UNLOCK(sc); + } + + *rxkeyix = *keyix; + + return (1); +} + +static int +rsu_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k, + int set) +{ + struct rsu_softc *sc = vap->iv_ic->ic_softc; + int ret; + + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + /* Not for us. */ + return (1); + } + + /* Handle group keys. */ + if (&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + KASSERT(k->wk_keyix < nitems(sc->group_keys), + ("keyix %u > %zu\n", k->wk_keyix, nitems(sc->group_keys))); + + RSU_LOCK(sc); + sc->group_keys[k->wk_keyix] = (set ? k : NULL); + if (!sc->sc_running) { + /* Static keys will be set during device startup. */ + RSU_UNLOCK(sc); + return (1); + } + + if (set) + ret = rsu_set_key_group(sc, k); + else + ret = rsu_delete_key(sc, k->wk_keyix); + RSU_UNLOCK(sc); + + return (!ret); + } + + if (set) { + /* wait for pending key removal */ + taskqueue_drain(taskqueue_thread, &sc->del_key_task); + + RSU_LOCK(sc); + ret = rsu_set_key_pair(sc, k); + RSU_UNLOCK(sc); + } else { + RSU_DELKEY_BMAP_LOCK(sc); + setbit(sc->free_keys_bmap, k->wk_keyix); + RSU_DELKEY_BMAP_UNLOCK(sc); + + /* workaround ieee80211_node_delucastkey() locking */ + taskqueue_enqueue(taskqueue_thread, &sc->del_key_task); + ret = 0; /* fake success */ + } + + return (!ret); +} + +static int +rsu_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + return (rsu_process_key(vap, k, 1)); +} + +static int +rsu_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + return (rsu_process_key(vap, k, 0)); +} + +static int +rsu_cam_read(struct rsu_softc *sc, uint8_t addr, uint32_t *val) +{ + int ntries; + + rsu_write_4(sc, R92S_CAMCMD, + R92S_CAMCMD_POLLING | SM(R92S_CAMCMD_ADDR, addr)); + for (ntries = 0; ntries < 10; ntries++) { + if (!(rsu_read_4(sc, R92S_CAMCMD) & R92S_CAMCMD_POLLING)) + break; + + usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(1)); + } + if (ntries == 10) { + device_printf(sc->sc_dev, + "%s: cannot read CAM entry at address %02X\n", + __func__, addr); + return (ETIMEDOUT); + } + + *val = rsu_read_4(sc, R92S_CAMREAD); + + return (0); +} + +static void +rsu_cam_write(struct rsu_softc *sc, uint8_t addr, uint32_t data) +{ + + rsu_write_4(sc, R92S_CAMWRITE, data); + rsu_write_4(sc, R92S_CAMCMD, + R92S_CAMCMD_POLLING | R92S_CAMCMD_WRITE | + SM(R92S_CAMCMD_ADDR, addr)); +} + +static int +rsu_key_check(struct rsu_softc *sc, ieee80211_keyix keyix, int is_valid) +{ + uint32_t val; + int error, ntries; + + for (ntries = 0; ntries < 20; ntries++) { + usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(1)); + + error = rsu_cam_read(sc, R92S_CAM_CTL0(keyix), &val); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: cannot check key status!\n", __func__); + return (error); + } + if (((val & R92S_CAM_VALID) == 0) ^ is_valid) + break; + } + if (ntries == 20) { + device_printf(sc->sc_dev, + "%s: key %d is %s marked as valid, rejecting request\n", + __func__, keyix, is_valid ? "not" : "still"); + return (EIO); + } + + return (0); +} + +/* + * Map net80211 cipher to RTL8712 security mode. + */ +static uint8_t +rsu_crypto_mode(struct rsu_softc *sc, u_int cipher, int keylen) +{ + switch (cipher) { + case IEEE80211_CIPHER_WEP: + return keylen < 8 ? R92S_KEY_ALGO_WEP40 : R92S_KEY_ALGO_WEP104; + case IEEE80211_CIPHER_TKIP: + return R92S_KEY_ALGO_TKIP; + case IEEE80211_CIPHER_AES_CCM: + return R92S_KEY_ALGO_AES; + default: + device_printf(sc->sc_dev, "unknown cipher %d\n", cipher); + return R92S_KEY_ALGO_INVALID; + } +} + +static int +rsu_set_key_group(struct rsu_softc *sc, const struct ieee80211_key *k) +{ + struct r92s_fw_cmd_set_key key; + uint8_t algo; + int error; + + RSU_ASSERT_LOCKED(sc); + + /* Map net80211 cipher to HW crypto algorithm. */ + algo = rsu_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen); + if (algo == R92S_KEY_ALGO_INVALID) + return (EINVAL); + + memset(&key, 0, sizeof(key)); + key.algo = algo; + key.cam_id = k->wk_keyix; + key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0; + memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key))); + + RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD, + "%s: keyix %u, group %u, algo %u/%u, flags %04X, len %u, " + "macaddr %s\n", __func__, key.cam_id, key.grpkey, + k->wk_cipher->ic_cipher, key.algo, k->wk_flags, k->wk_keylen, + ether_sprintf(k->wk_macaddr)); + + error = rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key)); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: cannot send firmware command, error %d\n", + __func__, error); + return (error); + } + + return (rsu_key_check(sc, k->wk_keyix, 1)); +} + +static int +rsu_set_key_pair(struct rsu_softc *sc, const struct ieee80211_key *k) +{ + struct r92s_fw_cmd_set_key_mac key; + uint8_t algo; + int error; + + RSU_ASSERT_LOCKED(sc); + + if (!sc->sc_running) + return (ESHUTDOWN); + + /* Map net80211 cipher to HW crypto algorithm. */ + algo = rsu_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen); + if (algo == R92S_KEY_ALGO_INVALID) + return (EINVAL); + + memset(&key, 0, sizeof(key)); + key.algo = algo; + memcpy(key.macaddr, k->wk_macaddr, sizeof(key.macaddr)); + memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key))); + + RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD, + "%s: keyix %u, algo %u/%u, flags %04X, len %u, macaddr %s\n", + __func__, k->wk_keyix, k->wk_cipher->ic_cipher, key.algo, + k->wk_flags, k->wk_keylen, ether_sprintf(key.macaddr)); + + error = rsu_fw_cmd(sc, R92S_CMD_SET_STA_KEY, &key, sizeof(key)); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: cannot send firmware command, error %d\n", + __func__, error); + return (error); + } + + return (rsu_key_check(sc, k->wk_keyix, 1)); +} + +static int +rsu_reinit_static_keys(struct rsu_softc *sc) +{ + int i, error; + + for (i = 0; i < nitems(sc->group_keys); i++) { + if (sc->group_keys[i] != NULL) { + error = rsu_set_key_group(sc, sc->group_keys[i]); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: failed to set static key %d, " + "error %d\n", __func__, i, error); + return (error); + } + } + } + + return (0); +} + +static int +rsu_delete_key(struct rsu_softc *sc, ieee80211_keyix keyix) +{ + struct r92s_fw_cmd_set_key key; + uint32_t val; + int error; + + RSU_ASSERT_LOCKED(sc); + + if (!sc->sc_running) + return (0); + + /* check if it was automatically removed by firmware */ + error = rsu_cam_read(sc, R92S_CAM_CTL0(keyix), &val); + if (error == 0 && (val & R92S_CAM_VALID) == 0) { + RSU_DPRINTF(sc, RSU_DEBUG_KEY, + "%s: key %u does not exist\n", __func__, keyix); + clrbit(sc->keys_bmap, keyix); + return (0); + } + + memset(&key, 0, sizeof(key)); + key.cam_id = keyix; + + RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD, + "%s: removing key %u\n", __func__, key.cam_id); + + error = rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key)); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: cannot send firmware command, error %d\n", + __func__, error); + goto finish; + } + + usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(5)); + + /* + * Clear 'valid' bit manually (cannot be done via firmware command). + * Used for key check + when firmware command cannot be sent. + */ +finish: + rsu_cam_write(sc, R92S_CAM_CTL0(keyix), 0); + + clrbit(sc->keys_bmap, keyix); + + return (rsu_key_check(sc, keyix, 0)); +} + +static void +rsu_delete_key_pair_cb(void *arg, int pending __unused) +{ + struct rsu_softc *sc = arg; + int i; + + RSU_DELKEY_BMAP_LOCK(sc); + for (i = IEEE80211_WEP_NKID; i < R92S_CAM_ENTRY_LIMIT; i++) { + if (isset(sc->free_keys_bmap, i)) { + RSU_DELKEY_BMAP_UNLOCK(sc); + + RSU_LOCK(sc); + RSU_DPRINTF(sc, RSU_DEBUG_KEY, + "%s: calling rsu_delete_key() with keyix = %d\n", + __func__, i); + (void) rsu_delete_key(sc, i); + RSU_UNLOCK(sc); + + RSU_DELKEY_BMAP_LOCK(sc); + clrbit(sc->free_keys_bmap, i); + + /* bmap can be changed */ + i = IEEE80211_WEP_NKID - 1; + continue; + } + } + RSU_DELKEY_BMAP_UNLOCK(sc); +} + +static int +rsu_site_survey(struct rsu_softc *sc, struct ieee80211_scan_ssid *ssid) +{ + struct r92s_fw_cmd_sitesurvey cmd; + + RSU_ASSERT_LOCKED(sc); + + memset(&cmd, 0, sizeof(cmd)); + /* TODO: passive channels? */ + if (sc->sc_active_scan) + cmd.active = htole32(1); + cmd.limit = htole32(48); + + if (ssid != NULL) { + sc->sc_extra_scan = 1; + cmd.ssidlen = htole32(ssid->len); + memcpy(cmd.ssid, ssid->ssid, ssid->len); + } +#ifdef USB_DEBUG + if (rsu_debug & (RSU_DEBUG_SCAN | RSU_DEBUG_FWCMD)) { + device_printf(sc->sc_dev, + "sending site survey command, active %d", + le32toh(cmd.active)); + if (ssid != NULL) { + printf(", ssid: "); + ieee80211_print_essid(cmd.ssid, le32toh(cmd.ssidlen)); + } + printf("\n"); + } +#endif + return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd))); +} + +static int +rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ndis_wlan_bssid_ex *bss; + struct ndis_802_11_fixed_ies *fixed; + struct r92s_fw_cmd_auth auth; + uint8_t buf[sizeof(*bss) + 128] __aligned(4); + uint8_t *frm; + uint8_t opmode; + int error; + + RSU_ASSERT_LOCKED(sc); + + /* Let the FW decide the opmode based on the capinfo field. */ + opmode = NDIS802_11AUTOUNKNOWN; + RSU_DPRINTF(sc, RSU_DEBUG_RESET, + "%s: setting operating mode to %d\n", + __func__, opmode); + error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode)); + if (error != 0) + return (error); + + memset(&auth, 0, sizeof(auth)); + if (vap->iv_flags & IEEE80211_F_WPA) { + auth.mode = R92S_AUTHMODE_WPA; + auth.dot1x = (ni->ni_authmode == IEEE80211_AUTH_8021X); + } else + auth.mode = R92S_AUTHMODE_OPEN; + RSU_DPRINTF(sc, RSU_DEBUG_RESET, + "%s: setting auth mode to %d\n", + __func__, auth.mode); + error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth)); + if (error != 0) + return (error); + + memset(buf, 0, sizeof(buf)); + bss = (struct ndis_wlan_bssid_ex *)buf; + IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid); + bss->ssid.ssidlen = htole32(ni->ni_esslen); + memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen); + if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA)) + bss->privacy = htole32(1); + bss->rssi = htole32(ni->ni_avgrssi); + if (ic->ic_curmode == IEEE80211_MODE_11B) + bss->networktype = htole32(NDIS802_11DS); + else + bss->networktype = htole32(NDIS802_11OFDM24); + bss->config.len = htole32(sizeof(bss->config)); + bss->config.bintval = htole32(ni->ni_intval); + bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan)); + bss->inframode = htole32(NDIS802_11INFRASTRUCTURE); + /* XXX verify how this is supposed to look! */ + memcpy(bss->supprates, ni->ni_rates.rs_rates, + ni->ni_rates.rs_nrates); + /* Write the fixed fields of the beacon frame. */ + fixed = (struct ndis_802_11_fixed_ies *)&bss[1]; + memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8); + fixed->bintval = htole16(ni->ni_intval); + fixed->capabilities = htole16(ni->ni_capinfo); + /* Write IEs to be included in the association request. */ + frm = (uint8_t *)&fixed[1]; + frm = ieee80211_add_rsn(frm, vap); + frm = ieee80211_add_wpa(frm, vap); + frm = ieee80211_add_qos(frm, ni); + if ((ic->ic_flags & IEEE80211_F_WME) && + (ni->ni_ies.wme_ie != NULL)) + frm = ieee80211_add_wme_info(frm, &ic->ic_wme); + if (ni->ni_flags & IEEE80211_NODE_HT) { + frm = ieee80211_add_htcap(frm, ni); + frm = ieee80211_add_htinfo(frm, ni); + } + bss->ieslen = htole32(frm - (uint8_t *)fixed); + bss->len = htole32(((frm - buf) + 3) & ~3); + RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_FWCMD, + "%s: sending join bss command to %s chan %d\n", + __func__, + ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig)); + return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf))); +} + +static int +rsu_disconnect(struct rsu_softc *sc) +{ + uint32_t zero = 0; /* :-) */ + + /* Disassociate from our current BSS. */ + RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, + "%s: sending disconnect command\n", __func__); + return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero))); +} + +/* + * Map the hardware provided RSSI value to a signal level. + * For the most part it's just something we divide by and cap + * so it doesn't overflow the representation by net80211. + */ +static int +rsu_hwrssi_to_rssi(struct rsu_softc *sc, int hw_rssi) +{ + int v; + + if (hw_rssi == 0) + return (0); + v = hw_rssi >> 4; + if (v > 80) + v = 80; + return (v); +} + +static void +rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ndis_wlan_bssid_ex *bss; + struct ieee80211_rx_stats rxs; + struct mbuf *m; + int pktlen; + + if (__predict_false(len < sizeof(*bss))) + return; + bss = (struct ndis_wlan_bssid_ex *)buf; + if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen))) + return; + + RSU_DPRINTF(sc, RSU_DEBUG_SCAN, + "%s: found BSS %s: len=%d chan=%d inframode=%d " + "networktype=%d privacy=%d, RSSI=%d\n", + __func__, + ether_sprintf(bss->macaddr), le32toh(bss->len), + le32toh(bss->config.dsconfig), le32toh(bss->inframode), + le32toh(bss->networktype), le32toh(bss->privacy), + le32toh(bss->rssi)); + + /* Build a fake beacon frame to let net80211 do all the parsing. */ + /* XXX TODO: just call the new scan API methods! */ + pktlen = sizeof(*wh) + le32toh(bss->ieslen); + if (__predict_false(pktlen > MCLBYTES)) + return; + m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); + if (__predict_false(m == NULL)) + return; + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_BEACON; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + USETW(wh->i_dur, 0); + IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr); + *(uint16_t *)wh->i_seq = 0; + memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen)); + + /* Finalize mbuf. */ + m->m_pkthdr.len = m->m_len = pktlen; + + /* Set channel flags for input path */ + bzero(&rxs, sizeof(rxs)); + rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; + rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; + rxs.c_ieee = le32toh(bss->config.dsconfig); + rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ); + /* This is a number from 0..100; so let's just divide it down a bit */ + rxs.c_rssi = le32toh(bss->rssi) / 2; + rxs.c_nf = -96; + if (ieee80211_add_rx_params(m, &rxs) == 0) + return; + + /* XXX avoid a LOR */ + RSU_UNLOCK(sc); + ieee80211_input_mimo_all(ic, m); + RSU_LOCK(sc); +} + +static void +rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni = vap->iv_bss; + struct r92s_event_join_bss *rsp; + uint32_t tmp; + int res; + + if (__predict_false(len < sizeof(*rsp))) + return; + rsp = (struct r92s_event_join_bss *)buf; + res = (int)le32toh(rsp->join_res); + + RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, + "%s: Rx join BSS event len=%d res=%d\n", + __func__, len, res); + + /* + * XXX Don't do this; there's likely a better way to tell + * the caller we failed. + */ + if (res <= 0) { + RSU_UNLOCK(sc); + ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); + RSU_LOCK(sc); + return; + } + + tmp = le32toh(rsp->associd); + if (tmp >= vap->iv_max_aid) { + RSU_DPRINTF(sc, RSU_DEBUG_ANY, "Assoc ID overflow\n"); + tmp = 1; + } + RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, + "%s: associated with %s associd=%d\n", + __func__, ether_sprintf(rsp->bss.macaddr), tmp); + /* XXX is this required? What's the top two bits for again? */ + ni->ni_associd = tmp | 0xc000; + + /* Refresh Rx filter (was changed by firmware). */ + sc->sc_vap_is_running = 1; + rsu_rxfilter_refresh(sc); + + RSU_UNLOCK(sc); + ieee80211_new_state(vap, IEEE80211_S_RUN, + IEEE80211_FC0_SUBTYPE_ASSOC_RESP); + RSU_LOCK(sc); +} + +static void +rsu_event_addba_req_report(struct rsu_softc *sc, uint8_t *buf, int len) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct r92s_add_ba_event *ba = (void *) buf; + struct ieee80211_node *ni; + + if (len < sizeof(*ba)) { + device_printf(sc->sc_dev, "%s: short read (%d)\n", __func__, len); + return; + } + + if (vap == NULL) + return; + + RSU_DPRINTF(sc, RSU_DEBUG_AMPDU, "%s: mac=%s, tid=%d, ssn=%d\n", + __func__, + ether_sprintf(ba->mac_addr), + (int) ba->tid, + (int) le16toh(ba->ssn)); + + /* XXX do node lookup; this is STA specific */ + + ni = ieee80211_ref_node(vap->iv_bss); + ieee80211_ampdu_rx_start_ext(ni, ba->tid, le16toh(ba->ssn) >> 4, 32); + ieee80211_free_node(ni); +} + +static void +rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD, + "%s: Rx event code=%d len=%d\n", __func__, code, len); + switch (code) { + case R92S_EVT_SURVEY: + rsu_event_survey(sc, buf, len); + break; + case R92S_EVT_SURVEY_DONE: + RSU_DPRINTF(sc, RSU_DEBUG_SCAN, + "%s: %s scan done, found %d BSS\n", + __func__, sc->sc_extra_scan ? "direct" : "broadcast", + le32toh(*(uint32_t *)buf)); + if (sc->sc_extra_scan == 1) { + /* Send broadcast probe request. */ + sc->sc_extra_scan = 0; + if (vap != NULL && rsu_site_survey(sc, NULL) != 0) { + RSU_UNLOCK(sc); + ieee80211_cancel_scan(vap); + RSU_LOCK(sc); + } + break; + } + if (vap != NULL) { + RSU_UNLOCK(sc); + ieee80211_scan_done(vap); + RSU_LOCK(sc); + } + break; + case R92S_EVT_JOIN_BSS: + if (vap->iv_state == IEEE80211_S_AUTH) + rsu_event_join_bss(sc, buf, len); + break; + case R92S_EVT_DEL_STA: + RSU_DPRINTF(sc, RSU_DEBUG_FWCMD | RSU_DEBUG_STATE, + "%s: disassociated from %s\n", __func__, + ether_sprintf(buf)); + if (vap->iv_state == IEEE80211_S_RUN && + IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) { + RSU_UNLOCK(sc); + ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); + RSU_LOCK(sc); + } + break; + case R92S_EVT_WPS_PBC: + RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD, + "%s: WPS PBC pushed.\n", __func__); + break; + case R92S_EVT_FWDBG: + buf[60] = '\0'; + RSU_DPRINTF(sc, RSU_DEBUG_FWDBG, "FWDBG: %s\n", (char *)buf); + break; + case R92S_EVT_ADDBA_REQ_REPORT: + rsu_event_addba_req_report(sc, buf, len); + break; + default: + device_printf(sc->sc_dev, "%s: unhandled code (%d)\n", __func__, code); + break; + } +} + +static void +rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len) +{ + struct r92s_fw_cmd_hdr *cmd; + int cmdsz; + + RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx events len=%d\n", __func__, len); + + /* Skip Rx status. */ + buf += sizeof(struct r92s_rx_stat); + len -= sizeof(struct r92s_rx_stat); + + /* Process all events. */ + for (;;) { + /* Check that command header fits. */ + if (__predict_false(len < sizeof(*cmd))) + break; + cmd = (struct r92s_fw_cmd_hdr *)buf; + /* Check that command payload fits. */ + cmdsz = le16toh(cmd->len); + if (__predict_false(len < sizeof(*cmd) + cmdsz)) + break; + + /* Process firmware event. */ + rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz); + + if (!(cmd->seq & R92S_FW_CMD_MORE)) + break; + buf += sizeof(*cmd) + cmdsz; + len -= sizeof(*cmd) + cmdsz; + } +} + +static int8_t +rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt) +{ + static const int8_t cckoff[] = { 14, -2, -20, -40 }; + struct r92s_rx_phystat *phy; + struct r92s_rx_cck *cck; + uint8_t rpt; + int8_t rssi; + + if (rate <= 3) { + cck = (struct r92s_rx_cck *)physt; + rpt = (cck->agc_rpt >> 6) & 0x3; + rssi = cck->agc_rpt & 0x3e; + rssi = cckoff[rpt] - rssi; + } else { /* OFDM/HT. */ + phy = (struct r92s_rx_phystat *)physt; + rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106; + } + return (rssi); +} + +static struct mbuf * +rsu_rx_copy_to_mbuf(struct rsu_softc *sc, struct r92s_rx_stat *stat, + int totlen) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct mbuf *m; + uint32_t rxdw0; + int pktlen; + + rxdw0 = le32toh(stat->rxdw0); + if (__predict_false(rxdw0 & (R92S_RXDW0_CRCERR | R92S_RXDW0_ICVERR))) { + RSU_DPRINTF(sc, RSU_DEBUG_RX, + "%s: RX flags error (%s)\n", __func__, + rxdw0 & R92S_RXDW0_CRCERR ? "CRC" : "ICV"); + goto fail; + } + + pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN); + if (__predict_false(pktlen < sizeof (struct ieee80211_frame_ack))) { + RSU_DPRINTF(sc, RSU_DEBUG_RX, + "%s: frame is too short: %d\n", __func__, pktlen); + goto fail; + } + + m = m_get2(totlen, M_NOWAIT, MT_DATA, M_PKTHDR); + if (__predict_false(m == NULL)) { + device_printf(sc->sc_dev, + "%s: could not allocate RX mbuf, totlen %d\n", + __func__, totlen); + goto fail; + } + + /* Finalize mbuf. */ + memcpy(mtod(m, uint8_t *), (uint8_t *)stat, totlen); + m->m_pkthdr.len = m->m_len = totlen; + + return (m); +fail: + counter_u64_add(ic->ic_ierrors, 1); + return (NULL); +} + +static uint32_t +rsu_get_tsf_low(struct rsu_softc *sc) +{ + return (rsu_read_4(sc, R92S_TSFTR)); +} + +static uint32_t +rsu_get_tsf_high(struct rsu_softc *sc) +{ + return (rsu_read_4(sc, R92S_TSFTR + 4)); +} + +static struct ieee80211_node * +rsu_rx_frame(struct rsu_softc *sc, struct mbuf *m) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame_min *wh; + struct ieee80211_rx_stats rxs; + struct r92s_rx_stat *stat; + uint32_t rxdw0, rxdw3; + uint8_t cipher, rate; + int infosz; + int rssi; + + stat = mtod(m, struct r92s_rx_stat *); + rxdw0 = le32toh(stat->rxdw0); + rxdw3 = le32toh(stat->rxdw3); + + rate = MS(rxdw3, R92S_RXDW3_RATE); + cipher = MS(rxdw0, R92S_RXDW0_CIPHER); + infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8; + + /* Get RSSI from PHY status descriptor if present. */ + if (infosz != 0 && (rxdw0 & R92S_RXDW0_PHYST)) + rssi = rsu_get_rssi(sc, rate, &stat[1]); + else { + /* Cheat and get the last calibrated RSSI */ + rssi = rsu_hwrssi_to_rssi(sc, sc->sc_currssi); + } + + if (ieee80211_radiotap_active(ic)) { + struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap; + + /* Map HW rate index to 802.11 rate. */ + tap->wr_flags = 0; /* TODO */ + tap->wr_tsft = rsu_get_tsf_high(sc); + if (le32toh(stat->tsf_low) > rsu_get_tsf_low(sc)) + tap->wr_tsft--; + tap->wr_tsft = (uint64_t)htole32(tap->wr_tsft) << 32; + tap->wr_tsft += stat->tsf_low; + + if (rate < 12) { + switch (rate) { + /* CCK. */ + case 0: tap->wr_rate = 2; break; + case 1: tap->wr_rate = 4; break; + case 2: tap->wr_rate = 11; break; + case 3: tap->wr_rate = 22; break; + /* OFDM. */ + case 4: tap->wr_rate = 12; break; + case 5: tap->wr_rate = 18; break; + case 6: tap->wr_rate = 24; break; + case 7: tap->wr_rate = 36; break; + case 8: tap->wr_rate = 48; break; + case 9: tap->wr_rate = 72; break; + case 10: tap->wr_rate = 96; break; + case 11: tap->wr_rate = 108; break; + } + } else { /* MCS0~15. */ + /* Bit 7 set means HT MCS instead of rate. */ + tap->wr_rate = 0x80 | (rate - 12); + } + + tap->wr_dbm_antsignal = rssi; + tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + }; + + /* Hardware does Rx TCP checksum offload. */ + /* + * This flag can be set for some other + * (e.g., EAPOL) frame types, so don't rely on it. + */ + if (rxdw3 & R92S_RXDW3_TCPCHKVALID) { + RSU_DPRINTF(sc, RSU_DEBUG_RX, + "%s: TCP/IP checksums: %schecked / %schecked\n", + __func__, + (rxdw3 & R92S_RXDW3_TCPCHKRPT) ? "" : "not ", + (rxdw3 & R92S_RXDW3_IPCHKRPT) ? "" : "not "); + + /* + * 'IP header checksum valid' bit will not be set if + * the frame was not checked / has incorrect checksum / + * does not have checksum (IPv6). + * + * NB: if DF bit is not set then frame will not be checked. + */ + if (rxdw3 & R92S_RXDW3_IPCHKRPT) { + m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; + m->m_pkthdr.csum_flags |= CSUM_IP_VALID; + } + + /* + * This is independent of the above check. + */ + if (rxdw3 & R92S_RXDW3_TCPCHKRPT) { + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; + m->m_pkthdr.csum_flags |= CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } + + /* RX flags */ + + /* Set channel flags for input path */ + bzero(&rxs, sizeof(rxs)); + + /* normal RSSI */ + rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; + rxs.c_rssi = rssi; + rxs.c_nf = -96; + + /* Rate */ + if (!(rxdw3 & R92S_RXDW3_HTC)) { + switch (rate) { + /* CCK. */ + case 0: + rxs.c_rate = 2; + rxs.c_pktflags |= IEEE80211_RX_F_CCK; + break; + case 1: + rxs.c_rate = 4; + rxs.c_pktflags |= IEEE80211_RX_F_CCK; + break; + case 2: + rxs.c_rate = 11; + rxs.c_pktflags |= IEEE80211_RX_F_CCK; + break; + case 3: + rxs.c_rate = 22; + rxs.c_pktflags |= IEEE80211_RX_F_CCK; + break; + /* OFDM. */ + case 4: + rxs.c_rate = 12; + rxs.c_pktflags |= IEEE80211_RX_F_OFDM; + break; + case 5: + rxs.c_rate = 18; + rxs.c_pktflags |= IEEE80211_RX_F_OFDM; + break; + case 6: + rxs.c_rate = 24; + rxs.c_pktflags |= IEEE80211_RX_F_OFDM; + break; + case 7: + rxs.c_rate = 36; + rxs.c_pktflags |= IEEE80211_RX_F_OFDM; + break; + case 8: + rxs.c_rate = 48; + rxs.c_pktflags |= IEEE80211_RX_F_OFDM; + break; + case 9: + rxs.c_rate = 72; + rxs.c_pktflags |= IEEE80211_RX_F_OFDM; + break; + case 10: + rxs.c_rate = 96; + rxs.c_pktflags |= IEEE80211_RX_F_OFDM; + break; + case 11: + rxs.c_rate = 108; + rxs.c_pktflags |= IEEE80211_RX_F_OFDM; + break; + } + } else if (rate >= 12) { /* MCS0~15. */ + /* Bit 7 set means HT MCS instead of rate. */ + rxs.c_rate = (rate - 12); + rxs.c_pktflags |= IEEE80211_RX_F_HT; + } + + (void) ieee80211_add_rx_params(m, &rxs); + + /* Drop descriptor. */ + m_adj(m, sizeof(*stat) + infosz); + wh = mtod(m, struct ieee80211_frame_min *); + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + cipher != R92S_KEY_ALGO_NONE) { + m->m_flags |= M_WEP; + } + + RSU_DPRINTF(sc, RSU_DEBUG_RX, + "%s: Rx frame len %d, rate %d, infosz %d\n", + __func__, m->m_len, rate, infosz); + + if (m->m_len >= sizeof(*wh)) + return (ieee80211_find_rxnode(ic, wh)); + + return (NULL); +} + +static struct mbuf * +rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len) +{ + struct r92s_rx_stat *stat; + uint32_t rxdw0; + int totlen, pktlen, infosz, npkts; + struct mbuf *m, *m0 = NULL, *prevm = NULL; + + /* + * don't pass packets to the ieee80211 framework if the driver isn't + * RUNNING. + */ + if (!sc->sc_running) + return (NULL); + + /* Get the number of encapsulated frames. */ + stat = (struct r92s_rx_stat *)buf; + npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT); + RSU_DPRINTF(sc, RSU_DEBUG_RX, + "%s: Rx %d frames in one chunk\n", __func__, npkts); + + /* Process all of them. */ + while (npkts-- > 0) { + if (__predict_false(len < sizeof(*stat))) + break; + stat = (struct r92s_rx_stat *)buf; + rxdw0 = le32toh(stat->rxdw0); + + pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN); + if (__predict_false(pktlen == 0)) + break; + + infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8; + + /* Make sure everything fits in xfer. */ + totlen = sizeof(*stat) + infosz + pktlen; + if (__predict_false(totlen > len)) + break; + + /* Process 802.11 frame. */ + m = rsu_rx_copy_to_mbuf(sc, stat, totlen); + if (m0 == NULL) + m0 = m; + if (prevm == NULL) + prevm = m; + else { + prevm->m_next = m; + prevm = m; + } + /* Next chunk is 128-byte aligned. */ + totlen = (totlen + 127) & ~127; + buf += totlen; + len -= totlen; + } + + return (m0); +} + +static struct mbuf * +rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data) +{ + struct rsu_softc *sc = data->sc; + struct ieee80211com *ic = &sc->sc_ic; + struct r92s_rx_stat *stat; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + if (__predict_false(len < sizeof(*stat))) { + RSU_DPRINTF(sc, RSU_DEBUG_RX, "xfer too short %d\n", len); + counter_u64_add(ic->ic_ierrors, 1); + return (NULL); + } + /* Determine if it is a firmware C2H event or an 802.11 frame. */ + stat = (struct r92s_rx_stat *)data->buf; + if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) { + rsu_rx_multi_event(sc, data->buf, len); + /* No packets to process. */ + return (NULL); + } else + return (rsu_rx_multi_frame(sc, data->buf, len)); +} + +static void +rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct rsu_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct mbuf *m = NULL, *next; + struct rsu_data *data; + + RSU_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data == NULL) + goto tr_setup; + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + m = rsu_rxeof(xfer, data); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->sc_rx_inactive); + if (data == NULL) { + KASSERT(m == NULL, ("mbuf isn't NULL")); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); + usbd_xfer_set_frame_data(xfer, 0, data->buf, + usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + /* + * To avoid LOR we should unlock our private mutex here to call + * ieee80211_input() because here is at the end of a USB + * callback and safe to unlock. + */ + while (m != NULL) { + next = m->m_next; + m->m_next = NULL; + + ni = rsu_rx_frame(sc, m); + RSU_UNLOCK(sc); + + if (ni != NULL) { + if (ni->ni_flags & IEEE80211_NODE_HT) + m->m_flags |= M_AMPDU; + (void)ieee80211_input_mimo(ni, m); + ieee80211_free_node(ni); + } else + (void)ieee80211_input_mimo_all(ic, m); + + RSU_LOCK(sc); + m = next; + } + break; + default: + /* needs it to the inactive queue due to a error. */ + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + break; + } + +} + +static void +rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data) +{ +#ifdef USB_DEBUG + struct rsu_softc *sc = usbd_xfer_softc(xfer); +#endif + + RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: called; data=%p\n", + __func__, + data); + + if (data->m) { + /* XXX status? */ + ieee80211_tx_complete(data->ni, data->m, 0); + data->m = NULL; + data->ni = NULL; + } +} + +static void +rsu_bulk_tx_callback_sub(struct usb_xfer *xfer, usb_error_t error, + uint8_t which) +{ + struct rsu_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct rsu_data *data; + + RSU_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_tx_active[which]); + if (data == NULL) + goto tr_setup; + RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: transfer done %p\n", + __func__, data); + STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); + rsu_txeof(xfer, data); + rsu_freebuf(sc, data); + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->sc_tx_pending[which]); + if (data == NULL) { + RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, + "%s: empty pending queue sc %p\n", __func__, sc); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next); + STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next); + usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); + RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, + "%s: submitting transfer %p\n", + __func__, + data); + usbd_transfer_submit(xfer); + break; + default: + data = STAILQ_FIRST(&sc->sc_tx_active[which]); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); + rsu_txeof(xfer, data); + rsu_freebuf(sc, data); + } + counter_u64_add(ic->ic_oerrors, 1); + + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } + + /* + * XXX TODO: if the queue is low, flush out FF TX frames. + * Remember to unlock the driver for now; net80211 doesn't + * defer it for us. + */ +} + +static void +rsu_bulk_tx_callback_be_bk(struct usb_xfer *xfer, usb_error_t error) +{ + struct rsu_softc *sc = usbd_xfer_softc(xfer); + + rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_BE_BK); + + /* This kicks the TX taskqueue */ + rsu_start(sc); +} + +static void +rsu_bulk_tx_callback_vi_vo(struct usb_xfer *xfer, usb_error_t error) +{ + struct rsu_softc *sc = usbd_xfer_softc(xfer); + + rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_VI_VO); + + /* This kicks the TX taskqueue */ + rsu_start(sc); +} + +static void +rsu_bulk_tx_callback_h2c(struct usb_xfer *xfer, usb_error_t error) +{ + struct rsu_softc *sc = usbd_xfer_softc(xfer); + + rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_H2C); + + /* This kicks the TX taskqueue */ + rsu_start(sc); +} + +/* + * Transmit the given frame. + * + * This doesn't free the node or mbuf upon failure. + */ +static int +rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni, + struct mbuf *m0, struct rsu_data *data) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame *wh; + struct ieee80211_key *k = NULL; + struct r92s_tx_desc *txd; + uint8_t type, cipher; + int prio = 0; + uint8_t which; + int hasqos; + int xferlen; + int qid; + + RSU_ASSERT_LOCKED(sc); + + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: data=%p, m=%p\n", + __func__, data, m0); + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + device_printf(sc->sc_dev, + "ieee80211_crypto_encap returns NULL.\n"); + /* XXX we don't expect the fragmented frames */ + return (ENOBUFS); + } + wh = mtod(m0, struct ieee80211_frame *); + } + /* If we have QoS then use it */ + /* XXX TODO: mbuf WME/PRI versus TID? */ + if (IEEE80211_QOS_HAS_SEQ(wh)) { + /* Has QoS */ + prio = M_WME_GETAC(m0); + which = rsu_wme_ac_xfer_map[prio]; + hasqos = 1; + } else { + /* Non-QoS TID */ + /* XXX TODO: tid=0 for non-qos TID? */ + which = rsu_wme_ac_xfer_map[WME_AC_BE]; + hasqos = 0; + prio = 0; + } + + qid = rsu_ac2qid[prio]; +#if 0 + switch (type) { + case IEEE80211_FC0_TYPE_CTL: + case IEEE80211_FC0_TYPE_MGT: + which = rsu_wme_ac_xfer_map[WME_AC_VO]; + break; + default: + which = rsu_wme_ac_xfer_map[M_WME_GETAC(m0)]; + break; + } + hasqos = 0; +#endif + + RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: pri=%d, which=%d, hasqos=%d\n", + __func__, + prio, + which, + hasqos); + + /* Fill Tx descriptor. */ + txd = (struct r92s_tx_desc *)data->buf; + memset(txd, 0, sizeof(*txd)); + + txd->txdw0 |= htole32( + SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) | + SM(R92S_TXDW0_OFFSET, sizeof(*txd)) | + R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG); + + txd->txdw1 |= htole32( + SM(R92S_TXDW1_MACID, R92S_MACID_BSS) | SM(R92S_TXDW1_QSEL, qid)); + if (!hasqos) + txd->txdw1 |= htole32(R92S_TXDW1_NONQOS); + if (k != NULL && !(k->wk_flags & IEEE80211_KEY_SWENCRYPT)) { + switch (k->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_WEP: + cipher = R92S_TXDW1_CIPHER_WEP; + break; + case IEEE80211_CIPHER_TKIP: + cipher = R92S_TXDW1_CIPHER_TKIP; + break; + case IEEE80211_CIPHER_AES_CCM: + cipher = R92S_TXDW1_CIPHER_AES; + break; + default: + cipher = R92S_TXDW1_CIPHER_NONE; + } + txd->txdw1 |= htole32( + SM(R92S_TXDW1_CIPHER, cipher) | + SM(R92S_TXDW1_KEYIDX, k->wk_keyix)); + } + /* XXX todo: set AGGEN bit if appropriate? */ + txd->txdw2 |= htole32(R92S_TXDW2_BK); + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + txd->txdw2 |= htole32(R92S_TXDW2_BMCAST); + /* + * Firmware will use and increment the sequence number for the + * specified priority. + */ + txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, prio)); + + if (ieee80211_radiotap_active_vap(vap)) { + struct rsu_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); + ieee80211_radiotap_tx(vap, m0); + } + + xferlen = sizeof(*txd) + m0->m_pkthdr.len; + m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]); + + data->buflen = xferlen; + data->ni = ni; + data->m = m0; + STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); + + /* start transfer, if any */ + usbd_transfer_start(sc->sc_xfer[which]); + return (0); +} + +static int +rsu_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct rsu_softc *sc = ic->ic_softc; + int error; + + RSU_LOCK(sc); + if (!sc->sc_running) { + RSU_UNLOCK(sc); + return (ENXIO); + } + + /* + * XXX TODO: ensure that we treat 'm' as a list of frames + * to transmit! + */ + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RSU_DPRINTF(sc, RSU_DEBUG_TX, + "%s: mbufq_enable: failed (%d)\n", + __func__, + error); + RSU_UNLOCK(sc); + return (error); + } + RSU_UNLOCK(sc); + + /* This kicks the TX taskqueue */ + rsu_start(sc); + + return (0); +} + +static void +rsu_drain_mbufq(struct rsu_softc *sc) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + RSU_ASSERT_LOCKED(sc); + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + ieee80211_free_node(ni); + m_freem(m); + } +} + +static void +_rsu_start(struct rsu_softc *sc) +{ + struct ieee80211_node *ni; + struct rsu_data *bf; + struct mbuf *m; + + RSU_ASSERT_LOCKED(sc); + + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + bf = rsu_getbuf(sc); + if (bf == NULL) { + RSU_DPRINTF(sc, RSU_DEBUG_TX, + "%s: failed to get buffer\n", __func__); + mbufq_prepend(&sc->sc_snd, m); + break; + } + + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + + if (rsu_tx_start(sc, ni, m, bf) != 0) { + RSU_DPRINTF(sc, RSU_DEBUG_TX, + "%s: failed to transmit\n", __func__); + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + rsu_freebuf(sc, bf); + ieee80211_free_node(ni); + m_freem(m); + break; + } + } +} + +static void +rsu_start(struct rsu_softc *sc) +{ + + taskqueue_enqueue(taskqueue_thread, &sc->tx_task); +} + +static int +rsu_ioctl_net(struct ieee80211com *ic, u_long cmd, void *data) +{ + struct rsu_softc *sc = ic->ic_softc; + struct ifreq *ifr = (struct ifreq *)data; + int error; + + error = 0; + switch (cmd) { + case SIOCSIFCAP: + { + struct ieee80211vap *vap; + int rxmask; + + rxmask = ifr->ifr_reqcap & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6); + + RSU_LOCK(sc); + /* Both RXCSUM bits must be set (or unset). */ + if (sc->sc_rx_checksum_enable && + rxmask != (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { + rxmask = 0; + sc->sc_rx_checksum_enable = 0; + rsu_rxfilter_set(sc, R92S_RCR_TCP_OFFLD_EN, 0); + } else if (!sc->sc_rx_checksum_enable && rxmask != 0) { + rxmask = IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; + sc->sc_rx_checksum_enable = 1; + rsu_rxfilter_set(sc, 0, R92S_RCR_TCP_OFFLD_EN); + } else { + /* Nothing to do. */ + RSU_UNLOCK(sc); + break; + } + RSU_UNLOCK(sc); + + IEEE80211_LOCK(ic); /* XXX */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ifnet *ifp = vap->iv_ifp; + + ifp->if_capenable &= + ~(IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6); + ifp->if_capenable |= rxmask; + } + IEEE80211_UNLOCK(ic); + break; + } + default: + error = ENOTTY; /* for net80211 */ + break; + } + + return (error); +} + +static void +rsu_parent(struct ieee80211com *ic) +{ + struct rsu_softc *sc = ic->ic_softc; + + if (ic->ic_nrunning > 0) { + if (rsu_init(sc) == 0) + ieee80211_start_all(ic); + else { + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + if (vap != NULL) + ieee80211_stop(vap); + } + } else + rsu_stop(sc); +} + +/* + * Power on sequence for A-cut adapters. + */ +static void +rsu_power_on_acut(struct rsu_softc *sc) +{ + uint32_t reg; + + rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53); + rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57); + + /* Enable AFE macro block's bandgap and Mbias. */ + rsu_write_1(sc, R92S_AFE_MISC, + rsu_read_1(sc, R92S_AFE_MISC) | + R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN); + /* Enable LDOA15 block. */ + rsu_write_1(sc, R92S_LDOA15_CTRL, + rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN); + + rsu_write_1(sc, R92S_SPS1_CTRL, + rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN); + rsu_ms_delay(sc, 2000); + /* Enable switch regulator block. */ + rsu_write_1(sc, R92S_SPS1_CTRL, + rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN); + + rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267); + + rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, + rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08); + + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, + rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20); + + rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, + rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90); + + /* Enable AFE clock. */ + rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1, + rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04); + /* Enable AFE PLL macro block. */ + rsu_write_1(sc, R92S_AFE_PLL_CTRL, + rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11); + /* Attach AFE PLL to MACTOP/BB. */ + rsu_write_1(sc, R92S_SYS_ISO_CTRL, + rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11); + + /* Switch to 40MHz clock instead of 80MHz. */ + rsu_write_2(sc, R92S_SYS_CLKR, + rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL); + + /* Enable MAC clock. */ + rsu_write_2(sc, R92S_SYS_CLKR, + rsu_read_2(sc, R92S_SYS_CLKR) | + R92S_MAC_CLK_EN | R92S_SYS_CLK_EN); + + rsu_write_1(sc, R92S_PMC_FSM, 0x02); + + /* Enable digital core and IOREG R/W. */ + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, + rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08); + + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, + rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80); + + /* Switch the control path to firmware. */ + reg = rsu_read_2(sc, R92S_SYS_CLKR); + reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL; + rsu_write_2(sc, R92S_SYS_CLKR, reg); + + rsu_write_2(sc, R92S_CR, 0x37fc); + + /* Fix USB RX FIFO issue. */ + rsu_write_1(sc, 0xfe5c, + rsu_read_1(sc, 0xfe5c) | 0x80); + rsu_write_1(sc, 0x00ab, + rsu_read_1(sc, 0x00ab) | 0xc0); + + rsu_write_1(sc, R92S_SYS_CLKR, + rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL); +} + +/* + * Power on sequence for B-cut and C-cut adapters. + */ +static void +rsu_power_on_bcut(struct rsu_softc *sc) +{ + uint32_t reg; + int ntries; + + /* Prevent eFuse leakage. */ + rsu_write_1(sc, 0x37, 0xb0); + rsu_ms_delay(sc, 10); + rsu_write_1(sc, 0x37, 0x30); + + /* Switch the control path to hardware. */ + reg = rsu_read_2(sc, R92S_SYS_CLKR); + if (reg & R92S_FWHW_SEL) { + rsu_write_2(sc, R92S_SYS_CLKR, + reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL)); + } + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, + rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c); + rsu_ms_delay(sc, 1); + + rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53); + rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57); + + reg = rsu_read_1(sc, R92S_AFE_MISC); + rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN); + rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN | + R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN); + + /* Enable PLL. */ + rsu_write_1(sc, R92S_LDOA15_CTRL, + rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN); + + rsu_write_1(sc, R92S_LDOV12D_CTRL, + rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN); + + rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, + rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08); + + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, + rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20); + + /* Support 64KB IMEM. */ + rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, + rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97); + + /* Enable AFE clock. */ + rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1, + rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04); + /* Enable AFE PLL macro block. */ + reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL); + rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11); + rsu_ms_delay(sc, 1); + rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51); + rsu_ms_delay(sc, 1); + rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11); + rsu_ms_delay(sc, 1); + + /* Attach AFE PLL to MACTOP/BB. */ + rsu_write_1(sc, R92S_SYS_ISO_CTRL, + rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11); + + /* Switch to 40MHz clock. */ + rsu_write_1(sc, R92S_SYS_CLKR, 0x00); + /* Disable CPU clock and 80MHz SSC. */ + rsu_write_1(sc, R92S_SYS_CLKR, + rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0); + /* Enable MAC clock. */ + rsu_write_2(sc, R92S_SYS_CLKR, + rsu_read_2(sc, R92S_SYS_CLKR) | + R92S_MAC_CLK_EN | R92S_SYS_CLK_EN); + + rsu_write_1(sc, R92S_PMC_FSM, 0x02); + + /* Enable digital core and IOREG R/W. */ + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, + rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08); + + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, + rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80); + + /* Switch the control path to firmware. */ + reg = rsu_read_2(sc, R92S_SYS_CLKR); + reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL; + rsu_write_2(sc, R92S_SYS_CLKR, reg); + + rsu_write_2(sc, R92S_CR, 0x37fc); + + /* Fix USB RX FIFO issue. */ + rsu_write_1(sc, 0xfe5c, + rsu_read_1(sc, 0xfe5c) | 0x80); + + rsu_write_1(sc, R92S_SYS_CLKR, + rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL); + + rsu_write_1(sc, 0xfe1c, 0x80); + + /* Make sure TxDMA is ready to download firmware. */ + for (ntries = 0; ntries < 20; ntries++) { + reg = rsu_read_1(sc, R92S_TCR); + if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) == + (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) + break; + rsu_ms_delay(sc, 1); + } + if (ntries == 20) { + RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_TX, + "%s: TxDMA is not ready\n", + __func__); + /* Reset TxDMA. */ + reg = rsu_read_1(sc, R92S_CR); + rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN); + rsu_ms_delay(sc, 1); + rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN); + } +} + +static void +rsu_power_off(struct rsu_softc *sc) +{ + /* Turn RF off. */ + rsu_write_1(sc, R92S_RF_CTRL, 0x00); + rsu_ms_delay(sc, 5); + + /* Turn MAC off. */ + /* Switch control path. */ + rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38); + /* Reset MACTOP. */ + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70); + rsu_write_1(sc, R92S_PMC_FSM, 0x06); + rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9); + rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8); + + /* Disable AFE PLL. */ + rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00); + /* Disable A15V. */ + rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54); + /* Disable eFuse 1.2V. */ + rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50); + rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24); + /* Enable AFE macro block's bandgap and Mbias. */ + rsu_write_1(sc, R92S_AFE_MISC, 0x30); + /* Disable 1.6V LDO. */ + rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56); + rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43); + + /* Firmware - tell it to switch things off */ + (void) rsu_set_fw_power_state(sc, RSU_PWR_OFF); +} + +static int +rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len) +{ + const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO]; + struct rsu_data *data; + struct r92s_tx_desc *txd; + int mlen; + + while (len > 0) { + data = rsu_getbuf(sc); + if (data == NULL) + return (ENOMEM); + txd = (struct r92s_tx_desc *)data->buf; + memset(txd, 0, sizeof(*txd)); + if (len <= RSU_TXBUFSZ - sizeof(*txd)) { + /* Last chunk. */ + txd->txdw0 |= htole32(R92S_TXDW0_LINIP); + mlen = len; + } else + mlen = RSU_TXBUFSZ - sizeof(*txd); + txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen)); + memcpy(&txd[1], buf, mlen); + data->buflen = sizeof(*txd) + mlen; + RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FW | RSU_DEBUG_RESET, + "%s: starting transfer %p\n", + __func__, data); + STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); + buf += mlen; + len -= mlen; + } + usbd_transfer_start(sc->sc_xfer[which]); + return (0); +} + +static int +rsu_load_firmware(struct rsu_softc *sc) +{ + const struct r92s_fw_hdr *hdr; + struct r92s_fw_priv *dmem; + struct ieee80211com *ic = &sc->sc_ic; + const uint8_t *imem, *emem; + int imemsz, ememsz; + const struct firmware *fw; + size_t size; + uint32_t reg; + int ntries, error; + + if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY) { + RSU_DPRINTF(sc, RSU_DEBUG_ANY, + "%s: Firmware already loaded\n", + __func__); + return (0); + } + + RSU_UNLOCK(sc); + /* Read firmware image from the filesystem. */ + if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) { + device_printf(sc->sc_dev, + "%s: failed load firmware of file rsu-rtl8712fw\n", + __func__); + RSU_LOCK(sc); + return (ENXIO); + } + RSU_LOCK(sc); + size = fw->datasize; + if (size < sizeof(*hdr)) { + device_printf(sc->sc_dev, "firmware too short\n"); + error = EINVAL; + goto fail; + } + hdr = (const struct r92s_fw_hdr *)fw->data; + if (hdr->signature != htole16(0x8712) && + hdr->signature != htole16(0x8192)) { + device_printf(sc->sc_dev, + "invalid firmware signature 0x%x\n", + le16toh(hdr->signature)); + error = EINVAL; + goto fail; + } + RSU_DPRINTF(sc, RSU_DEBUG_FW, "FW V%d %02x-%02x %02x:%02x\n", + le16toh(hdr->version), hdr->month, hdr->day, hdr->hour, + hdr->minute); + + /* Make sure that driver and firmware are in sync. */ + if (hdr->privsz != htole32(sizeof(*dmem))) { + device_printf(sc->sc_dev, "unsupported firmware image\n"); + error = EINVAL; + goto fail; + } + /* Get FW sections sizes. */ + imemsz = le32toh(hdr->imemsz); + ememsz = le32toh(hdr->sramsz); + /* Check that all FW sections fit in image. */ + if (size < sizeof(*hdr) + imemsz + ememsz) { + device_printf(sc->sc_dev, "firmware too short\n"); + error = EINVAL; + goto fail; + } + imem = (const uint8_t *)&hdr[1]; + emem = imem + imemsz; + + /* Load IMEM section. */ + error = rsu_fw_loadsection(sc, imem, imemsz); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load firmware section %s\n", "IMEM"); + goto fail; + } + /* Wait for load to complete. */ + for (ntries = 0; ntries != 50; ntries++) { + rsu_ms_delay(sc, 10); + reg = rsu_read_1(sc, R92S_TCR); + if (reg & R92S_TCR_IMEM_CODE_DONE) + break; + } + if (ntries == 50) { + device_printf(sc->sc_dev, "timeout waiting for IMEM transfer\n"); + error = ETIMEDOUT; + goto fail; + } + /* Load EMEM section. */ + error = rsu_fw_loadsection(sc, emem, ememsz); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load firmware section %s\n", "EMEM"); + goto fail; + } + /* Wait for load to complete. */ + for (ntries = 0; ntries != 50; ntries++) { + rsu_ms_delay(sc, 10); + reg = rsu_read_2(sc, R92S_TCR); + if (reg & R92S_TCR_EMEM_CODE_DONE) + break; + } + if (ntries == 50) { + device_printf(sc->sc_dev, "timeout waiting for EMEM transfer\n"); + error = ETIMEDOUT; + goto fail; + } + /* Enable CPU. */ + rsu_write_1(sc, R92S_SYS_CLKR, + rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL); + if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) { + device_printf(sc->sc_dev, "could not enable system clock\n"); + error = EIO; + goto fail; + } + rsu_write_2(sc, R92S_SYS_FUNC_EN, + rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN); + if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) { + device_printf(sc->sc_dev, + "could not enable microcontroller\n"); + error = EIO; + goto fail; + } + /* Wait for CPU to initialize. */ + for (ntries = 0; ntries < 100; ntries++) { + if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_IMEM_RDY) + break; + rsu_ms_delay(sc, 1); + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for microcontroller\n"); + error = ETIMEDOUT; + goto fail; + } + + /* Update DMEM section before loading. */ + dmem = __DECONST(struct r92s_fw_priv *, &hdr->priv); + memset(dmem, 0, sizeof(*dmem)); + dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172; + dmem->nendpoints = sc->sc_nendpoints; + dmem->chip_version = sc->cut; + dmem->rf_config = sc->sc_rftype; + dmem->vcs_type = R92S_VCS_TYPE_AUTO; + dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS; + dmem->turbo_mode = 0; + dmem->bw40_en = !! (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40); + dmem->amsdu2ampdu_en = !! (sc->sc_ht); + dmem->ampdu_en = !! (sc->sc_ht); + dmem->agg_offload = !! (sc->sc_ht); + dmem->qos_en = 1; + dmem->ps_offload = 1; + dmem->lowpower_mode = 1; /* XXX TODO: configurable? */ + /* Load DMEM section. */ + error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem)); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load firmware section %s\n", "DMEM"); + goto fail; + } + /* Wait for load to complete. */ + for (ntries = 0; ntries < 100; ntries++) { + if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE) + break; + rsu_ms_delay(sc, 1); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for %s transfer\n", + "DMEM"); + error = ETIMEDOUT; + goto fail; + } + /* Wait for firmware readiness. */ + for (ntries = 0; ntries < 60; ntries++) { + if (!(rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY)) + break; + rsu_ms_delay(sc, 1); + } + if (ntries == 60) { + device_printf(sc->sc_dev, + "timeout waiting for firmware readiness\n"); + error = ETIMEDOUT; + goto fail; + } + fail: + firmware_put(fw, FIRMWARE_UNLOAD); + return (error); +} + + +static int +rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct rsu_softc *sc = ic->ic_softc; + struct rsu_data *bf; + + /* prevent management frames from being sent if we're not ready */ + if (!sc->sc_running) { + m_freem(m); + return (ENETDOWN); + } + RSU_LOCK(sc); + bf = rsu_getbuf(sc); + if (bf == NULL) { + m_freem(m); + RSU_UNLOCK(sc); + return (ENOBUFS); + } + if (rsu_tx_start(sc, ni, m, bf) != 0) { + m_freem(m); + rsu_freebuf(sc, bf); + RSU_UNLOCK(sc); + return (EIO); + } + RSU_UNLOCK(sc); + + return (0); +} + +static void +rsu_rxfilter_init(struct rsu_softc *sc) +{ + uint32_t reg; + + RSU_ASSERT_LOCKED(sc); + + /* Setup multicast filter. */ + rsu_set_multi(sc); + + /* Adjust Rx filter. */ + reg = rsu_read_4(sc, R92S_RCR); + reg &= ~R92S_RCR_AICV; + reg |= R92S_RCR_APP_PHYSTS; + if (sc->sc_rx_checksum_enable) + reg |= R92S_RCR_TCP_OFFLD_EN; + rsu_write_4(sc, R92S_RCR, reg); + + /* Update dynamic Rx filter parts. */ + rsu_rxfilter_refresh(sc); +} + +static void +rsu_rxfilter_set(struct rsu_softc *sc, uint32_t clear, uint32_t set) +{ + /* NB: firmware can touch this register too. */ + rsu_write_4(sc, R92S_RCR, + (rsu_read_4(sc, R92S_RCR) & ~clear) | set); +} + +static void +rsu_rxfilter_refresh(struct rsu_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t mask_all, mask_min; + + RSU_ASSERT_LOCKED(sc); + + /* NB: RCR_AMF / RXFLTMAP_MGT are used by firmware. */ + mask_all = R92S_RCR_ACF | R92S_RCR_AAP; + mask_min = R92S_RCR_APM; + if (sc->sc_vap_is_running) + mask_min |= R92S_RCR_CBSSID; + else + mask_all |= R92S_RCR_ADF; + + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + uint16_t rxfltmap; + if (sc->sc_vap_is_running) + rxfltmap = 0; + else + rxfltmap = R92S_RXFLTMAP_MGT_DEF; + rsu_write_2(sc, R92S_RXFLTMAP_MGT, rxfltmap); + } + + if (ic->ic_promisc == 0 && ic->ic_opmode != IEEE80211_M_MONITOR) + rsu_rxfilter_set(sc, mask_all, mask_min); + else + rsu_rxfilter_set(sc, mask_min, mask_all); +} + +static int +rsu_init(struct rsu_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint8_t macaddr[IEEE80211_ADDR_LEN]; + int error; + int i; + + RSU_LOCK(sc); + + if (sc->sc_running) { + RSU_UNLOCK(sc); + return (0); + } + + /* Ensure the mbuf queue is drained */ + rsu_drain_mbufq(sc); + + /* Reset power management state. */ + rsu_write_1(sc, R92S_USB_HRPWM, 0); + + /* Power on adapter. */ + if (sc->cut == 1) + rsu_power_on_acut(sc); + else + rsu_power_on_bcut(sc); + + /* Load firmware. */ + error = rsu_load_firmware(sc); + if (error != 0) + goto fail; + + rsu_write_4(sc, R92S_CR, + rsu_read_4(sc, R92S_CR) & ~0xff000000); + + /* Use 128 bytes pages. */ + rsu_write_1(sc, 0x00b5, + rsu_read_1(sc, 0x00b5) | 0x01); + /* Enable USB Rx aggregation. */ + rsu_write_1(sc, 0x00bd, + rsu_read_1(sc, 0x00bd) | 0x80); + /* Set USB Rx aggregation threshold. */ + rsu_write_1(sc, 0x00d9, 0x01); + /* Set USB Rx aggregation timeout (1.7ms/4). */ + rsu_write_1(sc, 0xfe5b, 0x04); + /* Fix USB Rx FIFO issue. */ + rsu_write_1(sc, 0xfe5c, + rsu_read_1(sc, 0xfe5c) | 0x80); + + /* Set MAC address. */ + IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr); + rsu_write_region_1(sc, R92S_MACID, macaddr, IEEE80211_ADDR_LEN); + + /* It really takes 1.5 seconds for the firmware to boot: */ + usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(2000)); + + RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting MAC address to %s\n", + __func__, + ether_sprintf(macaddr)); + error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, macaddr, + IEEE80211_ADDR_LEN); + if (error != 0) { + device_printf(sc->sc_dev, "could not set MAC address\n"); + goto fail; + } + + /* Initialize Rx filter. */ + rsu_rxfilter_init(sc); + + /* Set PS mode fully active */ + error = rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); + if (error != 0) { + device_printf(sc->sc_dev, "could not set PS mode\n"); + goto fail; + } + + /* Install static keys (if any). */ + error = rsu_reinit_static_keys(sc); + if (error != 0) + goto fail; + + sc->sc_extra_scan = 0; + usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]); + + /* We're ready to go. */ + sc->sc_running = 1; + RSU_UNLOCK(sc); + + return (0); +fail: + /* Need to stop all failed transfers, if any */ + for (i = 0; i != RSU_N_TRANSFER; i++) + usbd_transfer_stop(sc->sc_xfer[i]); + RSU_UNLOCK(sc); + + return (error); +} + +static void +rsu_stop(struct rsu_softc *sc) +{ + int i; + + RSU_LOCK(sc); + if (!sc->sc_running) { + RSU_UNLOCK(sc); + return; + } + + sc->sc_running = 0; + sc->sc_vap_is_running = 0; + sc->sc_calibrating = 0; + taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL); + taskqueue_cancel(taskqueue_thread, &sc->tx_task, NULL); + + /* Power off adapter. */ + rsu_power_off(sc); + + /* + * CAM is not accessible after shutdown; + * all entries are marked (by firmware?) as invalid. + */ + memset(sc->free_keys_bmap, 0, sizeof(sc->free_keys_bmap)); + memset(sc->keys_bmap, 0, sizeof(sc->keys_bmap)); + + for (i = 0; i < RSU_N_TRANSFER; i++) + usbd_transfer_stop(sc->sc_xfer[i]); + + /* Ensure the mbuf queue is drained */ + rsu_drain_mbufq(sc); + RSU_UNLOCK(sc); +} + +/* + * Note: usb_pause_mtx() actually releases the mutex before calling pause(), + * which breaks any kind of driver serialisation. + */ +static void +rsu_ms_delay(struct rsu_softc *sc, int ms) +{ + + //usb_pause_mtx(&sc->sc_mtx, hz / 1000); + DELAY(ms * 1000); +} diff --git a/freebsd/sys/dev/usb/wlan/if_rsureg.h b/freebsd/sys/dev/usb/wlan/if_rsureg.h new file mode 100644 index 00000000..973280cf --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_rsureg.h @@ -0,0 +1,895 @@ +/*- + * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $OpenBSD: if_rsureg.h,v 1.3 2013/04/15 09:23:01 mglocker Exp $ + * $FreeBSD$ + */ + +/* USB Requests. */ +#define R92S_REQ_REGS 0x05 + +/* + * MAC registers. + */ +#define R92S_SYSCFG 0x0000 +#define R92S_SYS_ISO_CTRL (R92S_SYSCFG + 0x000) +#define R92S_SYS_FUNC_EN (R92S_SYSCFG + 0x002) +#define R92S_PMC_FSM (R92S_SYSCFG + 0x004) +#define R92S_SYS_CLKR (R92S_SYSCFG + 0x008) +#define R92S_EE_9346CR (R92S_SYSCFG + 0x00a) +#define R92S_AFE_MISC (R92S_SYSCFG + 0x010) +#define R92S_SPS0_CTRL (R92S_SYSCFG + 0x011) +#define R92S_SPS1_CTRL (R92S_SYSCFG + 0x018) +#define R92S_RF_CTRL (R92S_SYSCFG + 0x01f) +#define R92S_LDOA15_CTRL (R92S_SYSCFG + 0x020) +#define R92S_LDOV12D_CTRL (R92S_SYSCFG + 0x021) +#define R92S_AFE_XTAL_CTRL (R92S_SYSCFG + 0x026) +#define R92S_AFE_PLL_CTRL (R92S_SYSCFG + 0x028) +#define R92S_EFUSE_CTRL (R92S_SYSCFG + 0x030) +#define R92S_EFUSE_TEST (R92S_SYSCFG + 0x034) +#define R92S_EFUSE_CLK_CTRL (R92S_SYSCFG + 0x2f8) + +#define R92S_CMDCTRL 0x0040 +#define R92S_CR (R92S_CMDCTRL + 0x000) +#define R92S_TXPAUSE (R92S_CMDCTRL + 0x002) +#define R92S_TCR (R92S_CMDCTRL + 0x004) +#define R92S_RCR (R92S_CMDCTRL + 0x008) + +#define R92S_MACIDSETTING 0x0050 +#define R92S_MACID (R92S_MACIDSETTING + 0x000) +#define R92S_MAR (R92S_MACIDSETTING + 0x010) + +#define R92S_TIMECTRL 0x0080 +#define R92S_TSFTR (R92S_TIMECTRL + 0x000) + +#define R92S_FIFOCTRL 0x00a0 +#define R92S_RXFLTMAP_MGT (R92S_FIFOCTRL + 0x076) +#define R92S_RXFLTMAP_CTL (R92S_FIFOCTRL + 0x078) +#define R92S_RXFLTMAP_DATA (R92S_FIFOCTRL + 0x07a) +#define R92S_RXFLTMAP_MESH (R92S_FIFOCTRL + 0x07c) + +#define R92S_SECURITY 0x0240 +#define R92S_CAMCMD (R92S_SECURITY + 0x000) +#define R92S_CAMWRITE (R92S_SECURITY + 0x004) +#define R92S_CAMREAD (R92S_SECURITY + 0x008) + +#define R92S_GP 0x02e0 +#define R92S_GPIO_CTRL (R92S_GP + 0x00c) +#define R92S_GPIO_IO_SEL (R92S_GP + 0x00e) +#define R92S_MAC_PINMUX_CTRL (R92S_GP + 0x011) +#define R92S_LEDCFG (R92S_GP + 0x012) + +#define R92S_IOCMD_CTRL 0x0370 +#define R92S_IOCMD_DATA 0x0374 + +#define R92S_USB_HRPWM 0xfe58 + +/* Bits for R92S_SYS_FUNC_EN. */ +#define R92S_FEN_CPUEN 0x0400 + +/* Bits for R92S_PMC_FSM. */ +#define R92S_PMC_FSM_CUT_M 0x000f8000 +#define R92S_PMC_FSM_CUT_S 15 + +/* Bits for R92S_SYS_CLKR. */ +#define R92S_SYS_CLKSEL 0x0001 +#define R92S_SYS_PS_CLKSEL 0x0002 +#define R92S_SYS_CPU_CLKSEL 0x0004 +#define R92S_MAC_CLK_EN 0x0800 +#define R92S_SYS_CLK_EN 0x1000 +#define R92S_SWHW_SEL 0x4000 +#define R92S_FWHW_SEL 0x8000 + +/* Bits for R92S_EE_9346CR. */ +#define R92S_9356SEL 0x10 +#define R92S_EEPROM_EN 0x20 + +/* Bits for R92S_AFE_MISC. */ +#define R92S_AFE_MISC_BGEN 0x01 +#define R92S_AFE_MISC_MBEN 0x02 +#define R92S_AFE_MISC_I32_EN 0x08 + +/* Bits for R92S_SPS1_CTRL. */ +#define R92S_SPS1_LDEN 0x01 +#define R92S_SPS1_SWEN 0x02 + +/* Bits for R92S_LDOA15_CTRL. */ +#define R92S_LDA15_EN 0x01 + +/* Bits for R92S_LDOV12D_CTRL. */ +#define R92S_LDV12_EN 0x01 + +/* Bits for R92C_EFUSE_CTRL. */ +#define R92S_EFUSE_CTRL_DATA_M 0x000000ff +#define R92S_EFUSE_CTRL_DATA_S 0 +#define R92S_EFUSE_CTRL_ADDR_M 0x0003ff00 +#define R92S_EFUSE_CTRL_ADDR_S 8 +#define R92S_EFUSE_CTRL_VALID 0x80000000 + +/* Bits for R92S_CR. */ +#define R92S_CR_TXDMA_EN 0x10 + +/* Bits for R92S_TXPAUSE. */ +#define R92S_TXPAUSE_VO 0x01 +#define R92S_TXPAUSE_VI 0x02 +#define R92S_TXPAUSE_BE 0x04 +#define R92S_TXPAUSE_BK 0x08 +#define R92S_TXPAUSE_MGT 0x10 +#define R92S_TXPAUSE_HIGH 0x20 +#define R92S_TXPAUSE_HCCA 0x40 + +/* Shortcuts. */ +#define R92S_TXPAUSE_AC \ + (R92S_TXPAUSE_VO | R92S_TXPAUSE_VI | \ + R92S_TXPAUSE_BE | R92S_TXPAUSE_BK) + +#define R92S_TXPAUSE_ALL \ + (R92S_TXPAUSE_AC | R92S_TXPAUSE_MGT | \ + R92S_TXPAUSE_HIGH | R92S_TXPAUSE_HCCA | 0x80) + +/* Bits for R92S_TCR. */ +#define R92S_TCR_IMEM_CODE_DONE 0x01 +#define R92S_TCR_IMEM_CHK_RPT 0x02 +#define R92S_TCR_EMEM_CODE_DONE 0x04 +#define R92S_TCR_EMEM_CHK_RPT 0x08 +#define R92S_TCR_DMEM_CODE_DONE 0x10 +#define R92S_TCR_IMEM_RDY 0x20 +#define R92S_TCR_FWRDY 0x80 + +/* Bits for R92S_RCR. */ +#define R92S_RCR_AAP 0x00000001 +#define R92S_RCR_APM 0x00000002 +#define R92S_RCR_AM 0x00000004 +#define R92S_RCR_AB 0x00000008 +#define R92S_RCR_ACRC32 0x00000020 +#define R92S_RCR_AICV 0x00001000 +#define R92S_RCR_APP_ICV 0x00010000 +#define R92S_RCR_APP_MIC 0x00020000 +#define R92S_RCR_ADF 0x00040000 +#define R92S_RCR_ACF 0x00080000 +#define R92S_RCR_AMF 0x00100000 +#define R92S_RCR_ADD3 0x00200000 +#define R92S_RCR_APWRMGT 0x00400000 +#define R92S_RCR_CBSSID 0x00800000 +#define R92S_RCR_APP_PHYSTS 0x02000000 +#define R92S_RCR_TCP_OFFLD_EN 0x04000000 +#define R92S_RCR_ENMBID 0x08000000 + +/* Bits for R92S_RXFLTMAP*. */ +#define R92S_RXFLTMAP_MGT_DEF 0x3f3f +#define R92S_RXFLTMAP_FW(subtype) \ + (1 << ((subtype) >> IEEE80211_FC0_SUBTYPE_SHIFT)) + +/* Bits for R92S_GPIO_IO_SEL. */ +#define R92S_GPIO_WPS 0x10 + +/* Bits for R92S_MAC_PINMUX_CTRL. */ +#define R92S_GPIOSEL_GPIO_M 0x03 +#define R92S_GPIOSEL_GPIO_S 0 +#define R92S_GPIOSEL_GPIO_JTAG 0 +#define R92S_GPIOSEL_GPIO_PHYDBG 1 +#define R92S_GPIOSEL_GPIO_BT 2 +#define R92S_GPIOSEL_GPIO_WLANDBG 3 +#define R92S_GPIOMUX_EN 0x08 + +/* Bits for R92S_CAMCMD. */ +#define R92S_CAMCMD_ADDR_M 0x000000ff +#define R92S_CAMCMD_ADDR_S 0 +#define R92S_CAMCMD_READ 0x00000000 +#define R92S_CAMCMD_WRITE 0x00010000 +#define R92S_CAMCMD_POLLING 0x80000000 + +/* + * CAM entries. + */ +#define R92S_CAM_ENTRY_LIMIT 32 +#define R92S_CAM_ENTRY_BYTES howmany(R92S_CAM_ENTRY_LIMIT, NBBY) + +#define R92S_CAM_CTL0(entry) ((entry) * 8 + 0) +#define R92S_CAM_CTL1(entry) ((entry) * 8 + 1) +#define R92S_CAM_KEY(entry, i) ((entry) * 8 + 2 + (i)) + +/* Bits for R92S_CAM_CTL0(i). */ +#define R92S_CAM_KEYID_M 0x00000003 +#define R92S_CAM_KEYID_S 0 +#define R92S_CAM_ALGO_M 0x0000001c +#define R92S_CAM_ALGO_S 2 +#define R92S_CAM_VALID 0x00008000 +#define R92S_CAM_MACLO_M 0xffff0000 +#define R92S_CAM_MACLO_S 16 + +/* Bits for R92S_IOCMD_CTRL. */ +#define R92S_IOCMD_CLASS_M 0xff000000 +#define R92S_IOCMD_CLASS_S 24 +#define R92S_IOCMD_CLASS_BB_RF 0xf0 +#define R92S_IOCMD_VALUE_M 0x00ffff00 +#define R92S_IOCMD_VALUE_S 8 +#define R92S_IOCMD_INDEX_M 0x000000ff +#define R92S_IOCMD_INDEX_S 0 +#define R92S_IOCMD_INDEX_BB_READ 0 +#define R92S_IOCMD_INDEX_BB_WRITE 1 +#define R92S_IOCMD_INDEX_RF_READ 2 +#define R92S_IOCMD_INDEX_RF_WRITE 3 + +/* Bits for R92S_USB_HRPWM. */ +#define R92S_USB_HRPWM_PS_ALL_ON 0x04 +#define R92S_USB_HRPWM_PS_ST_ACTIVE 0x08 + +/* + * Macros to access subfields in registers. + */ +/* Mask and Shift (getter). */ +#define MS(val, field) \ + (((val) & field##_M) >> field##_S) + +/* Shift and Mask (setter). */ +#define SM(field, val) \ + (((val) << field##_S) & field##_M) + +/* Rewrite. */ +#define RW(var, field, val) \ + (((var) & ~field##_M) | SM(field, val)) + +/* + * ROM field with RF config. + */ +enum { + RTL8712_RFCONFIG_1T = 0x10, + RTL8712_RFCONFIG_2T = 0x20, + RTL8712_RFCONFIG_1R = 0x01, + RTL8712_RFCONFIG_2R = 0x02, + RTL8712_RFCONFIG_1T1R = 0x11, + RTL8712_RFCONFIG_1T2R = 0x12, + RTL8712_RFCONFIG_TURBO = 0x92, + RTL8712_RFCONFIG_2T2R = 0x22 +}; + +/* + * Firmware image header. + */ +struct r92s_fw_priv { + /* QWORD0 */ + uint16_t signature; + uint8_t hci_sel; +#define R92S_HCI_SEL_PCIE 0x01 +#define R92S_HCI_SEL_USB 0x02 +#define R92S_HCI_SEL_SDIO 0x04 +#define R92S_HCI_SEL_8172 0x10 +#define R92S_HCI_SEL_AP 0x80 + + uint8_t chip_version; + uint16_t custid; + uint8_t rf_config; +//0x11: 1T1R, 0x12: 1T2R, 0x92: 1T2R turbo, 0x22: 2T2R + uint8_t nendpoints; + /* QWORD1 */ + uint32_t regulatory; + uint8_t rfintfs; + uint8_t def_nettype; + uint8_t turbo_mode; + uint8_t lowpower_mode; + /* QWORD2 */ + uint8_t lbk_mode; + uint8_t mp_mode; + uint8_t vcs_type; +#define R92S_VCS_TYPE_DISABLE 0 +#define R92S_VCS_TYPE_ENABLE 1 +#define R92S_VCS_TYPE_AUTO 2 + + uint8_t vcs_mode; +#define R92S_VCS_MODE_NONE 0 +#define R92S_VCS_MODE_RTS_CTS 1 +#define R92S_VCS_MODE_CTS2SELF 2 + + uint32_t reserved1; + /* QWORD3 */ + uint8_t qos_en; + uint8_t bw40_en; + uint8_t amsdu2ampdu_en; + uint8_t ampdu_en; + uint8_t rc_offload; + uint8_t agg_offload; + uint16_t reserved2; + /* QWORD4 */ + uint8_t beacon_offload; + uint8_t mlme_offload; + uint8_t hwpc_offload; + uint8_t tcpcsum_offload; + uint8_t tcp_offload; + uint8_t ps_offload; + uint8_t wwlan_offload; + uint8_t reserved3; + /* QWORD5 */ + uint16_t tcp_tx_len; + uint16_t tcp_rx_len; + uint32_t reserved4; +} __packed; + +struct r92s_fw_hdr { + uint16_t signature; + uint16_t version; + uint32_t dmemsz; + uint32_t imemsz; + uint32_t sramsz; + uint32_t privsz; + uint16_t efuse_addr; + uint16_t h2c_resp_addr; + uint32_t svnrev; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + struct r92s_fw_priv priv; +} __packed; + +/* Structure for FW commands and FW events notifications. */ +struct r92s_fw_cmd_hdr { + uint16_t len; + uint8_t code; + uint8_t seq; +#define R92S_FW_CMD_MORE 0x80 + + uint32_t reserved; +} __packed; + +/* FW commands codes. */ +#define R92S_CMD_READ_MACREG 0 +#define R92S_CMD_WRITE_MACREG 1 +#define R92S_CMD_READ_BBREG 2 +#define R92S_CMD_WRITE_BBREG 3 +#define R92S_CMD_READ_RFREG 4 +#define R92S_CMD_WRITE_RFREG 5 +#define R92S_CMD_READ_EEPROM 6 +#define R92S_CMD_WRITE_EEPROM 7 +#define R92S_CMD_READ_EFUSE 8 +#define R92S_CMD_WRITE_EFUSE 9 +#define R92S_CMD_READ_CAM 10 +#define R92S_CMD_WRITE_CAM 11 +#define R92S_CMD_SET_BCNITV 12 +#define R92S_CMD_SET_MBIDCFG 13 +#define R92S_CMD_JOIN_BSS 14 +#define R92S_CMD_DISCONNECT 15 +#define R92S_CMD_CREATE_BSS 16 +#define R92S_CMD_SET_OPMODE 17 +#define R92S_CMD_SITE_SURVEY 18 +#define R92S_CMD_SET_AUTH 19 +#define R92S_CMD_SET_KEY 20 +#define R92S_CMD_SET_STA_KEY 21 +#define R92S_CMD_SET_ASSOC_STA 22 +#define R92S_CMD_DEL_ASSOC_STA 23 +#define R92S_CMD_SET_STAPWRSTATE 24 +#define R92S_CMD_SET_BASIC_RATE 25 +#define R92S_CMD_GET_BASIC_RATE 26 +#define R92S_CMD_SET_DATA_RATE 27 +#define R92S_CMD_GET_DATA_RATE 28 +#define R92S_CMD_SET_PHY_INFO 29 +#define R92S_CMD_GET_PHY_INFO 30 +#define R92S_CMD_SET_PHY 31 +#define R92S_CMD_GET_PHY 32 +#define R92S_CMD_READ_RSSI 33 +#define R92S_CMD_READ_GAIN 34 +#define R92S_CMD_SET_ATIM 35 +#define R92S_CMD_SET_PWR_MODE 36 +#define R92S_CMD_JOIN_BSS_RPT 37 +#define R92S_CMD_SET_RA_TABLE 38 +#define R92S_CMD_GET_RA_TABLE 39 +#define R92S_CMD_GET_CCX_REPORT 40 +#define R92S_CMD_GET_DTM_REPORT 41 +#define R92S_CMD_GET_TXRATE_STATS 42 +#define R92S_CMD_SET_USB_SUSPEND 43 +#define R92S_CMD_SET_H2C_LBK 44 +#define R92S_CMD_ADDBA_REQ 45 +#define R92S_CMD_SET_CHANNEL 46 +#define R92S_CMD_SET_TXPOWER 47 +#define R92S_CMD_SWITCH_ANTENNA 48 +#define R92S_CMD_SET_CRYSTAL_CAL 49 +#define R92S_CMD_SET_SINGLE_CARRIER_TX 50 +#define R92S_CMD_SET_SINGLE_TONE_TX 51 +#define R92S_CMD_SET_CARRIER_SUPPR_TX 52 +#define R92S_CMD_SET_CONTINUOUS_TX 53 +#define R92S_CMD_SWITCH_BANDWIDTH 54 +#define R92S_CMD_TX_BEACON 55 +#define R92S_CMD_SET_POWER_TRACKING 56 +#define R92S_CMD_AMSDU_TO_AMPDU 57 +#define R92S_CMD_SET_MAC_ADDRESS 58 +#define R92S_CMD_GET_H2C_LBK 59 +#define R92S_CMD_SET_PBREQ_IE 60 +#define R92S_CMD_SET_ASSOCREQ_IE 61 +#define R92S_CMD_SET_PBRESP_IE 62 +#define R92S_CMD_SET_ASSOCRESP_IE 63 +#define R92S_CMD_GET_CURDATARATE 64 +#define R92S_CMD_GET_TXRETRY_CNT 65 +#define R92S_CMD_GET_RXRETRY_CNT 66 +#define R92S_CMD_GET_BCNOK_CNT 67 +#define R92S_CMD_GET_BCNERR_CNT 68 +#define R92S_CMD_GET_CURTXPWR_LEVEL 69 +#define R92S_CMD_SET_DIG 70 +#define R92S_CMD_SET_RA 71 +#define R92S_CMD_SET_PT 72 +#define R92S_CMD_READ_TSSI 73 + +/* FW events notifications codes. */ +#define R92S_EVT_READ_MACREG 0 +#define R92S_EVT_READ_BBREG 1 +#define R92S_EVT_READ_RFREG 2 +#define R92S_EVT_READ_EEPROM 3 +#define R92S_EVT_READ_EFUSE 4 +#define R92S_EVT_READ_CAM 5 +#define R92S_EVT_GET_BASICRATE 6 +#define R92S_EVT_GET_DATARATE 7 +#define R92S_EVT_SURVEY 8 +#define R92S_EVT_SURVEY_DONE 9 +#define R92S_EVT_JOIN_BSS 10 +#define R92S_EVT_ADD_STA 11 +#define R92S_EVT_DEL_STA 12 +#define R92S_EVT_ATIM_DONE 13 +#define R92S_EVT_TX_REPORT 14 +#define R92S_EVT_CCX_REPORT 15 +#define R92S_EVT_DTM_REPORT 16 +#define R92S_EVT_TXRATE_STATS 17 +#define R92S_EVT_C2H_LBK 18 +#define R92S_EVT_FWDBG 19 +#define R92S_EVT_C2H_FEEDBACK 20 +#define R92S_EVT_ADDBA 21 +#define R92S_EVT_C2H_BCN 22 +#define R92S_EVT_PWR_STATE 23 +#define R92S_EVT_WPS_PBC 24 +#define R92S_EVT_ADDBA_REQ_REPORT 25 + +/* Structure for R92S_CMD_SITE_SURVEY. */ +struct r92s_fw_cmd_sitesurvey { + uint32_t active; + uint32_t limit; + uint32_t ssidlen; + uint8_t ssid[32 + 1]; +} __packed; + +/* Structure for R92S_CMD_SET_AUTH. */ +struct r92s_fw_cmd_auth { + uint8_t mode; +#define R92S_AUTHMODE_OPEN 0 +#define R92S_AUTHMODE_SHARED 1 +#define R92S_AUTHMODE_WPA 2 + + uint8_t dot1x; +} __packed; + +/* Structure for R92S_CMD_SET_KEY. */ +struct r92s_fw_cmd_set_key { + uint8_t algo; +#define R92S_KEY_ALGO_NONE 0 +#define R92S_KEY_ALGO_WEP40 1 +#define R92S_KEY_ALGO_TKIP 2 +#define R92S_KEY_ALGO_TKIP_MMIC 3 +#define R92S_KEY_ALGO_AES 4 +#define R92S_KEY_ALGO_WEP104 5 +#define R92S_KEY_ALGO_INVALID 0xff /* for rsu_crypto_mode() only */ + + uint8_t cam_id; + uint8_t grpkey; + uint8_t key[IEEE80211_KEYBUF_SIZE]; +} __packed; + +/* Structure for R92S_CMD_SET_STA_KEY. */ +struct r92s_fw_cmd_set_key_mac { + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t algo; + uint8_t key[IEEE80211_KEYBUF_SIZE]; +} __packed; + +/* Structures for R92S_EVENT_SURVEY/R92S_CMD_JOIN_BSS. */ +/* NDIS_802_11_SSID. */ +struct ndis_802_11_ssid { + uint32_t ssidlen; + uint8_t ssid[32]; +} __packed; + +/* NDIS_802_11_CONFIGURATION_FH. */ +struct ndis_802_11_configuration_fh { + uint32_t len; + uint32_t hoppattern; + uint32_t hopset; + uint32_t dwelltime; +} __packed; + +/* NDIS_802_11_CONFIGURATION. */ +struct ndis_802_11_configuration { + uint32_t len; + uint32_t bintval; + uint32_t atim; + uint32_t dsconfig; + struct ndis_802_11_configuration_fh fhconfig; +} __packed; + +/* NDIS_WLAN_BSSID_EX. */ +struct ndis_wlan_bssid_ex { + uint32_t len; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t reserved[2]; + struct ndis_802_11_ssid ssid; + uint32_t privacy; + int32_t rssi; + uint32_t networktype; +#define NDIS802_11FH 0 +#define NDIS802_11DS 1 +#define NDIS802_11OFDM5 2 +#define NDIS802_11OFDM24 3 +#define NDIS802_11AUTOMODE 4 + + struct ndis_802_11_configuration config; + uint32_t inframode; +#define NDIS802_11IBSS 0 +#define NDIS802_11INFRASTRUCTURE 1 +#define NDIS802_11AUTOUNKNOWN 2 +#define NDIS802_11MONITOR 3 +#define NDIS802_11APMODE 4 + + uint8_t supprates[16]; + uint32_t ieslen; + /* Followed by ``ieslen'' bytes. */ +} __packed; + +/* NDIS_802_11_FIXED_IEs. */ +struct ndis_802_11_fixed_ies { + uint8_t tstamp[8]; + uint16_t bintval; + uint16_t capabilities; +} __packed; + +/* Structure for R92S_CMD_SET_PWR_MODE. */ +struct r92s_set_pwr_mode { + uint8_t mode; +#define R92S_PS_MODE_ACTIVE 0 +#define R92S_PS_MODE_MIN 1 +#define R92S_PS_MODE_MAX 2 +#define R92S_PS_MODE_DTIM 3 +#define R92S_PS_MODE_VOIP 4 +#define R92S_PS_MODE_UAPSD_WMM 5 +#define R92S_PS_MODE_UAPSD 6 +#define R92S_PS_MODE_IBSS 7 +#define R92S_PS_MODE_WWLAN 8 +#define R92S_PS_MODE_RADIOOFF 9 +#define R92S_PS_MODE_DISABLE 10 + + uint8_t low_traffic_en; + uint8_t lpnav_en; + uint8_t rf_low_snr_en; + uint8_t dps_en; + uint8_t bcn_rx_en; + uint8_t bcn_pass_cnt; + uint8_t bcn_to; + uint16_t bcn_itv; + uint8_t app_itv; + uint8_t awake_bcn_itv; + uint8_t smart_ps; + uint8_t bcn_pass_time; +} __packed; + +/* Structure for R92S_CMD_SET_CHANNEL. */ +struct r92s_set_channel { + uint32_t channel; +} __packed; + +/* Structure for event R92S_EVENT_JOIN_BSS. */ +struct r92s_event_join_bss { + uint32_t next; + uint32_t prev; + uint32_t networktype; + uint32_t fixed; + uint32_t lastscanned; + uint32_t associd; + uint32_t join_res; + struct ndis_wlan_bssid_ex bss; +} __packed; + +#define R92S_MACID_BSS 5 /* XXX hardcoded somewhere */ + +/* Rx MAC descriptor. */ +struct r92s_rx_stat { + uint32_t rxdw0; +#define R92S_RXDW0_PKTLEN_M 0x00003fff +#define R92S_RXDW0_PKTLEN_S 0 +#define R92S_RXDW0_CRCERR 0x00004000 +#define R92S_RXDW0_ICVERR 0x00008000 +#define R92S_RXDW0_INFOSZ_M 0x000f0000 +#define R92S_RXDW0_INFOSZ_S 16 +#define R92S_RXDW0_CIPHER_M 0x00700000 +#define R92S_RXDW0_CIPHER_S 20 +#define R92S_RXDW0_QOS 0x00800000 +#define R92S_RXDW0_SHIFT_M 0x03000000 +#define R92S_RXDW0_SHIFT_S 24 +#define R92S_RXDW0_PHYST 0x04000000 +#define R92S_RXDW0_DECRYPTED 0x08000000 + + uint32_t rxdw1; +#define R92S_RXDW1_MOREFRAG 0x08000000 + + uint32_t rxdw2; +#define R92S_RXDW2_FRAG_M 0x0000f000 +#define R92S_RXDW2_FRAG_S 12 +#define R92S_RXDW2_PKTCNT_M 0x00ff0000 +#define R92S_RXDW2_PKTCNT_S 16 + + uint32_t rxdw3; +#define R92S_RXDW3_RATE_M 0x0000003f +#define R92S_RXDW3_RATE_S 0 +#define R92S_RXDW3_TCPCHKRPT 0x00000800 +#define R92S_RXDW3_IPCHKRPT 0x00001000 +#define R92S_RXDW3_TCPCHKVALID 0x00002000 +#define R92S_RXDW3_HTC 0x00004000 + + uint32_t rxdw4; + uint32_t tsf_low; +} __packed __aligned(4); + +/* Rx PHY descriptor. */ +struct r92s_rx_phystat { + uint32_t phydw0; + uint32_t phydw1; + uint32_t phydw2; + uint32_t phydw3; + uint32_t phydw4; + uint32_t phydw5; + uint32_t phydw6; + uint32_t phydw7; +} __packed __aligned(4); + +/* Rx PHY CCK descriptor. */ +struct r92s_rx_cck { + uint8_t adc_pwdb[4]; + uint8_t sq_rpt; + uint8_t agc_rpt; +} __packed; + +/* Tx MAC descriptor. */ +struct r92s_tx_desc { + uint32_t txdw0; +#define R92S_TXDW0_PKTLEN_M 0x0000ffff +#define R92S_TXDW0_PKTLEN_S 0 +#define R92S_TXDW0_OFFSET_M 0x00ff0000 +#define R92S_TXDW0_OFFSET_S 16 +#define R92S_TXDW0_TYPE_M 0x03000000 +#define R92S_TXDW0_TYPE_S 24 +#define R92S_TXDW0_LSG 0x04000000 +#define R92S_TXDW0_FSG 0x08000000 +#define R92S_TXDW0_LINIP 0x10000000 +#define R92S_TXDW0_OWN 0x80000000 + + uint32_t txdw1; +#define R92S_TXDW1_MACID_M 0x0000001f +#define R92S_TXDW1_MACID_S 0 +#define R92S_TXDW1_MOREDATA 0x00000020 +#define R92S_TXDW1_MOREFRAG 0x00000040 +#define R92S_TXDW1_QSEL_M 0x00001f00 +#define R92S_TXDW1_QSEL_S 8 +#define R92S_TXDW1_QSEL_BE 0x03 +#define R92S_TXDW1_QSEL_H2C 0x13 +#define R92S_TXDW1_NONQOS 0x00010000 +#define R92S_TXDW1_KEYIDX_M 0x00060000 +#define R92S_TXDW1_KEYIDX_S 17 +#define R92S_TXDW1_CIPHER_M 0x00c00000 +#define R92S_TXDW1_CIPHER_S 22 +#define R92S_TXDW1_CIPHER_NONE 0 +#define R92S_TXDW1_CIPHER_WEP 1 +#define R92S_TXDW1_CIPHER_TKIP 2 +#define R92S_TXDW1_CIPHER_AES 3 +#define R92S_TXDW1_HWPC 0x80000000 + + uint32_t txdw2; +#define R92S_TXDW2_BMCAST 0x00000080 +#define R92S_TXDW2_AGGEN 0x20000000 +#define R92S_TXDW2_BK 0x40000000 + + uint32_t txdw3; +#define R92S_TXDW3_SEQ_M 0x0fff0000 +#define R92S_TXDW3_SEQ_S 16 +#define R92S_TXDW3_FRAG_M 0xf0000000 +#define R92S_TXDW3_FRAG_S 28 + + uint32_t txdw4; +#define R92S_TXDW4_TXBW 0x00040000 + + uint32_t txdw5; +#define R92S_TXDW5_DISFB 0x00008000 + + uint16_t ipchksum; + uint16_t tcpchksum; + + uint16_t txbufsize; + uint16_t reserved1; +} __packed __aligned(4); + +struct r92s_add_ba_event { + uint8_t mac_addr[IEEE80211_ADDR_LEN]; + uint16_t ssn; + uint8_t tid; +}; + +struct r92s_add_ba_req { + uint32_t tid; +}; + +/* + * Driver definitions. + */ +#define RSU_RX_LIST_COUNT 1 +#define RSU_TX_LIST_COUNT 32 + +#define RSU_RXBUFSZ (30 * 1024) +#define RSU_TXBUFSZ \ + ((sizeof(struct r92s_tx_desc) + IEEE80211_MAX_LEN + 3) & ~3) + +#define RSU_TX_TIMEOUT 5000 /* ms */ +#define RSU_CMD_TIMEOUT 2000 /* ms */ + +/* Queue ids (used by soft only). */ +#define RSU_QID_BCN 0 +#define RSU_QID_MGT 1 +#define RSU_QID_BMC 2 +#define RSU_QID_VO 3 +#define RSU_QID_VI 4 +#define RSU_QID_BE 5 +#define RSU_QID_BK 6 +#define RSU_QID_RXOFF 7 +#define RSU_QID_H2C 8 +#define RSU_QID_C2H 9 + +/* Map AC to queue id. */ +static const uint8_t rsu_ac2qid[WME_NUM_AC] = { + RSU_QID_BE, + RSU_QID_BK, + RSU_QID_VI, + RSU_QID_VO +}; + +/* Pipe index to endpoint address mapping. */ +static const uint8_t r92s_epaddr[] = + { 0x83, 0x04, 0x06, 0x0d, + 0x05, 0x07, + 0x89, 0x0a, 0x0b, 0x0c }; + +/* Queue id to pipe index mapping for 4 endpoints configurations. */ +static const uint8_t rsu_qid2idx_4ep[] = + { 3, 3, 3, 1, 1, 2, 2, 0, 3, 0 }; + +/* Queue id to pipe index mapping for 6 endpoints configurations. */ +static const uint8_t rsu_qid2idx_6ep[] = + { 3, 3, 3, 1, 4, 2, 5, 0, 3, 0 }; + +/* Queue id to pipe index mapping for 11 endpoints configurations. */ +static const uint8_t rsu_qid2idx_11ep[] = + { 7, 9, 8, 1, 4, 2, 5, 0, 3, 6 }; + +struct rsu_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint64_t wr_tsft; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_dbm_antsignal; +} __packed __aligned(8); + +#define RSU_RX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_TSFT | \ + 1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL | \ + 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) + +struct rsu_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed __aligned(8); + +#define RSU_TX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_CHANNEL) + +struct rsu_softc; + +enum { + RSU_BULK_RX, + RSU_BULK_TX_BE_BK, /* = WME_AC_BE/BK */ + RSU_BULK_TX_VI_VO, /* = WME_AC_VI/VO */ + RSU_BULK_TX_H2C, /* H2C */ + RSU_N_TRANSFER, +}; + +struct rsu_data { + struct rsu_softc *sc; + uint8_t *buf; + uint16_t buflen; + struct mbuf *m; + struct ieee80211_node *ni; + STAILQ_ENTRY(rsu_data) next; +}; + +struct rsu_vap { + struct ieee80211vap vap; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define RSU_VAP(vap) ((struct rsu_vap *)(vap)) + +#define RSU_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RSU_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RSU_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) + +#define RSU_DELKEY_BMAP_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->free_keys_bmap_mtx, "bmap lock", NULL, MTX_DEF) +#define RSU_DELKEY_BMAP_LOCK(_sc) mtx_lock(&(_sc)->free_keys_bmap_mtx) +#define RSU_DELKEY_BMAP_UNLOCK(_sc) mtx_unlock(&(_sc)->free_keys_bmap_mtx) +#define RSU_DELKEY_BMAP_LOCK_DESTROY(_sc) \ + mtx_destroy(&(_sc)->free_keys_bmap_mtx) + +struct rsu_softc { + struct ieee80211com sc_ic; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + + struct timeout_task calib_task; + struct task tx_task; + struct mtx sc_mtx; + int sc_ht; + int sc_nendpoints; + int sc_curpwrstate; + int sc_currssi; + + u_int sc_running:1, + sc_vap_is_running:1, + sc_rx_checksum_enable:1, + sc_calibrating:1, + sc_active_scan:1, + sc_extra_scan:1; + u_int cut; + uint8_t sc_rftype; + int8_t sc_nrxstream; + int8_t sc_ntxstream; + struct rsu_data sc_rx[RSU_RX_LIST_COUNT]; + struct rsu_data sc_tx[RSU_TX_LIST_COUNT]; + uint8_t cmd_seq; + uint8_t rom[128]; + struct usb_xfer *sc_xfer[RSU_N_TRANSFER]; + + STAILQ_HEAD(, rsu_data) sc_rx_active; + STAILQ_HEAD(, rsu_data) sc_rx_inactive; + STAILQ_HEAD(, rsu_data) sc_tx_active[RSU_N_TRANSFER]; + STAILQ_HEAD(, rsu_data) sc_tx_inactive; + STAILQ_HEAD(, rsu_data) sc_tx_pending[RSU_N_TRANSFER]; + + struct task del_key_task; + uint8_t keys_bmap[R92S_CAM_ENTRY_BYTES]; + const struct ieee80211_key *group_keys[IEEE80211_WEP_NKID]; + + struct mtx free_keys_bmap_mtx; + uint8_t free_keys_bmap[R92S_CAM_ENTRY_BYTES]; + + union { + struct rsu_rx_radiotap_header th; + uint8_t pad[64]; + } sc_rxtapu; +#define sc_rxtap sc_rxtapu.th + + union { + struct rsu_tx_radiotap_header th; + uint8_t pad[64]; + } sc_txtapu; +#define sc_txtap sc_txtapu.th +}; diff --git a/freebsd/sys/dev/usb/wlan/if_rum.c b/freebsd/sys/dev/usb/wlan/if_rum.c new file mode 100644 index 00000000..19155ec2 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_rum.c @@ -0,0 +1,3321 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005-2007 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org> + * Copyright (c) 2007-2008 Hans Petter Selasky <hselasky@FreeBSD.org> + * Copyright (c) 2015 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2501USB/RT2601USB chipset driver + * http://www.ralinktech.com.tw/ + */ + +#include <rtems/bsd/sys/param.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/kdb.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_ratectl.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR rum_debug +#include <dev/usb/usb_debug.h> + +#include <dev/usb/wlan/if_rumreg.h> +#include <dev/usb/wlan/if_rumvar.h> +#include <dev/usb/wlan/if_rumfw.h> + +#ifdef USB_DEBUG +static int rum_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum"); +SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RWTUN, &rum_debug, 0, + "Debug level"); +#endif + +static const STRUCT_USB_HOST_ID rum_devs[] = { +#define RUM_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + RUM_DEV(ABOCOM, HWU54DM), + RUM_DEV(ABOCOM, RT2573_2), + RUM_DEV(ABOCOM, RT2573_3), + RUM_DEV(ABOCOM, RT2573_4), + RUM_DEV(ABOCOM, WUG2700), + RUM_DEV(AMIT, CGWLUSB2GO), + RUM_DEV(ASUS, RT2573_1), + RUM_DEV(ASUS, RT2573_2), + RUM_DEV(BELKIN, F5D7050A), + RUM_DEV(BELKIN, F5D9050V3), + RUM_DEV(CISCOLINKSYS, WUSB54GC), + RUM_DEV(CISCOLINKSYS, WUSB54GR), + RUM_DEV(CONCEPTRONIC2, C54RU2), + RUM_DEV(COREGA, CGWLUSB2GL), + RUM_DEV(COREGA, CGWLUSB2GPX), + RUM_DEV(DICKSMITH, CWD854F), + RUM_DEV(DICKSMITH, RT2573), + RUM_DEV(EDIMAX, EW7318USG), + RUM_DEV(DLINK2, DWLG122C1), + RUM_DEV(DLINK2, WUA1340), + RUM_DEV(DLINK2, DWA111), + RUM_DEV(DLINK2, DWA110), + RUM_DEV(GIGABYTE, GNWB01GS), + RUM_DEV(GIGABYTE, GNWI05GS), + RUM_DEV(GIGASET, RT2573), + RUM_DEV(GOODWAY, RT2573), + RUM_DEV(GUILLEMOT, HWGUSB254LB), + RUM_DEV(GUILLEMOT, HWGUSB254V2AP), + RUM_DEV(HUAWEI3COM, WUB320G), + RUM_DEV(MELCO, G54HP), + RUM_DEV(MELCO, SG54HP), + RUM_DEV(MELCO, SG54HG), + RUM_DEV(MELCO, WLIUCG), + RUM_DEV(MELCO, WLRUCG), + RUM_DEV(MELCO, WLRUCGAOSS), + RUM_DEV(MSI, RT2573_1), + RUM_DEV(MSI, RT2573_2), + RUM_DEV(MSI, RT2573_3), + RUM_DEV(MSI, RT2573_4), + RUM_DEV(NOVATECH, RT2573), + RUM_DEV(PLANEX2, GWUS54HP), + RUM_DEV(PLANEX2, GWUS54MINI2), + RUM_DEV(PLANEX2, GWUSMM), + RUM_DEV(QCOM, RT2573), + RUM_DEV(QCOM, RT2573_2), + RUM_DEV(QCOM, RT2573_3), + RUM_DEV(RALINK, RT2573), + RUM_DEV(RALINK, RT2573_2), + RUM_DEV(RALINK, RT2671), + RUM_DEV(SITECOMEU, WL113R2), + RUM_DEV(SITECOMEU, WL172), + RUM_DEV(SPARKLAN, RT2573), + RUM_DEV(SURECOM, RT2573), +#undef RUM_DEV +}; + +static device_probe_t rum_match; +static device_attach_t rum_attach; +static device_detach_t rum_detach; + +static usb_callback_t rum_bulk_read_callback; +static usb_callback_t rum_bulk_write_callback; + +static usb_error_t rum_do_request(struct rum_softc *sc, + struct usb_device_request *req, void *data); +static usb_error_t rum_do_mcu_request(struct rum_softc *sc, int); +static struct ieee80211vap *rum_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, + int, const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void rum_vap_delete(struct ieee80211vap *); +static void rum_cmdq_cb(void *, int); +static int rum_cmd_sleepable(struct rum_softc *, const void *, + size_t, uint8_t, CMD_FUNC_PROTO); +static void rum_tx_free(struct rum_tx_data *, int); +static void rum_setup_tx_list(struct rum_softc *); +static void rum_reset_tx_list(struct rum_softc *, + struct ieee80211vap *); +static void rum_unsetup_tx_list(struct rum_softc *); +static void rum_beacon_miss(struct ieee80211vap *); +static void rum_sta_recv_mgmt(struct ieee80211_node *, + struct mbuf *, int, + const struct ieee80211_rx_stats *, int, int); +static int rum_set_power_state(struct rum_softc *, int); +static int rum_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static uint8_t rum_crypto_mode(struct rum_softc *, u_int, int); +static void rum_setup_tx_desc(struct rum_softc *, + struct rum_tx_desc *, struct ieee80211_key *, + uint32_t, uint8_t, uint8_t, int, int, int); +static uint32_t rum_tx_crypto_flags(struct rum_softc *, + struct ieee80211_node *, + const struct ieee80211_key *); +static int rum_tx_mgt(struct rum_softc *, struct mbuf *, + struct ieee80211_node *); +static int rum_tx_raw(struct rum_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *); +static int rum_tx_data(struct rum_softc *, struct mbuf *, + struct ieee80211_node *); +static int rum_transmit(struct ieee80211com *, struct mbuf *); +static void rum_start(struct rum_softc *); +static void rum_parent(struct ieee80211com *); +static void rum_eeprom_read(struct rum_softc *, uint16_t, void *, + int); +static uint32_t rum_read(struct rum_softc *, uint16_t); +static void rum_read_multi(struct rum_softc *, uint16_t, void *, + int); +static usb_error_t rum_write(struct rum_softc *, uint16_t, uint32_t); +static usb_error_t rum_write_multi(struct rum_softc *, uint16_t, void *, + size_t); +static usb_error_t rum_setbits(struct rum_softc *, uint16_t, uint32_t); +static usb_error_t rum_clrbits(struct rum_softc *, uint16_t, uint32_t); +static usb_error_t rum_modbits(struct rum_softc *, uint16_t, uint32_t, + uint32_t); +static int rum_bbp_busy(struct rum_softc *); +static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t); +static uint8_t rum_bbp_read(struct rum_softc *, uint8_t); +static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t); +static void rum_select_antenna(struct rum_softc *); +static void rum_enable_mrr(struct rum_softc *); +static void rum_set_txpreamble(struct rum_softc *); +static void rum_set_basicrates(struct rum_softc *); +static void rum_select_band(struct rum_softc *, + struct ieee80211_channel *); +static void rum_set_chan(struct rum_softc *, + struct ieee80211_channel *); +static void rum_set_maxretry(struct rum_softc *, + struct ieee80211vap *); +static int rum_enable_tsf_sync(struct rum_softc *); +static void rum_enable_tsf(struct rum_softc *); +static void rum_abort_tsf_sync(struct rum_softc *); +static void rum_get_tsf(struct rum_softc *, uint64_t *); +static void rum_update_slot_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_update_slot(struct ieee80211com *); +static int rum_wme_update(struct ieee80211com *); +static void rum_set_bssid(struct rum_softc *, const uint8_t *); +static void rum_set_macaddr(struct rum_softc *, const uint8_t *); +static void rum_update_mcast(struct ieee80211com *); +static void rum_update_promisc(struct ieee80211com *); +static void rum_setpromisc(struct rum_softc *); +static const char *rum_get_rf(int); +static void rum_read_eeprom(struct rum_softc *); +static int rum_bbp_wakeup(struct rum_softc *); +static int rum_bbp_init(struct rum_softc *); +static void rum_clr_shkey_regs(struct rum_softc *); +static int rum_init(struct rum_softc *); +static void rum_stop(struct rum_softc *); +static void rum_load_microcode(struct rum_softc *, const uint8_t *, + size_t); +static int rum_set_sleep_time(struct rum_softc *, uint16_t); +static int rum_reset(struct ieee80211vap *, u_long); +static int rum_set_beacon(struct rum_softc *, + struct ieee80211vap *); +static int rum_alloc_beacon(struct rum_softc *, + struct ieee80211vap *); +static void rum_update_beacon_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_update_beacon(struct ieee80211vap *, int); +static int rum_common_key_set(struct rum_softc *, + struct ieee80211_key *, uint16_t); +static void rum_group_key_set_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_group_key_del_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_pair_key_set_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_pair_key_del_cb(struct rum_softc *, + union sec_param *, uint8_t); +static int rum_key_alloc(struct ieee80211vap *, + struct ieee80211_key *, ieee80211_keyix *, + ieee80211_keyix *); +static int rum_key_set(struct ieee80211vap *, + const struct ieee80211_key *); +static int rum_key_delete(struct ieee80211vap *, + const struct ieee80211_key *); +static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void rum_scan_start(struct ieee80211com *); +static void rum_scan_end(struct ieee80211com *); +static void rum_set_channel(struct ieee80211com *); +static void rum_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static int rum_get_rssi(struct rum_softc *, uint8_t); +static void rum_ratectl_start(struct rum_softc *, + struct ieee80211_node *); +static void rum_ratectl_timeout(void *); +static void rum_ratectl_task(void *, int); +static int rum_pause(struct rum_softc *, int); + +static const struct { + uint32_t reg; + uint32_t val; +} rum_def_mac[] = { + { RT2573_TXRX_CSR0, 0x025fb032 }, + { RT2573_TXRX_CSR1, 0x9eaa9eaf }, + { RT2573_TXRX_CSR2, 0x8a8b8c8d }, + { RT2573_TXRX_CSR3, 0x00858687 }, + { RT2573_TXRX_CSR7, 0x2e31353b }, + { RT2573_TXRX_CSR8, 0x2a2a2a2c }, + { RT2573_TXRX_CSR15, 0x0000000f }, + { RT2573_MAC_CSR6, 0x00000fff }, + { RT2573_MAC_CSR8, 0x016c030a }, + { RT2573_MAC_CSR10, 0x00000718 }, + { RT2573_MAC_CSR12, 0x00000004 }, + { RT2573_MAC_CSR13, 0x00007f00 }, + { RT2573_SEC_CSR2, 0x00000000 }, + { RT2573_SEC_CSR3, 0x00000000 }, + { RT2573_SEC_CSR4, 0x00000000 }, + { RT2573_PHY_CSR1, 0x000023b0 }, + { RT2573_PHY_CSR5, 0x00040a06 }, + { RT2573_PHY_CSR6, 0x00080606 }, + { RT2573_PHY_CSR7, 0x00000408 }, + { RT2573_AIFSN_CSR, 0x00002273 }, + { RT2573_CWMIN_CSR, 0x00002344 }, + { RT2573_CWMAX_CSR, 0x000034aa } +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rum_def_bbp[] = { + { 3, 0x80 }, + { 15, 0x30 }, + { 17, 0x20 }, + { 21, 0xc8 }, + { 22, 0x38 }, + { 23, 0x06 }, + { 24, 0xfe }, + { 25, 0x0a }, + { 26, 0x0d }, + { 32, 0x0b }, + { 34, 0x12 }, + { 37, 0x07 }, + { 39, 0xf8 }, + { 41, 0x60 }, + { 53, 0x10 }, + { 54, 0x18 }, + { 60, 0x10 }, + { 61, 0x04 }, + { 62, 0x04 }, + { 75, 0xfe }, + { 86, 0xfe }, + { 88, 0xfe }, + { 90, 0x0f }, + { 99, 0x00 }, + { 102, 0x16 }, + { 107, 0x04 } +}; + +static const uint8_t rum_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + +static const uint8_t rum_chan_5ghz[] = + { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64, + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, + 149, 153, 157, 161, 165 }; + +static const struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; +} rum_rf5226[] = { + { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 }, + { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 }, + { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 }, + { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 }, + { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 }, + { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 }, + { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 }, + { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 }, + { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 }, + { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 }, + { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 }, + { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 }, + { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 }, + { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 }, + + { 34, 0x00b03, 0x20266, 0x36014, 0x30282 }, + { 38, 0x00b03, 0x20267, 0x36014, 0x30284 }, + { 42, 0x00b03, 0x20268, 0x36014, 0x30286 }, + { 46, 0x00b03, 0x20269, 0x36014, 0x30288 }, + + { 36, 0x00b03, 0x00266, 0x26014, 0x30288 }, + { 40, 0x00b03, 0x00268, 0x26014, 0x30280 }, + { 44, 0x00b03, 0x00269, 0x26014, 0x30282 }, + { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 }, + { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 }, + { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 }, + { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 }, + { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 }, + + { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 }, + { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 }, + { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 }, + { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 }, + { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 }, + { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 }, + { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 }, + { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 }, + { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 }, + { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 }, + { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 }, + + { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 }, + { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 }, + { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 }, + { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 }, + { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 } +}, rum_rf5225[] = { + { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 }, + { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 }, + { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 }, + { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 }, + { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 }, + { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 }, + { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 }, + { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 }, + { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 }, + { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 }, + { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 }, + { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 }, + { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 }, + { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 }, + + { 34, 0x00b33, 0x01266, 0x26014, 0x30282 }, + { 38, 0x00b33, 0x01267, 0x26014, 0x30284 }, + { 42, 0x00b33, 0x01268, 0x26014, 0x30286 }, + { 46, 0x00b33, 0x01269, 0x26014, 0x30288 }, + + { 36, 0x00b33, 0x01266, 0x26014, 0x30288 }, + { 40, 0x00b33, 0x01268, 0x26014, 0x30280 }, + { 44, 0x00b33, 0x01269, 0x26014, 0x30282 }, + { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 }, + { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 }, + { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 }, + { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 }, + { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 }, + + { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 }, + { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 }, + { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 }, + { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 }, + { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 }, + { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 }, + { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 }, + { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 }, + { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 }, + { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 }, + { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 }, + + { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 }, + { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 }, + { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 }, + { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 }, + { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 } +}; + +static const struct usb_config rum_config[RUM_N_TRANSFER] = { + [RUM_BULK_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = rum_bulk_write_callback, + .timeout = 5000, /* ms */ + }, + [RUM_BULK_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = rum_bulk_read_callback, + }, +}; + +static int +rum_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != 0) + return (ENXIO); + if (uaa->info.bIfaceIndex != RT2573_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa)); +} + +static int +rum_attach(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + struct rum_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + uint8_t iface_index; + int error, ntries; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + + RUM_LOCK_INIT(sc); + RUM_CMDQ_LOCK_INIT(sc); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + iface_index = RT2573_IFACE_INDEX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(self, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto detach; + } + + RUM_LOCK(sc); + /* retrieve RT2573 rev. no */ + for (ntries = 0; ntries < 100; ntries++) { + if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for chip to settle\n"); + RUM_UNLOCK(sc); + goto detach; + } + + /* retrieve MAC address and various other things from EEPROM */ + rum_read_eeprom(sc); + + device_printf(sc->sc_dev, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n", + tmp, rum_get_rf(sc->rf_rev)); + + rum_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode)); + RUM_UNLOCK(sc); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(self); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_HOSTAP /* HostAp mode supported */ + | IEEE80211_C_AHDEMO /* adhoc demo mode */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* bg scanning supported */ + | IEEE80211_C_WPA /* 802.11i */ + | IEEE80211_C_WME /* 802.11e */ + | IEEE80211_C_PMGT /* Station-side power mgmt */ + | IEEE80211_C_SWSLEEP /* net80211 managed power mgmt */ + ; + + ic->ic_cryptocaps = + IEEE80211_CRYPTO_WEP | + IEEE80211_CRYPTO_AES_CCM | + IEEE80211_CRYPTO_TKIPMIC | + IEEE80211_CRYPTO_TKIP; + + rum_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + ic->ic_update_promisc = rum_update_promisc; + ic->ic_raw_xmit = rum_raw_xmit; + ic->ic_scan_start = rum_scan_start; + ic->ic_scan_end = rum_scan_end; + ic->ic_set_channel = rum_set_channel; + ic->ic_getradiocaps = rum_getradiocaps; + ic->ic_transmit = rum_transmit; + ic->ic_parent = rum_parent; + ic->ic_vap_create = rum_vap_create; + ic->ic_vap_delete = rum_vap_delete; + ic->ic_updateslot = rum_update_slot; + ic->ic_wme.wme_update = rum_wme_update; + ic->ic_update_mcast = rum_update_mcast; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + RT2573_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RT2573_RX_RADIOTAP_PRESENT); + + TASK_INIT(&sc->cmdq_task, 0, rum_cmdq_cb, sc); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +detach: + rum_detach(self); + return (ENXIO); /* failure */ +} + +static int +rum_detach(device_t self) +{ + struct rum_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + + /* Prevent further ioctls */ + RUM_LOCK(sc); + sc->sc_detached = 1; + RUM_UNLOCK(sc); + + /* stop all USB transfers */ + usbd_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER); + + /* free TX list, if any */ + RUM_LOCK(sc); + rum_unsetup_tx_list(sc); + RUM_UNLOCK(sc); + + if (ic->ic_softc == sc) { + ieee80211_draintask(ic, &sc->cmdq_task); + ieee80211_ifdetach(ic); + } + + mbufq_drain(&sc->sc_snd); + RUM_CMDQ_LOCK_DESTROY(sc); + RUM_LOCK_DESTROY(sc); + + return (0); +} + +static usb_error_t +rum_do_request(struct rum_softc *sc, + struct usb_device_request *req, void *data) +{ + usb_error_t err; + int ntries = 10; + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0) + break; + + DPRINTFN(1, "Control request failed, %s (retrying)\n", + usbd_errstr(err)); + if (rum_pause(sc, hz / 100)) + break; + } + return (err); +} + +static usb_error_t +rum_do_mcu_request(struct rum_softc *sc, int request) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2573_MCU_CNTL; + USETW(req.wValue, request); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + return (rum_do_request(sc, &req, NULL)); +} + +static struct ieee80211vap * +rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct rum_softc *sc = ic->ic_softc; + struct rum_vap *rvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + rvp = malloc(sizeof(struct rum_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &rvp->vap; + /* enable s/w bmiss handling for sta mode */ + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(rvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + rvp->newstate = vap->iv_newstate; + vap->iv_newstate = rum_newstate; + vap->iv_key_alloc = rum_key_alloc; + vap->iv_key_set = rum_key_set; + vap->iv_key_delete = rum_key_delete; + vap->iv_update_beacon = rum_update_beacon; + vap->iv_reset = rum_reset; + vap->iv_max_aid = RT2573_ADDR_MAX; + + if (opmode == IEEE80211_M_STA) { + /* + * Move device to the sleep state when + * beacon is received and there is no data for us. + * + * Used only for IEEE80211_S_SLEEP state. + */ + rvp->recv_mgmt = vap->iv_recv_mgmt; + vap->iv_recv_mgmt = rum_sta_recv_mgmt; + + /* Ignored while sleeping. */ + rvp->bmiss = vap->iv_bmiss; + vap->iv_bmiss = rum_beacon_miss; + } + + usb_callout_init_mtx(&rvp->ratectl_ch, &sc->sc_mtx, 0); + TASK_INIT(&rvp->ratectl_task, 0, rum_ratectl_task, rvp); + ieee80211_ratectl_init(vap); + ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return vap; +} + +static void +rum_vap_delete(struct ieee80211vap *vap) +{ + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + + /* Put vap into INIT state. */ + ieee80211_new_state(vap, IEEE80211_S_INIT, -1); + ieee80211_draintask(ic, &vap->iv_nstate_task); + + RUM_LOCK(sc); + /* Cancel any unfinished Tx. */ + rum_reset_tx_list(sc, vap); + RUM_UNLOCK(sc); + + usb_callout_drain(&rvp->ratectl_ch); + ieee80211_draintask(ic, &rvp->ratectl_task); + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + m_freem(rvp->bcn_mbuf); + free(rvp, M_80211_VAP); +} + +static void +rum_cmdq_cb(void *arg, int pending) +{ + struct rum_softc *sc = arg; + struct rum_cmdq *rc; + + RUM_CMDQ_LOCK(sc); + while (sc->cmdq[sc->cmdq_first].func != NULL) { + rc = &sc->cmdq[sc->cmdq_first]; + RUM_CMDQ_UNLOCK(sc); + + RUM_LOCK(sc); + rc->func(sc, &rc->data, rc->rvp_id); + RUM_UNLOCK(sc); + + RUM_CMDQ_LOCK(sc); + memset(rc, 0, sizeof (*rc)); + sc->cmdq_first = (sc->cmdq_first + 1) % RUM_CMDQ_SIZE; + } + RUM_CMDQ_UNLOCK(sc); +} + +static int +rum_cmd_sleepable(struct rum_softc *sc, const void *ptr, size_t len, + uint8_t rvp_id, CMD_FUNC_PROTO) +{ + struct ieee80211com *ic = &sc->sc_ic; + + KASSERT(len <= sizeof(union sec_param), ("buffer overflow")); + + RUM_CMDQ_LOCK(sc); + if (sc->cmdq[sc->cmdq_last].func != NULL) { + device_printf(sc->sc_dev, "%s: cmdq overflow\n", __func__); + RUM_CMDQ_UNLOCK(sc); + + return EAGAIN; + } + + if (ptr != NULL) + memcpy(&sc->cmdq[sc->cmdq_last].data, ptr, len); + sc->cmdq[sc->cmdq_last].rvp_id = rvp_id; + sc->cmdq[sc->cmdq_last].func = func; + sc->cmdq_last = (sc->cmdq_last + 1) % RUM_CMDQ_SIZE; + RUM_CMDQ_UNLOCK(sc); + + ieee80211_runtask(ic, &sc->cmdq_task); + + return 0; +} + +static void +rum_tx_free(struct rum_tx_data *data, int txerr) +{ + struct rum_softc *sc = data->sc; + + if (data->m != NULL) { + ieee80211_tx_complete(data->ni, data->m, txerr); + data->m = NULL; + data->ni = NULL; + } + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; +} + +static void +rum_setup_tx_list(struct rum_softc *sc) +{ + struct rum_tx_data *data; + int i; + + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + for (i = 0; i < RUM_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + data->sc = sc; + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } +} + +static void +rum_reset_tx_list(struct rum_softc *sc, struct ieee80211vap *vap) +{ + struct rum_tx_data *data, *tmp; + + KASSERT(vap != NULL, ("%s: vap is NULL\n", __func__)); + + STAILQ_FOREACH_SAFE(data, &sc->tx_q, next, tmp) { + if (data->ni != NULL && data->ni->ni_vap == vap) { + ieee80211_free_node(data->ni); + data->ni = NULL; + + KASSERT(data->m != NULL, ("%s: m is NULL\n", + __func__)); + m_freem(data->m); + data->m = NULL; + + STAILQ_REMOVE(&sc->tx_q, data, rum_tx_data, next); + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } + } +} + +static void +rum_unsetup_tx_list(struct rum_softc *sc) +{ + struct rum_tx_data *data; + int i; + + /* make sure any subsequent use of the queues will fail */ + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + /* free up all node references and mbufs */ + for (i = 0; i < RUM_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +static void +rum_beacon_miss(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + struct rum_vap *rvp = RUM_VAP(vap); + int sleep; + + RUM_LOCK(sc); + if (sc->sc_sleeping && sc->sc_sleep_end < ticks) { + DPRINTFN(12, "dropping 'sleeping' bit, " + "device must be awake now\n"); + + sc->sc_sleeping = 0; + } + + sleep = sc->sc_sleeping; + RUM_UNLOCK(sc); + + if (!sleep) + rvp->bmiss(vap); +#ifdef USB_DEBUG + else + DPRINTFN(13, "bmiss event is ignored whilst sleeping\n"); +#endif +} + +static void +rum_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, + const struct ieee80211_rx_stats *rxs, + int rssi, int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct rum_softc *sc = vap->iv_ic->ic_softc; + struct rum_vap *rvp = RUM_VAP(vap); + + if (vap->iv_state == IEEE80211_S_SLEEP && + subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + RUM_LOCK(sc); + DPRINTFN(12, "beacon, mybss %d (flags %02X)\n", + !!(sc->last_rx_flags & RT2573_RX_MYBSS), + sc->last_rx_flags); + + if ((sc->last_rx_flags & (RT2573_RX_MYBSS | RT2573_RX_BC)) == + (RT2573_RX_MYBSS | RT2573_RX_BC)) { + /* + * Put it to sleep here; in case if there is a data + * for us, iv_recv_mgmt() will wakeup the device via + * SLEEP -> RUN state transition. + */ + rum_set_power_state(sc, 1); + } + RUM_UNLOCK(sc); + } + + rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); +} + +static int +rum_set_power_state(struct rum_softc *sc, int sleep) +{ + usb_error_t uerror; + + RUM_LOCK_ASSERT(sc); + + DPRINTFN(12, "moving to %s state (sleep time %u)\n", + sleep ? "sleep" : "awake", sc->sc_sleep_time); + + uerror = rum_do_mcu_request(sc, + sleep ? RT2573_MCU_SLEEP : RT2573_MCU_WAKEUP); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + device_printf(sc->sc_dev, + "%s: could not change power state: %s\n", + __func__, usbd_errstr(uerror)); + return (EIO); + } + + sc->sc_sleeping = !!sleep; + sc->sc_sleep_end = sleep ? ticks + sc->sc_sleep_time : 0; + + return (0); +} + +static int +rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + const struct ieee80211_txparam *tp; + enum ieee80211_state ostate; + struct ieee80211_node *ni; + usb_error_t uerror; + int ret = 0; + + ostate = vap->iv_state; + DPRINTF("%s -> %s\n", + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + RUM_LOCK(sc); + usb_callout_stop(&rvp->ratectl_ch); + + if (ostate == IEEE80211_S_SLEEP && vap->iv_opmode == IEEE80211_M_STA) { + rum_clrbits(sc, RT2573_TXRX_CSR4, RT2573_ACKCTS_PWRMGT); + rum_clrbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + + /* + * Ignore any errors; + * any subsequent TX will wakeup it anyway + */ + (void) rum_set_power_state(sc, 0); + } + + switch (nstate) { + case IEEE80211_S_INIT: + if (ostate == IEEE80211_S_RUN) + rum_abort_tsf_sync(sc); + + break; + + case IEEE80211_S_RUN: + if (ostate == IEEE80211_S_SLEEP) + break; /* already handled */ + + ni = ieee80211_ref_node(vap->iv_bss); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) { + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC || + ni->ni_chan == IEEE80211_CHAN_ANYC) { + ret = EINVAL; + goto run_fail; + } + rum_update_slot_cb(sc, NULL, 0); + rum_enable_mrr(sc); + rum_set_txpreamble(sc); + rum_set_basicrates(sc); + rum_set_maxretry(sc, vap); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + rum_set_bssid(sc, sc->sc_bssid); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) { + if ((ret = rum_alloc_beacon(sc, vap)) != 0) + goto run_fail; + } + + if (vap->iv_opmode != IEEE80211_M_MONITOR && + vap->iv_opmode != IEEE80211_M_AHDEMO) { + if ((ret = rum_enable_tsf_sync(sc)) != 0) + goto run_fail; + } else + rum_enable_tsf(sc); + + /* enable automatic rate adaptation */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + rum_ratectl_start(sc, ni); +run_fail: + ieee80211_free_node(ni); + break; + case IEEE80211_S_SLEEP: + /* Implemented for STA mode only. */ + if (vap->iv_opmode != IEEE80211_M_STA) + break; + + uerror = rum_setbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + ret = EIO; + break; + } + + uerror = rum_setbits(sc, RT2573_TXRX_CSR4, RT2573_ACKCTS_PWRMGT); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + ret = EIO; + break; + } + + ret = rum_set_power_state(sc, 1); + if (ret != 0) { + device_printf(sc->sc_dev, + "%s: could not move to the SLEEP state: %s\n", + __func__, usbd_errstr(uerror)); + } + break; + default: + break; + } + RUM_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (ret == 0 ? rvp->newstate(vap, nstate, arg) : ret); +} + +static void +rum_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct rum_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211vap *vap; + struct rum_tx_data *data; + struct mbuf *m; + struct usb_page_cache *pc; + unsigned int len; + int actlen, sumlen; + + usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete, %d bytes\n", actlen); + + /* free resources */ + data = usbd_xfer_get_priv(xfer); + rum_tx_free(data, 0); + usbd_xfer_set_priv(xfer, NULL); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->tx_q); + if (data) { + STAILQ_REMOVE_HEAD(&sc->tx_q, next); + m = data->m; + + if (m->m_pkthdr.len > (int)(MCLBYTES + RT2573_TX_DESC_SIZE)) { + DPRINTFN(0, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE); + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &data->desc, RT2573_TX_DESC_SIZE); + usbd_m_copy_in(pc, RT2573_TX_DESC_SIZE, m, 0, + m->m_pkthdr.len); + + vap = data->ni->ni_vap; + if (ieee80211_radiotap_active_vap(vap)) { + struct rum_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = data->rate; + tap->wt_antenna = sc->tx_ant; + + ieee80211_radiotap_tx(vap, m); + } + + /* align end on a 4-bytes boundary */ + len = (RT2573_TX_DESC_SIZE + m->m_pkthdr.len + 3) & ~3; + if ((len % 64) == 0) + len += 4; + + DPRINTFN(11, "sending frame len=%u xferlen=%u\n", + m->m_pkthdr.len, len); + + usbd_xfer_set_frame_len(xfer, 0, len); + usbd_xfer_set_priv(xfer, data); + + usbd_transfer_submit(xfer); + } + rum_start(sc); + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + data = usbd_xfer_get_priv(xfer); + if (data != NULL) { + rum_tx_free(data, error); + usbd_xfer_set_priv(xfer, NULL); + } + + if (error != USB_ERR_CANCELLED) { + if (error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + + /* + * Try to clear stall first, also if other + * errors occur, hence clearing stall + * introduces a 50 ms delay: + */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +rum_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct rum_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame_min *wh; + struct ieee80211_node *ni; + struct mbuf *m = NULL; + struct usb_page_cache *pc; + uint32_t flags; + uint8_t rssi = 0; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(15, "rx done, actlen=%d\n", len); + + if (len < RT2573_RX_DESC_SIZE) { + DPRINTF("%s: xfer too short %d\n", + device_get_nameunit(sc->sc_dev), len); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + + len -= RT2573_RX_DESC_SIZE; + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &sc->sc_rx_desc, RT2573_RX_DESC_SIZE); + + rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); + flags = le32toh(sc->sc_rx_desc.flags); + sc->last_rx_flags = flags; + if (len < ((flags >> 16) & 0xfff)) { + DPRINTFN(5, "%s: frame is truncated from %d to %d " + "bytes\n", device_get_nameunit(sc->sc_dev), + (flags >> 16) & 0xfff, len); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + len = (flags >> 16) & 0xfff; + if (len < sizeof(struct ieee80211_frame_ack)) { + DPRINTFN(5, "%s: frame too short %d\n", + device_get_nameunit(sc->sc_dev), len); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + if (flags & RT2573_RX_CRC_ERROR) { + /* + * This should not happen since we did not + * request to receive those frames when we + * filled RUM_TXRX_CSR2: + */ + DPRINTFN(5, "PHY or CRC error\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + if ((flags & RT2573_RX_DEC_MASK) != RT2573_RX_DEC_OK) { + switch (flags & RT2573_RX_DEC_MASK) { + case RT2573_RX_IV_ERROR: + DPRINTFN(5, "IV/EIV error\n"); + break; + case RT2573_RX_MIC_ERROR: + DPRINTFN(5, "MIC error\n"); + break; + case RT2573_RX_KEY_ERROR: + DPRINTFN(5, "Key error\n"); + break; + } + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + + m = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + DPRINTF("could not allocate mbuf\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + usbd_copy_out(pc, RT2573_RX_DESC_SIZE, + mtod(m, uint8_t *), len); + + wh = mtod(m, struct ieee80211_frame_min *); + + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + (flags & RT2573_RX_CIP_MASK) != + RT2573_RX_CIP_MODE(RT2573_MODE_NOSEC)) { + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + m->m_flags |= M_WEP; + } + + /* finalize mbuf */ + m->m_pkthdr.len = m->m_len = len; + + if (ieee80211_radiotap_active(ic)) { + struct rum_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, + (flags & RT2573_RX_OFDM) ? + IEEE80211_T_OFDM : IEEE80211_T_CCK); + rum_get_tsf(sc, &tap->wr_tsf); + tap->wr_antsignal = RT2573_NOISE_FLOOR + rssi; + tap->wr_antnoise = RT2573_NOISE_FLOOR; + tap->wr_antenna = sc->rx_ant; + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "ieee80211_input" here, and not some lines up! + */ + RUM_UNLOCK(sc); + if (m) { + if (m->m_len >= sizeof(struct ieee80211_frame_min)) + ni = ieee80211_find_rxnode(ic, wh); + else + ni = NULL; + + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi, + RT2573_NOISE_FLOOR); + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi, + RT2573_NOISE_FLOOR); + } + RUM_LOCK(sc); + rum_start(sc); + return; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static uint8_t +rum_plcp_signal(int rate) +{ + switch (rate) { + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: return 0xb; + case 18: return 0xf; + case 24: return 0xa; + case 36: return 0xe; + case 48: return 0x9; + case 72: return 0xd; + case 96: return 0x8; + case 108: return 0xc; + + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: return 0x0; + case 4: return 0x1; + case 11: return 0x2; + case 22: return 0x3; + } + return 0xff; /* XXX unsupported/unknown rate */ +} + +/* + * Map net80211 cipher to RT2573 security mode. + */ +static uint8_t +rum_crypto_mode(struct rum_softc *sc, u_int cipher, int keylen) +{ + switch (cipher) { + case IEEE80211_CIPHER_WEP: + return (keylen < 8 ? RT2573_MODE_WEP40 : RT2573_MODE_WEP104); + case IEEE80211_CIPHER_TKIP: + return RT2573_MODE_TKIP; + case IEEE80211_CIPHER_AES_CCM: + return RT2573_MODE_AES_CCMP; + default: + device_printf(sc->sc_dev, "unknown cipher %d\n", cipher); + return 0; + } +} + +static void +rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc, + struct ieee80211_key *k, uint32_t flags, uint8_t xflags, uint8_t qid, + int hdrlen, int len, int rate) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wmeParams *wmep = &sc->wme_params[qid]; + uint16_t plcp_length; + int remainder; + + flags |= RT2573_TX_VALID; + flags |= len << 16; + + if (k != NULL && !(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { + const struct ieee80211_cipher *cip = k->wk_cipher; + + len += cip->ic_header + cip->ic_trailer + cip->ic_miclen; + + desc->eiv = 0; /* for WEP */ + cip->ic_setiv(k, (uint8_t *)&desc->iv); + } + + /* setup PLCP fields */ + desc->plcp_signal = rum_plcp_signal(rate); + desc->plcp_service = 4; + + len += IEEE80211_CRC_LEN; + if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) { + flags |= RT2573_TX_OFDM; + + plcp_length = len & 0xfff; + desc->plcp_length_hi = plcp_length >> 6; + desc->plcp_length_lo = plcp_length & 0x3f; + } else { + if (rate == 0) + rate = 2; /* avoid division by zero */ + plcp_length = howmany(16 * len, rate); + if (rate == 22) { + remainder = (16 * len) % 22; + if (remainder != 0 && remainder < 7) + desc->plcp_service |= RT2573_PLCP_LENGEXT; + } + desc->plcp_length_hi = plcp_length >> 8; + desc->plcp_length_lo = plcp_length & 0xff; + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->plcp_signal |= 0x08; + } + + desc->flags = htole32(flags); + desc->hdrlen = hdrlen; + desc->xflags = xflags; + + desc->wme = htole16(RT2573_QID(qid) | + RT2573_AIFSN(wmep->wmep_aifsn) | + RT2573_LOGCWMIN(wmep->wmep_logcwmin) | + RT2573_LOGCWMAX(wmep->wmep_logcwmax)); +} + +static int +rum_sendprot(struct rum_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_frame *wh; + struct rum_tx_data *data; + struct mbuf *mprot; + int protrate, pktlen, flags, isshort; + uint16_t dur; + + RUM_LOCK_ASSERT(sc); + KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, + ("protection %d", prot)); + + wh = mtod(m, const struct ieee80211_frame *); + pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; + + protrate = ieee80211_ctl_rate(ic->ic_rt, rate); + + isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; + dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort) + + ieee80211_ack_duration(ic->ic_rt, rate, isshort); + flags = 0; + if (prot == IEEE80211_PROT_RTSCTS) { + /* NB: CTS is the same size as an ACK */ + dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); + flags |= RT2573_TX_NEED_ACK; + mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); + } else { + mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); + } + if (mprot == NULL) { + /* XXX stat + msg */ + return (ENOBUFS); + } + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = mprot; + data->ni = ieee80211_ref_node(ni); + data->rate = protrate; + rum_setup_tx_desc(sc, &data->desc, NULL, flags, 0, 0, 0, + mprot->m_pkthdr.len, protrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static uint32_t +rum_tx_crypto_flags(struct rum_softc *sc, struct ieee80211_node *ni, + const struct ieee80211_key *k) +{ + struct ieee80211vap *vap = ni->ni_vap; + u_int cipher; + uint32_t flags = 0; + uint8_t mode, pos; + + if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { + cipher = k->wk_cipher->ic_cipher; + pos = k->wk_keyix; + mode = rum_crypto_mode(sc, cipher, k->wk_keylen); + if (mode == 0) + return 0; + + flags |= RT2573_TX_CIP_MODE(mode); + + /* Do not trust GROUP flag */ + if (!(k >= &vap->iv_nw_keys[0] && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) + flags |= RT2573_TX_KEY_PAIR; + else + pos += 0 * RT2573_SKEY_MAX; /* vap id */ + + flags |= RT2573_TX_KEY_ID(pos); + + if (cipher == IEEE80211_CIPHER_TKIP) + flags |= RT2573_TX_TKIPMIC; + } + + return flags; +} + +static int +rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = &sc->sc_ic; + struct rum_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k = NULL; + uint32_t flags = 0; + uint16_t dur; + uint8_t ac, type, xflags = 0; + int hdrlen; + + RUM_LOCK_ASSERT(sc); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + hdrlen = ieee80211_anyhdrsize(wh); + ac = M_WME_GETAC(m0); + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_get_txkey(ni, m0); + if (k == NULL) + return (ENOENT); + + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !k->wk_cipher->ic_encap(k, m0)) + return (ENOBUFS); + + wh = mtod(m0, struct ieee80211_frame *); + } + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RT2573_TX_NEED_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + USETW(wh->i_dur, dur); + + /* tell hardware to add timestamp for probe responses */ + if (type == IEEE80211_FC0_TYPE_MGT && + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= RT2573_TX_TIMESTAMP; + } + + if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh)) + xflags |= RT2573_TX_HWSEQ; + + if (k != NULL) + flags |= rum_tx_crypto_flags(sc, ni, k); + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + rum_setup_tx_desc(sc, &data->desc, k, flags, xflags, ac, hdrlen, + m0->m_pkthdr.len, tp->mgmtrate); + + DPRINTFN(10, "sending mgt frame len=%d rate=%d\n", + m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return (0); +} + +static int +rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + struct rum_tx_data *data; + uint32_t flags; + uint8_t ac, type, xflags = 0; + int rate, error; + + RUM_LOCK_ASSERT(sc); + + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + ac = params->ibp_pri & 3; + + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) + return (EINVAL); + + flags = 0; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= RT2573_TX_NEED_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { + error = rum_sendprot(sc, m0, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + if (error || sc->tx_nfree == 0) + return (ENOBUFS); + + flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; + } + + if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh)) + xflags |= RT2573_TX_HWSEQ; + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + /* XXX need to setup descriptor ourself */ + rum_setup_tx_desc(sc, &data->desc, NULL, flags, xflags, ac, 0, + m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending raw frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static int +rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = &sc->sc_ic; + struct rum_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k = NULL; + uint32_t flags = 0; + uint16_t dur; + uint8_t ac, type, qos, xflags = 0; + int error, hdrlen, rate; + + RUM_LOCK_ASSERT(sc); + + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + hdrlen = ieee80211_anyhdrsize(wh); + + if (IEEE80211_QOS_HAS_SEQ(wh)) + qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; + else + qos = 0; + ac = M_WME_GETAC(m0); + + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else if (m0->m_flags & M_EAPOL) + rate = tp->mgmtrate; + else { + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = ni->ni_txrate; + } + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_get_txkey(ni, m0); + if (k == NULL) { + m_freem(m0); + return (ENOENT); + } + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !k->wk_cipher->ic_encap(k, m0)) { + m_freem(m0); + return (ENOBUFS); + } + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh)) + xflags |= RT2573_TX_HWSEQ; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + int prot = IEEE80211_PROT_NONE; + if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) + prot = IEEE80211_PROT_RTSCTS; + else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) + prot = ic->ic_protmode; + if (prot != IEEE80211_PROT_NONE) { + error = rum_sendprot(sc, m0, ni, prot, rate); + if (error || sc->tx_nfree == 0) { + m_freem(m0); + return ENOBUFS; + } + flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; + } + } + + if (k != NULL) + flags |= rum_tx_crypto_flags(sc, ni, k); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* Unicast frame, check if an ACK is expected. */ + if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK) + flags |= RT2573_TX_NEED_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + USETW(wh->i_dur, dur); + } + + rum_setup_tx_desc(sc, &data->desc, k, flags, xflags, ac, hdrlen, + m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending frame len=%d rate=%d\n", + m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static int +rum_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct rum_softc *sc = ic->ic_softc; + int error; + + RUM_LOCK(sc); + if (!sc->sc_running) { + RUM_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RUM_UNLOCK(sc); + return (error); + } + rum_start(sc); + RUM_UNLOCK(sc); + + return (0); +} + +static void +rum_start(struct rum_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + RUM_LOCK_ASSERT(sc); + + if (!sc->sc_running) + return; + + while (sc->tx_nfree >= RUM_TX_MINFREE && + (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + if (rum_tx_data(sc, m, ni) != 0) { + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + break; + } + } +} + +static void +rum_parent(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + RUM_LOCK(sc); + if (sc->sc_detached) { + RUM_UNLOCK(sc); + return; + } + RUM_UNLOCK(sc); + + if (ic->ic_nrunning > 0) { + if (rum_init(sc) == 0) + ieee80211_start_all(ic); + else + ieee80211_stop(vap); + } else + rum_stop(sc); +} + +static void +rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2573_READ_EEPROM; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + error = rum_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read EEPROM: %s\n", + usbd_errstr(error)); + } +} + +static uint32_t +rum_read(struct rum_softc *sc, uint16_t reg) +{ + uint32_t val; + + rum_read_multi(sc, reg, &val, sizeof val); + + return le32toh(val); +} + +static void +rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2573_READ_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = rum_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, + "could not multi read MAC register: %s\n", + usbd_errstr(error)); + } +} + +static usb_error_t +rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val) +{ + uint32_t tmp = htole32(val); + + return (rum_write_multi(sc, reg, &tmp, sizeof tmp)); +} + +static usb_error_t +rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len) +{ + struct usb_device_request req; + usb_error_t error; + size_t offset; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2573_WRITE_MULTI_MAC; + USETW(req.wValue, 0); + + /* write at most 64 bytes at a time */ + for (offset = 0; offset < len; offset += 64) { + USETW(req.wIndex, reg + offset); + USETW(req.wLength, MIN(len - offset, 64)); + + error = rum_do_request(sc, &req, (char *)buf + offset); + if (error != 0) { + device_printf(sc->sc_dev, + "could not multi write MAC register: %s\n", + usbd_errstr(error)); + return (error); + } + } + + return (USB_ERR_NORMAL_COMPLETION); +} + +static usb_error_t +rum_setbits(struct rum_softc *sc, uint16_t reg, uint32_t mask) +{ + return (rum_write(sc, reg, rum_read(sc, reg) | mask)); +} + +static usb_error_t +rum_clrbits(struct rum_softc *sc, uint16_t reg, uint32_t mask) +{ + return (rum_write(sc, reg, rum_read(sc, reg) & ~mask)); +} + +static usb_error_t +rum_modbits(struct rum_softc *sc, uint16_t reg, uint32_t set, uint32_t unset) +{ + return (rum_write(sc, reg, (rum_read(sc, reg) & ~unset) | set)); +} + +static int +rum_bbp_busy(struct rum_softc *sc) +{ + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + return (0); +} + +static void +rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + + DPRINTFN(2, "reg=0x%08x\n", reg); + + if (rum_bbp_busy(sc) != 0) { + device_printf(sc->sc_dev, "could not write to BBP\n"); + return; + } + + tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val; + rum_write(sc, RT2573_PHY_CSR3, tmp); +} + +static uint8_t +rum_bbp_read(struct rum_softc *sc, uint8_t reg) +{ + uint32_t val; + int ntries; + + DPRINTFN(2, "reg=0x%08x\n", reg); + + if (rum_bbp_busy(sc) != 0) { + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; + } + + val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8; + rum_write(sc, RT2573_PHY_CSR3, val); + + for (ntries = 0; ntries < 100; ntries++) { + val = rum_read(sc, RT2573_PHY_CSR3); + if (!(val & RT2573_BBP_BUSY)) + return val & 0xff; + if (rum_pause(sc, hz / 100)) + break; + } + + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; +} + +static void +rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY)) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to RF\n"); + return; + } + + tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 | + (reg & 3); + rum_write(sc, RT2573_PHY_CSR4, tmp); + + /* remember last written value in sc */ + sc->rf_regs[reg] = val; + + DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff); +} + +static void +rum_select_antenna(struct rum_softc *sc) +{ + uint8_t bbp4, bbp77; + uint32_t tmp; + + bbp4 = rum_bbp_read(sc, 4); + bbp77 = rum_bbp_read(sc, 77); + + /* TBD */ + + /* make sure Rx is disabled before switching antenna */ + tmp = rum_read(sc, RT2573_TXRX_CSR0); + rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); + + rum_bbp_write(sc, 4, bbp4); + rum_bbp_write(sc, 77, bbp77); + + rum_write(sc, RT2573_TXRX_CSR0, tmp); +} + +/* + * Enable multi-rate retries for frames sent at OFDM rates. + * In 802.11b/g mode, allow fallback to CCK rates. + */ +static void +rum_enable_mrr(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) { + rum_setbits(sc, RT2573_TXRX_CSR4, + RT2573_MRR_ENABLED | RT2573_MRR_CCK_FALLBACK); + } else { + rum_modbits(sc, RT2573_TXRX_CSR4, + RT2573_MRR_ENABLED, RT2573_MRR_CCK_FALLBACK); + } +} + +static void +rum_set_txpreamble(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + rum_setbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_PREAMBLE); + else + rum_clrbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_PREAMBLE); +} + +static void +rum_set_basicrates(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + /* update basic rate set */ + if (ic->ic_curmode == IEEE80211_MODE_11B) { + /* 11b basic rates: 1, 2Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0x3); + } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) { + /* 11a basic rates: 6, 12, 24Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0x150); + } else { + /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0xf); + } +} + +/* + * Reprogram MAC/BBP to switch to a new band. Values taken from the reference + * driver. + */ +static void +rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c) +{ + uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; + + /* update all BBP registers that depend on the band */ + bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c; + bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48; + if (IEEE80211_IS_CHAN_5GHZ(c)) { + bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c; + bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10; + } + if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || + (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { + bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10; + } + + sc->bbp17 = bbp17; + rum_bbp_write(sc, 17, bbp17); + rum_bbp_write(sc, 96, bbp96); + rum_bbp_write(sc, 104, bbp104); + + if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || + (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { + rum_bbp_write(sc, 75, 0x80); + rum_bbp_write(sc, 86, 0x80); + rum_bbp_write(sc, 88, 0x80); + } + + rum_bbp_write(sc, 35, bbp35); + rum_bbp_write(sc, 97, bbp97); + rum_bbp_write(sc, 98, bbp98); + + if (IEEE80211_IS_CHAN_2GHZ(c)) { + rum_modbits(sc, RT2573_PHY_CSR0, RT2573_PA_PE_2GHZ, + RT2573_PA_PE_5GHZ); + } else { + rum_modbits(sc, RT2573_PHY_CSR0, RT2573_PA_PE_5GHZ, + RT2573_PA_PE_2GHZ); + } +} + +static void +rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + const struct rfprog *rfprog; + uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT; + int8_t power; + int i, chan; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + /* select the appropriate RF settings based on what EEPROM says */ + rfprog = (sc->rf_rev == RT2573_RF_5225 || + sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rfprog[i].chan != chan; i++); + + power = sc->txpow[i]; + if (power < 0) { + bbp94 += power; + power = 0; + } else if (power > 31) { + bbp94 += power - 31; + power = 31; + } + + /* + * If we are switching from the 2GHz band to the 5GHz band or + * vice-versa, BBP registers need to be reprogrammed. + */ + if (c->ic_flags != ic->ic_curchan->ic_flags) { + rum_select_band(sc, c); + rum_select_antenna(sc); + } + ic->ic_curchan = c; + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_pause(sc, hz / 100); + + /* enable smart mode for MIMO-capable RFs */ + bbp3 = rum_bbp_read(sc, 3); + + bbp3 &= ~RT2573_SMART_MODE; + if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527) + bbp3 |= RT2573_SMART_MODE; + + rum_bbp_write(sc, 3, bbp3); + + if (bbp94 != RT2573_BBPR94_DEFAULT) + rum_bbp_write(sc, 94, bbp94); + + /* give the chip some extra time to do the switchover */ + rum_pause(sc, hz / 100); +} + +static void +rum_set_maxretry(struct rum_softc *sc, struct ieee80211vap *vap) +{ + const struct ieee80211_txparam *tp; + struct ieee80211_node *ni = vap->iv_bss; + struct rum_vap *rvp = RUM_VAP(vap); + + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + rvp->maxretry = tp->maxretry < 0xf ? tp->maxretry : 0xf; + + rum_modbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_RETRY(rvp->maxretry) | + RT2573_LONG_RETRY(rvp->maxretry), + RT2573_SHORT_RETRY_MASK | RT2573_LONG_RETRY_MASK); +} + +/* + * Enable TSF synchronization and tell h/w to start sending beacons for IBSS + * and HostAP operating modes. + */ +static int +rum_enable_tsf_sync(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + uint16_t bintval; + + if (vap->iv_opmode != IEEE80211_M_STA) { + /* + * Change default 16ms TBTT adjustment to 8ms. + * Must be done before enabling beacon generation. + */ + if (rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8) != 0) + return EIO; + } + + tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000; + + /* set beacon interval (in 1/16ms unit) */ + bintval = vap->iv_bss->ni_intval; + tmp |= bintval * 16; + tmp |= RT2573_TSF_TIMER_EN | RT2573_TBTT_TIMER_EN; + + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + /* + * Local TSF is always updated with remote TSF on beacon + * reception. + */ + tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_STA); + break; + case IEEE80211_M_IBSS: + /* + * Local TSF is updated with remote TSF on beacon reception + * only if the remote TSF is greater than local TSF. + */ + tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_IBSS); + tmp |= RT2573_BCN_TX_EN; + break; + case IEEE80211_M_HOSTAP: + /* SYNC with nobody */ + tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_HOSTAP); + tmp |= RT2573_BCN_TX_EN; + break; + default: + device_printf(sc->sc_dev, + "Enabling TSF failed. undefined opmode %d\n", + vap->iv_opmode); + return EINVAL; + } + + if (rum_write(sc, RT2573_TXRX_CSR9, tmp) != 0) + return EIO; + + /* refresh current sleep time */ + return (rum_set_sleep_time(sc, bintval)); +} + +static void +rum_enable_tsf(struct rum_softc *sc) +{ + rum_modbits(sc, RT2573_TXRX_CSR9, RT2573_TSF_TIMER_EN | + RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_DIS), 0x00ffffff); +} + +static void +rum_abort_tsf_sync(struct rum_softc *sc) +{ + rum_clrbits(sc, RT2573_TXRX_CSR9, 0x00ffffff); +} + +static void +rum_get_tsf(struct rum_softc *sc, uint64_t *buf) +{ + rum_read_multi(sc, RT2573_TXRX_CSR12, buf, sizeof (*buf)); +} + +static void +rum_update_slot_cb(struct rum_softc *sc, union sec_param *data, uint8_t rvp_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint8_t slottime; + + slottime = IEEE80211_GET_SLOTTIME(ic); + + rum_modbits(sc, RT2573_MAC_CSR9, slottime, 0xff); + + DPRINTF("setting slot time to %uus\n", slottime); +} + +static void +rum_update_slot(struct ieee80211com *ic) +{ + rum_cmd_sleepable(ic->ic_softc, NULL, 0, 0, rum_update_slot_cb); +} + +static int +rum_wme_update(struct ieee80211com *ic) +{ + const struct wmeParams *chanp = + ic->ic_wme.wme_chanParams.cap_wmeParams; + struct rum_softc *sc = ic->ic_softc; + int error = 0; + + RUM_LOCK(sc); + error = rum_write(sc, RT2573_AIFSN_CSR, + chanp[WME_AC_VO].wmep_aifsn << 12 | + chanp[WME_AC_VI].wmep_aifsn << 8 | + chanp[WME_AC_BK].wmep_aifsn << 4 | + chanp[WME_AC_BE].wmep_aifsn); + if (error) + goto print_err; + error = rum_write(sc, RT2573_CWMIN_CSR, + chanp[WME_AC_VO].wmep_logcwmin << 12 | + chanp[WME_AC_VI].wmep_logcwmin << 8 | + chanp[WME_AC_BK].wmep_logcwmin << 4 | + chanp[WME_AC_BE].wmep_logcwmin); + if (error) + goto print_err; + error = rum_write(sc, RT2573_CWMAX_CSR, + chanp[WME_AC_VO].wmep_logcwmax << 12 | + chanp[WME_AC_VI].wmep_logcwmax << 8 | + chanp[WME_AC_BK].wmep_logcwmax << 4 | + chanp[WME_AC_BE].wmep_logcwmax); + if (error) + goto print_err; + error = rum_write(sc, RT2573_TXOP01_CSR, + chanp[WME_AC_BK].wmep_txopLimit << 16 | + chanp[WME_AC_BE].wmep_txopLimit); + if (error) + goto print_err; + error = rum_write(sc, RT2573_TXOP23_CSR, + chanp[WME_AC_VO].wmep_txopLimit << 16 | + chanp[WME_AC_VI].wmep_txopLimit); + if (error) + goto print_err; + + memcpy(sc->wme_params, chanp, sizeof(*chanp) * WME_NUM_AC); + +print_err: + RUM_UNLOCK(sc); + if (error != 0) { + device_printf(sc->sc_dev, "%s: WME update failed, error %d\n", + __func__, error); + } + + return (error); +} + +static void +rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid) +{ + + rum_write(sc, RT2573_MAC_CSR4, + bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); + rum_write(sc, RT2573_MAC_CSR5, + bssid[4] | bssid[5] << 8 | RT2573_NUM_BSSID_MSK(1)); +} + +static void +rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr) +{ + + rum_write(sc, RT2573_MAC_CSR2, + addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + rum_write(sc, RT2573_MAC_CSR3, + addr[4] | addr[5] << 8 | 0xff << 16); +} + +static void +rum_setpromisc(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (ic->ic_promisc == 0) + rum_setbits(sc, RT2573_TXRX_CSR0, RT2573_DROP_NOT_TO_ME); + else + rum_clrbits(sc, RT2573_TXRX_CSR0, RT2573_DROP_NOT_TO_ME); + + DPRINTF("%s promiscuous mode\n", ic->ic_promisc > 0 ? + "entering" : "leaving"); +} + +static void +rum_update_promisc(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + + RUM_LOCK(sc); + if (sc->sc_running) + rum_setpromisc(sc); + RUM_UNLOCK(sc); +} + +static void +rum_update_mcast(struct ieee80211com *ic) +{ + /* Ignore. */ +} + +static const char * +rum_get_rf(int rev) +{ + switch (rev) { + case RT2573_RF_2527: return "RT2527 (MIMO XR)"; + case RT2573_RF_2528: return "RT2528"; + case RT2573_RF_5225: return "RT5225 (MIMO XR)"; + case RT2573_RF_5226: return "RT5226"; + default: return "unknown"; + } +} + +static void +rum_read_eeprom(struct rum_softc *sc) +{ + uint16_t val; +#ifdef RUM_DEBUG + int i; +#endif + + /* read MAC address */ + rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_ic.ic_macaddr, 6); + + rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2); + val = le16toh(val); + sc->rf_rev = (val >> 11) & 0x1f; + sc->hw_radio = (val >> 10) & 0x1; + sc->rx_ant = (val >> 4) & 0x3; + sc->tx_ant = (val >> 2) & 0x3; + sc->nb_ant = val & 0x3; + + DPRINTF("RF revision=%d\n", sc->rf_rev); + + rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2); + val = le16toh(val); + sc->ext_5ghz_lna = (val >> 6) & 0x1; + sc->ext_2ghz_lna = (val >> 4) & 0x1; + + DPRINTF("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n", + sc->ext_2ghz_lna, sc->ext_5ghz_lna); + + rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ + + /* Only [-10, 10] is valid */ + if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10) + sc->rssi_2ghz_corr = 0; + + rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ + + /* Only [-10, 10] is valid */ + if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10) + sc->rssi_5ghz_corr = 0; + + if (sc->ext_2ghz_lna) + sc->rssi_2ghz_corr -= 14; + if (sc->ext_5ghz_lna) + sc->rssi_5ghz_corr -= 14; + + DPRINTF("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n", + sc->rssi_2ghz_corr, sc->rssi_5ghz_corr); + + rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rffreq = val & 0xff; + + DPRINTF("RF freq=%d\n", sc->rffreq); + + /* read Tx power for all a/b/g channels */ + rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14); + /* XXX default Tx power for 802.11a channels */ + memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14); +#ifdef RUM_DEBUG + for (i = 0; i < 14; i++) + DPRINTF("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i]); +#endif + + /* read default values for BBP registers */ + rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); +#ifdef RUM_DEBUG + for (i = 0; i < 14; i++) { + if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) + continue; + DPRINTF("BBP R%d=%02x\n", sc->bbp_prom[i].reg, + sc->bbp_prom[i].val); + } +#endif +} + +static int +rum_bbp_wakeup(struct rum_softc *sc) +{ + unsigned int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (rum_read(sc, RT2573_MAC_CSR12) & 8) + break; + rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for BBP/RF to wakeup\n"); + return (ETIMEDOUT); + } + + return (0); +} + +static int +rum_bbp_init(struct rum_softc *sc) +{ + int i, ntries; + + /* wait for BBP to be ready */ + for (ntries = 0; ntries < 100; ntries++) { + const uint8_t val = rum_bbp_read(sc, 0); + if (val != 0 && val != 0xff) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for BBP\n"); + return EIO; + } + + /* initialize BBP registers to default values */ + for (i = 0; i < nitems(rum_def_bbp); i++) + rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val); + + /* write vendor-specific BBP values (from EEPROM) */ + for (i = 0; i < 16; i++) { + if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) + continue; + rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); + } + + return 0; +} + +static void +rum_clr_shkey_regs(struct rum_softc *sc) +{ + rum_write(sc, RT2573_SEC_CSR0, 0); + rum_write(sc, RT2573_SEC_CSR1, 0); + rum_write(sc, RT2573_SEC_CSR5, 0); +} + +static int +rum_init(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + int i, ret; + + RUM_LOCK(sc); + if (sc->sc_running) { + ret = 0; + goto end; + } + + /* initialize MAC registers to default values */ + for (i = 0; i < nitems(rum_def_mac); i++) + rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); + + /* reset some WME parameters to default values */ + sc->wme_params[0].wmep_aifsn = 2; + sc->wme_params[0].wmep_logcwmin = 4; + sc->wme_params[0].wmep_logcwmax = 10; + + /* set host ready */ + rum_write(sc, RT2573_MAC_CSR1, RT2573_RESET_ASIC | RT2573_RESET_BBP); + rum_write(sc, RT2573_MAC_CSR1, 0); + + /* wait for BBP/RF to wakeup */ + if ((ret = rum_bbp_wakeup(sc)) != 0) + goto end; + + if ((ret = rum_bbp_init(sc)) != 0) + goto end; + + /* select default channel */ + rum_select_band(sc, ic->ic_curchan); + rum_select_antenna(sc); + rum_set_chan(sc, ic->ic_curchan); + + /* clear STA registers */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); + + /* clear security registers (if required) */ + if (sc->sc_clr_shkeys == 0) { + rum_clr_shkey_regs(sc); + sc->sc_clr_shkeys = 1; + } + + rum_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + + /* initialize ASIC */ + rum_write(sc, RT2573_MAC_CSR1, RT2573_HOST_READY); + + /* + * Allocate Tx and Rx xfer queues. + */ + rum_setup_tx_list(sc); + + /* update Rx filter */ + tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff; + + tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | + RT2573_DROP_ACKCTS; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + tmp |= RT2573_DROP_TODS; + if (ic->ic_promisc == 0) + tmp |= RT2573_DROP_NOT_TO_ME; + } + rum_write(sc, RT2573_TXRX_CSR0, tmp); + + sc->sc_running = 1; + usbd_xfer_set_stall(sc->sc_xfer[RUM_BULK_WR]); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_RD]); + +end: RUM_UNLOCK(sc); + + if (ret != 0) + rum_stop(sc); + + return ret; +} + +static void +rum_stop(struct rum_softc *sc) +{ + + RUM_LOCK(sc); + if (!sc->sc_running) { + RUM_UNLOCK(sc); + return; + } + sc->sc_running = 0; + RUM_UNLOCK(sc); + + /* + * Drain the USB transfers, if not already drained: + */ + usbd_transfer_drain(sc->sc_xfer[RUM_BULK_WR]); + usbd_transfer_drain(sc->sc_xfer[RUM_BULK_RD]); + + RUM_LOCK(sc); + rum_unsetup_tx_list(sc); + + /* disable Rx */ + rum_setbits(sc, RT2573_TXRX_CSR0, RT2573_DISABLE_RX); + + /* reset ASIC */ + rum_write(sc, RT2573_MAC_CSR1, RT2573_RESET_ASIC | RT2573_RESET_BBP); + rum_write(sc, RT2573_MAC_CSR1, 0); + RUM_UNLOCK(sc); +} + +static void +rum_load_microcode(struct rum_softc *sc, const uint8_t *ucode, size_t size) +{ + uint16_t reg = RT2573_MCU_CODE_BASE; + usb_error_t err; + + /* copy firmware image into NIC */ + for (; size >= 4; reg += 4, ucode += 4, size -= 4) { + err = rum_write(sc, reg, UGETDW(ucode)); + if (err) { + /* firmware already loaded ? */ + device_printf(sc->sc_dev, "Firmware load " + "failure! (ignored)\n"); + break; + } + } + + err = rum_do_mcu_request(sc, RT2573_MCU_RUN); + if (err != USB_ERR_NORMAL_COMPLETION) { + device_printf(sc->sc_dev, "could not run firmware: %s\n", + usbd_errstr(err)); + } + + /* give the chip some time to boot */ + rum_pause(sc, hz / 8); +} + +static int +rum_set_sleep_time(struct rum_softc *sc, uint16_t bintval) +{ + struct ieee80211com *ic = &sc->sc_ic; + usb_error_t uerror; + int exp, delay; + + RUM_LOCK_ASSERT(sc); + + exp = ic->ic_lintval / bintval; + delay = ic->ic_lintval % bintval; + + if (exp > RT2573_TBCN_EXP_MAX) + exp = RT2573_TBCN_EXP_MAX; + if (delay > RT2573_TBCN_DELAY_MAX) + delay = RT2573_TBCN_DELAY_MAX; + + uerror = rum_modbits(sc, RT2573_MAC_CSR11, + RT2573_TBCN_EXP(exp) | + RT2573_TBCN_DELAY(delay), + RT2573_TBCN_EXP(RT2573_TBCN_EXP_MAX) | + RT2573_TBCN_DELAY(RT2573_TBCN_DELAY_MAX)); + + if (uerror != USB_ERR_NORMAL_COMPLETION) + return (EIO); + + sc->sc_sleep_time = IEEE80211_TU_TO_TICKS(exp * bintval + delay); + + return (0); +} + +static int +rum_reset(struct ieee80211vap *vap, u_long cmd) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + struct rum_softc *sc = ic->ic_softc; + int error; + + switch (cmd) { + case IEEE80211_IOC_POWERSAVE: + case IEEE80211_IOC_PROTMODE: + case IEEE80211_IOC_RTSTHRESHOLD: + error = 0; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + ni = ieee80211_ref_node(vap->iv_bss); + + RUM_LOCK(sc); + error = rum_set_sleep_time(sc, ni->ni_intval); + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* Use new values for wakeup timer. */ + rum_clrbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + rum_setbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + } + /* XXX send reassoc */ + RUM_UNLOCK(sc); + + ieee80211_free_node(ni); + break; + default: + error = ENETRESET; + break; + } + + return (error); +} + +static int +rum_set_beacon(struct rum_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct rum_vap *rvp = RUM_VAP(vap); + struct mbuf *m = rvp->bcn_mbuf; + const struct ieee80211_txparam *tp; + struct rum_tx_desc desc; + + RUM_LOCK_ASSERT(sc); + + if (m == NULL) + return EINVAL; + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) + return EINVAL; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + rum_setup_tx_desc(sc, &desc, NULL, RT2573_TX_TIMESTAMP, + RT2573_TX_HWSEQ, 0, 0, m->m_pkthdr.len, tp->mgmtrate); + + /* copy the Tx descriptor into NIC memory */ + if (rum_write_multi(sc, RT2573_HW_BCN_BASE(0), (uint8_t *)&desc, + RT2573_TX_DESC_SIZE) != 0) + return EIO; + + /* copy beacon header and payload into NIC memory */ + if (rum_write_multi(sc, RT2573_HW_BCN_BASE(0) + RT2573_TX_DESC_SIZE, + mtod(m, uint8_t *), m->m_pkthdr.len) != 0) + return EIO; + + return 0; +} + +static int +rum_alloc_beacon(struct rum_softc *sc, struct ieee80211vap *vap) +{ + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211_node *ni = vap->iv_bss; + struct mbuf *m; + + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return EINVAL; + + m = ieee80211_beacon_alloc(ni); + if (m == NULL) + return ENOMEM; + + if (rvp->bcn_mbuf != NULL) + m_freem(rvp->bcn_mbuf); + + rvp->bcn_mbuf = m; + + return (rum_set_beacon(sc, vap)); +} + +static void +rum_update_beacon_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211vap *vap = data->vap; + + rum_set_beacon(sc, vap); +} + +static void +rum_update_beacon(struct ieee80211vap *vap, int item) +{ + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; + struct ieee80211_node *ni = vap->iv_bss; + struct mbuf *m = rvp->bcn_mbuf; + int mcast = 0; + + RUM_LOCK(sc); + if (m == NULL) { + m = ieee80211_beacon_alloc(ni); + if (m == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate beacon frame\n", __func__); + RUM_UNLOCK(sc); + return; + } + rvp->bcn_mbuf = m; + } + + switch (item) { + case IEEE80211_BEACON_ERP: + rum_update_slot(ic); + break; + case IEEE80211_BEACON_TIM: + mcast = 1; /*TODO*/ + break; + default: + break; + } + RUM_UNLOCK(sc); + + setbit(bo->bo_flags, item); + ieee80211_beacon_update(ni, m, mcast); + + rum_cmd_sleepable(sc, &vap, sizeof(vap), 0, rum_update_beacon_cb); +} + +static int +rum_common_key_set(struct rum_softc *sc, struct ieee80211_key *k, + uint16_t base) +{ + + if (rum_write_multi(sc, base, k->wk_key, k->wk_keylen)) + return EIO; + + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) { + if (rum_write_multi(sc, base + IEEE80211_KEYBUF_SIZE, + k->wk_txmic, 8)) + return EIO; + if (rum_write_multi(sc, base + IEEE80211_KEYBUF_SIZE + 8, + k->wk_rxmic, 8)) + return EIO; + } + + return 0; +} + +static void +rum_group_key_set_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211_key *k = &data->key; + uint8_t mode; + + if (sc->sc_clr_shkeys == 0) { + rum_clr_shkey_regs(sc); + sc->sc_clr_shkeys = 1; + } + + mode = rum_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen); + if (mode == 0) + goto print_err; + + DPRINTFN(1, "setting group key %d for vap %d, mode %d " + "(tx %s, rx %s)\n", k->wk_keyix, rvp_id, mode, + (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off", + (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off"); + + /* Install the key. */ + if (rum_common_key_set(sc, k, RT2573_SKEY(rvp_id, k->wk_keyix)) != 0) + goto print_err; + + /* Set cipher mode. */ + if (rum_modbits(sc, rvp_id < 2 ? RT2573_SEC_CSR1 : RT2573_SEC_CSR5, + mode << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX, + RT2573_MODE_MASK << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX) + != 0) + goto print_err; + + /* Mark this key as valid. */ + if (rum_setbits(sc, RT2573_SEC_CSR0, + 1 << (rvp_id * RT2573_SKEY_MAX + k->wk_keyix)) != 0) + goto print_err; + + return; + +print_err: + device_printf(sc->sc_dev, "%s: cannot set group key %d for vap %d\n", + __func__, k->wk_keyix, rvp_id); +} + +static void +rum_group_key_del_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211_key *k = &data->key; + + DPRINTF("%s: removing group key %d for vap %d\n", __func__, + k->wk_keyix, rvp_id); + rum_clrbits(sc, + rvp_id < 2 ? RT2573_SEC_CSR1 : RT2573_SEC_CSR5, + RT2573_MODE_MASK << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX); + rum_clrbits(sc, RT2573_SEC_CSR0, + rvp_id * RT2573_SKEY_MAX + k->wk_keyix); +} + +static void +rum_pair_key_set_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211_key *k = &data->key; + uint8_t buf[IEEE80211_ADDR_LEN + 1]; + uint8_t mode; + + mode = rum_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen); + if (mode == 0) + goto print_err; + + DPRINTFN(1, "setting pairwise key %d for vap %d, mode %d " + "(tx %s, rx %s)\n", k->wk_keyix, rvp_id, mode, + (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off", + (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off"); + + /* Install the key. */ + if (rum_common_key_set(sc, k, RT2573_PKEY(k->wk_keyix)) != 0) + goto print_err; + + IEEE80211_ADDR_COPY(buf, k->wk_macaddr); + buf[IEEE80211_ADDR_LEN] = mode; + + /* Set transmitter address and cipher mode. */ + if (rum_write_multi(sc, RT2573_ADDR_ENTRY(k->wk_keyix), + buf, sizeof buf) != 0) + goto print_err; + + /* Enable key table lookup for this vap. */ + if (sc->vap_key_count[rvp_id]++ == 0) + if (rum_setbits(sc, RT2573_SEC_CSR4, 1 << rvp_id) != 0) + goto print_err; + + /* Mark this key as valid. */ + if (rum_setbits(sc, + k->wk_keyix < 32 ? RT2573_SEC_CSR2 : RT2573_SEC_CSR3, + 1 << (k->wk_keyix % 32)) != 0) + goto print_err; + + return; + +print_err: + device_printf(sc->sc_dev, + "%s: cannot set pairwise key %d, vap %d\n", __func__, k->wk_keyix, + rvp_id); +} + +static void +rum_pair_key_del_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211_key *k = &data->key; + + DPRINTF("%s: removing key %d\n", __func__, k->wk_keyix); + rum_clrbits(sc, (k->wk_keyix < 32) ? RT2573_SEC_CSR2 : RT2573_SEC_CSR3, + 1 << (k->wk_keyix % 32)); + sc->keys_bmap &= ~(1ULL << k->wk_keyix); + if (--sc->vap_key_count[rvp_id] == 0) + rum_clrbits(sc, RT2573_SEC_CSR4, 1 << rvp_id); +} + +static int +rum_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) +{ + struct rum_softc *sc = vap->iv_ic->ic_softc; + uint8_t i; + + if (!(&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { + if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { + RUM_LOCK(sc); + for (i = 0; i < RT2573_ADDR_MAX; i++) { + if ((sc->keys_bmap & (1ULL << i)) == 0) { + sc->keys_bmap |= (1ULL << i); + *keyix = i; + break; + } + } + RUM_UNLOCK(sc); + if (i == RT2573_ADDR_MAX) { + device_printf(sc->sc_dev, + "%s: no free space in the key table\n", + __func__); + return 0; + } + } else + *keyix = 0; + } else { + *keyix = ieee80211_crypto_get_key_wepidx(vap, k); + } + *rxkeyix = *keyix; + return 1; +} + +static int +rum_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + struct rum_softc *sc = vap->iv_ic->ic_softc; + int group; + + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + /* Not for us. */ + return 1; + } + + group = k >= &vap->iv_nw_keys[0] && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]; + + return !rum_cmd_sleepable(sc, k, sizeof(*k), 0, + group ? rum_group_key_set_cb : rum_pair_key_set_cb); +} + +static int +rum_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + struct rum_softc *sc = vap->iv_ic->ic_softc; + int group; + + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + /* Not for us. */ + return 1; + } + + group = k >= &vap->iv_nw_keys[0] && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]; + + return !rum_cmd_sleepable(sc, k, sizeof(*k), 0, + group ? rum_group_key_del_cb : rum_pair_key_del_cb); +} + +static int +rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct rum_softc *sc = ni->ni_ic->ic_softc; + int ret; + + RUM_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!sc->sc_running) { + ret = ENETDOWN; + goto bad; + } + if (sc->tx_nfree < RUM_TX_MINFREE) { + ret = EIO; + goto bad; + } + + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + if ((ret = rum_tx_mgt(sc, m, ni)) != 0) + goto bad; + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + if ((ret = rum_tx_raw(sc, m, ni, params)) != 0) + goto bad; + } + RUM_UNLOCK(sc); + + return 0; +bad: + RUM_UNLOCK(sc); + m_freem(m); + return ret; +} + +static void +rum_ratectl_start(struct rum_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct rum_vap *rvp = RUM_VAP(vap); + + /* clear statistic registers (STA_CSR0 to STA_CSR5) */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); + + usb_callout_reset(&rvp->ratectl_ch, hz, rum_ratectl_timeout, rvp); +} + +static void +rum_ratectl_timeout(void *arg) +{ + struct rum_vap *rvp = arg; + struct ieee80211vap *vap = &rvp->vap; + struct ieee80211com *ic = vap->iv_ic; + + ieee80211_runtask(ic, &rvp->ratectl_task); +} + +static void +rum_ratectl_task(void *arg, int pending) +{ + struct rum_vap *rvp = arg; + struct ieee80211vap *vap = &rvp->vap; + struct rum_softc *sc = vap->iv_ic->ic_softc; + struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; + int ok[3], fail; + + RUM_LOCK(sc); + /* read and clear statistic registers (STA_CSR0 to STA_CSR5) */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof(sc->sta)); + + ok[0] = (le32toh(sc->sta[4]) & 0xffff); /* TX ok w/o retry */ + ok[1] = (le32toh(sc->sta[4]) >> 16); /* TX ok w/ one retry */ + ok[2] = (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ multiple retries */ + fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */ + + txs->flags = IEEE80211_RATECTL_TX_STATS_RETRIES; + txs->nframes = ok[0] + ok[1] + ok[2] + fail; + txs->nsuccess = txs->nframes - fail; + /* XXX at least */ + txs->nretries = ok[1] + ok[2] * 2 + fail * (rvp->maxretry + 1); + + if (txs->nframes != 0) + ieee80211_ratectl_tx_update(vap, txs); + + /* count TX retry-fail as Tx errors */ + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, fail); + + usb_callout_reset(&rvp->ratectl_ch, hz, rum_ratectl_timeout, rvp); + RUM_UNLOCK(sc); +} + +static void +rum_scan_start(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + + RUM_LOCK(sc); + rum_abort_tsf_sync(sc); + rum_set_bssid(sc, ieee80211broadcastaddr); + RUM_UNLOCK(sc); + +} + +static void +rum_scan_end(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + + if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { + RUM_LOCK(sc); + if (ic->ic_opmode != IEEE80211_M_AHDEMO) + rum_enable_tsf_sync(sc); + else + rum_enable_tsf(sc); + rum_set_bssid(sc, sc->sc_bssid); + RUM_UNLOCK(sc); + } +} + +static void +rum_set_channel(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + + RUM_LOCK(sc); + rum_set_chan(sc, ic->ic_curchan); + RUM_UNLOCK(sc); +} + +static void +rum_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct rum_softc *sc = ic->ic_softc; + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + rum_chan_2ghz, nitems(rum_chan_2ghz), bands, 0); + + if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) { + setbit(bands, IEEE80211_MODE_11A); + ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, + rum_chan_5ghz, nitems(rum_chan_5ghz), bands, 0); + } +} + +static int +rum_get_rssi(struct rum_softc *sc, uint8_t raw) +{ + struct ieee80211com *ic = &sc->sc_ic; + int lna, agc, rssi; + + lna = (raw >> 5) & 0x3; + agc = raw & 0x1f; + + if (lna == 0) { + /* + * No RSSI mapping + * + * NB: Since RSSI is relative to noise floor, -1 is + * adequate for caller to know error happened. + */ + return -1; + } + + rssi = (2 * agc) - RT2573_NOISE_FLOOR; + + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { + rssi += sc->rssi_2ghz_corr; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 74; + else if (lna == 3) + rssi -= 90; + } else { + rssi += sc->rssi_5ghz_corr; + + if (!sc->ext_5ghz_lna && lna != 1) + rssi += 4; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 86; + else if (lna == 3) + rssi -= 100; + } + return rssi; +} + +static int +rum_pause(struct rum_softc *sc, int timeout) +{ + + usb_pause_mtx(&sc->sc_mtx, timeout); + return (0); +} + +static device_method_t rum_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rum_match), + DEVMETHOD(device_attach, rum_attach), + DEVMETHOD(device_detach, rum_detach), + DEVMETHOD_END +}; + +static driver_t rum_driver = { + .name = "rum", + .methods = rum_methods, + .size = sizeof(struct rum_softc), +}; + +static devclass_t rum_devclass; + +DRIVER_MODULE(rum, uhub, rum_driver, rum_devclass, NULL, 0); +MODULE_DEPEND(rum, wlan, 1, 1, 1); +MODULE_DEPEND(rum, usb, 1, 1, 1); +MODULE_VERSION(rum, 1); +USB_PNP_HOST_INFO(rum_devs); diff --git a/freebsd/sys/dev/usb/wlan/if_rumfw.h b/freebsd/sys/dev/usb/wlan/if_rumfw.h new file mode 100644 index 00000000..0f086744 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_rumfw.h @@ -0,0 +1,213 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005-2006, Ralink Technology, Corp. + * Paul Lin <paul_lin@ralinktech.com.tw> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file contains the loadable 8051 microcode for the Ralink RT2573 + * chipset. + */ + +static const uint8_t rt2573_ucode[] = { + 0x02, 0x13, 0x25, 0x12, 0x10, 0xd9, 0x02, 0x12, 0x58, 0x02, 0x13, + 0x58, 0x02, 0x13, 0x5a, 0xc0, 0xd0, 0x75, 0xd0, 0x18, 0x12, 0x13, + 0x5c, 0xd0, 0xd0, 0x22, 0x02, 0x14, 0x5c, 0x02, 0x14, 0xe7, 0xed, + 0x4c, 0x70, 0x44, 0x90, 0x01, 0xa8, 0x74, 0x80, 0xf0, 0xef, 0x30, + 0xe5, 0x07, 0xe4, 0x90, 0x00, 0x0f, 0xf0, 0x80, 0x2c, 0xe5, 0x40, + 0x24, 0xc0, 0x60, 0x13, 0x24, 0xc0, 0x60, 0x16, 0x24, 0xc0, 0x60, + 0x19, 0x24, 0xc0, 0x70, 0x1a, 0xe4, 0x90, 0x00, 0x0b, 0xf0, 0x80, + 0x13, 0xe4, 0x90, 0x00, 0x13, 0xf0, 0x80, 0x0c, 0xe4, 0x90, 0x00, + 0x1b, 0xf0, 0x80, 0x05, 0xe4, 0x90, 0x00, 0x23, 0xf0, 0xe4, 0x90, + 0x01, 0xa8, 0xf0, 0xd3, 0x22, 0x90, 0x02, 0x02, 0xed, 0xf0, 0x90, + 0x02, 0x01, 0xef, 0xf0, 0xd3, 0x22, 0xef, 0x24, 0xc0, 0x60, 0x1f, + 0x24, 0xc0, 0x60, 0x2e, 0x24, 0xc0, 0x60, 0x3d, 0x24, 0xc0, 0x70, + 0x53, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90, + 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00, 0x08, 0x80, 0x37, 0x90, 0x00, + 0x13, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x11, 0xe0, + 0xfe, 0x90, 0x00, 0x10, 0x80, 0x24, 0x90, 0x00, 0x1b, 0xe0, 0x30, + 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00, + 0x18, 0x80, 0x11, 0x90, 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x02, 0xc3, + 0x22, 0x90, 0x00, 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd, + 0xee, 0xf5, 0x37, 0xed, 0xf5, 0x38, 0xd3, 0x22, 0x30, 0x09, 0x20, + 0x20, 0x04, 0x0b, 0x90, 0x02, 0x08, 0xe0, 0x54, 0x0f, 0x70, 0x03, + 0x02, 0x12, 0x57, 0xc2, 0x09, 0x90, 0x02, 0x00, 0xe0, 0x44, 0x04, + 0xf0, 0x74, 0x04, 0x12, 0x0c, 0x3a, 0xc2, 0x04, 0xc2, 0x07, 0x90, + 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03, + 0x26, 0xe0, 0x20, 0xe2, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x08, + 0xe0, 0x70, 0x1b, 0x20, 0x07, 0x03, 0x02, 0x12, 0x57, 0x90, 0x03, + 0x12, 0xe0, 0x64, 0x22, 0x60, 0x03, 0x02, 0x12, 0x57, 0xd2, 0x09, + 0xc2, 0x07, 0x74, 0x02, 0x12, 0x0c, 0x3a, 0x22, 0x90, 0x02, 0x03, + 0xe0, 0x30, 0xe4, 0x47, 0x20, 0x06, 0x44, 0xe5, 0x3c, 0x60, 0x34, + 0xe5, 0x40, 0x24, 0xc0, 0x60, 0x14, 0x24, 0xc0, 0x60, 0x18, 0x24, + 0xc0, 0x60, 0x1c, 0x24, 0xc0, 0x70, 0x22, 0x90, 0x00, 0x0b, 0xe0, + 0x30, 0xe1, 0x1b, 0x22, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x13, + 0x22, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x0b, 0x22, 0x90, 0x00, + 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x03, + 0x74, 0x01, 0xf0, 0x00, 0xe0, 0x54, 0xc0, 0xf5, 0x40, 0xe5, 0x40, + 0x24, 0xc0, 0x60, 0x20, 0x24, 0xc0, 0x60, 0x30, 0x24, 0xc0, 0x60, + 0x40, 0x24, 0xc0, 0x70, 0x56, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1, + 0x03, 0x02, 0x12, 0x57, 0x90, 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00, + 0x08, 0x80, 0x3a, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x03, 0x02, + 0x12, 0x57, 0x90, 0x00, 0x11, 0xe0, 0xfe, 0x90, 0x00, 0x10, 0x80, + 0x26, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, + 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00, 0x18, 0x80, 0x12, 0x90, + 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x00, + 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd, 0xee, 0xf5, 0x37, + 0xed, 0xf5, 0x38, 0x90, 0x03, 0x27, 0x74, 0x82, 0xf0, 0x90, 0x02, + 0x01, 0xe5, 0x40, 0xf0, 0x90, 0x02, 0x06, 0xe0, 0xf5, 0x3c, 0xc3, + 0xe5, 0x38, 0x95, 0x3a, 0xe5, 0x37, 0x95, 0x39, 0x50, 0x21, 0xe5, + 0x40, 0x44, 0x05, 0xff, 0xe5, 0x37, 0xa2, 0xe7, 0x13, 0xfc, 0xe5, + 0x38, 0x13, 0xfd, 0x12, 0x10, 0x20, 0xe5, 0x3c, 0x30, 0xe2, 0x04, + 0xd2, 0x06, 0x80, 0x02, 0xc2, 0x06, 0x53, 0x3c, 0x01, 0x22, 0x30, + 0x0b, 0x07, 0xe4, 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02, + 0x02, 0x74, 0x20, 0xf0, 0xe5, 0x40, 0x44, 0x01, 0x90, 0x02, 0x01, + 0xf0, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6, + 0x90, 0x03, 0x27, 0x74, 0x02, 0xf0, 0xaf, 0x40, 0x12, 0x10, 0x74, + 0x40, 0xa5, 0x00, 0x80, 0xf6, 0x22, 0x90, 0x7f, 0xf8, 0xe0, 0xb4, + 0x02, 0x03, 0x12, 0x16, 0x38, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, + 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03, 0x26, 0xe0, 0x20, 0xe1, 0x07, + 0xe5, 0x3b, 0x70, 0x03, 0x02, 0x13, 0x24, 0xe5, 0x3b, 0x70, 0x15, + 0x90, 0x03, 0x24, 0xe0, 0x75, 0xf0, 0x40, 0xa4, 0xf5, 0x36, 0x85, + 0xf0, 0x35, 0x75, 0x24, 0x83, 0x75, 0x3b, 0x01, 0x80, 0x03, 0x75, + 0x24, 0x03, 0xd3, 0xe5, 0x36, 0x95, 0x3a, 0xe5, 0x35, 0x95, 0x39, + 0x40, 0x36, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, + 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0, + 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x30, 0x0b, 0x07, 0xe4, + 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02, 0x02, 0x74, 0x20, + 0xf0, 0x90, 0x02, 0x01, 0x74, 0x21, 0xf0, 0x75, 0x24, 0x03, 0x80, + 0x3d, 0xe5, 0x35, 0xa2, 0xe7, 0x13, 0xfe, 0xe5, 0x36, 0x13, 0xfd, + 0xac, 0x06, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, + 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0, + 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x7f, 0x25, 0x12, 0x10, + 0x20, 0xe5, 0x36, 0xb5, 0x3a, 0x08, 0xe5, 0x35, 0xb5, 0x39, 0x03, + 0x00, 0x80, 0x04, 0xe4, 0xf5, 0x3b, 0x22, 0xc3, 0xe5, 0x36, 0x95, + 0x3a, 0xf5, 0x36, 0xe5, 0x35, 0x95, 0x39, 0xf5, 0x35, 0x02, 0x12, + 0x96, 0x22, 0x75, 0xa8, 0x0f, 0x90, 0x03, 0x06, 0x74, 0x01, 0xf0, + 0x90, 0x03, 0x07, 0xf0, 0x90, 0x03, 0x08, 0x04, 0xf0, 0x90, 0x03, + 0x09, 0x74, 0x6c, 0xf0, 0x90, 0x03, 0x0a, 0x74, 0xff, 0xf0, 0x90, + 0x03, 0x02, 0x74, 0x1f, 0xf0, 0x90, 0x03, 0x00, 0x74, 0x04, 0xf0, + 0x90, 0x03, 0x25, 0x74, 0x31, 0xf0, 0xd2, 0xaf, 0x22, 0x00, 0x22, + 0x00, 0x22, 0x90, 0x03, 0x05, 0xe0, 0x30, 0xe0, 0x0b, 0xe0, 0x44, + 0x01, 0xf0, 0x30, 0x09, 0x02, 0xd2, 0x04, 0xc2, 0x07, 0x22, 0x8d, + 0x24, 0xa9, 0x07, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5, + 0x26, 0xa3, 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff, + 0xa3, 0xe0, 0xfd, 0xe9, 0x30, 0xe5, 0x14, 0x54, 0xc0, 0x60, 0x05, + 0x43, 0x05, 0x03, 0x80, 0x03, 0x53, 0x05, 0xfc, 0xef, 0x54, 0x3f, + 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x07, 0x3f, 0x53, 0x05, 0xf0, + 0xe5, 0x24, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53, + 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28, + 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x8f, 0x24, 0xa9, + 0x05, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5, 0x26, 0xa3, + 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff, 0xa3, 0xe0, + 0xfd, 0xe5, 0x24, 0x30, 0xe5, 0x0b, 0x43, 0x05, 0x0f, 0xef, 0x54, + 0x3f, 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x05, 0xf0, 0x53, 0x07, + 0x3f, 0xe9, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53, + 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28, + 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x90, 0x7f, 0xfc, + 0xe0, 0xf9, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfb, + 0xef, 0x30, 0xe5, 0x0b, 0x43, 0x03, 0x0f, 0xec, 0x54, 0x3f, 0x44, + 0x40, 0xfc, 0x80, 0x06, 0x53, 0x03, 0xf0, 0x53, 0x04, 0x3f, 0xed, + 0x30, 0xe0, 0x07, 0xef, 0x54, 0xc0, 0x60, 0x07, 0x80, 0x0a, 0xef, + 0x54, 0xc0, 0x60, 0x05, 0x43, 0x03, 0x10, 0x80, 0x03, 0x53, 0x03, + 0xef, 0x90, 0x7f, 0xfc, 0xe9, 0xf0, 0xa3, 0xee, 0xf0, 0xa3, 0xec, + 0xf0, 0xa3, 0xeb, 0xf0, 0x22, 0xe5, 0x4b, 0xfd, 0x54, 0x1f, 0x90, + 0x7f, 0xf8, 0xf0, 0xe5, 0x4a, 0xf5, 0x09, 0x90, 0x30, 0x38, 0xe0, + 0x90, 0x7f, 0xfc, 0xf0, 0x90, 0x30, 0x39, 0xe0, 0x90, 0x7f, 0xfd, + 0xf0, 0x90, 0x30, 0x3a, 0xe0, 0x90, 0x7f, 0xfe, 0xf0, 0x90, 0x30, + 0x3b, 0xe0, 0x90, 0x7f, 0xff, 0xf0, 0xed, 0x30, 0xe5, 0x0c, 0x54, + 0xc0, 0x60, 0x0d, 0x90, 0x7f, 0xf0, 0xe5, 0x47, 0xf0, 0x80, 0x05, + 0xe4, 0x90, 0x7f, 0xf0, 0xf0, 0x90, 0x7f, 0xf8, 0xe0, 0x14, 0x60, + 0x08, 0x24, 0xfe, 0x60, 0x0d, 0x24, 0x03, 0x80, 0x12, 0xaf, 0x05, + 0xad, 0x09, 0x12, 0x13, 0xc5, 0x80, 0x10, 0xaf, 0x05, 0xad, 0x09, + 0x12, 0x14, 0x12, 0x80, 0x07, 0xaf, 0x05, 0xad, 0x09, 0x12, 0x13, + 0x6f, 0x90, 0x7f, 0xfc, 0xe0, 0x90, 0x30, 0x38, 0xf0, 0x90, 0x7f, + 0xfd, 0xe0, 0x90, 0x30, 0x39, 0xf0, 0x90, 0x7f, 0xfe, 0xe0, 0x90, + 0x30, 0x3a, 0xf0, 0x90, 0x7f, 0xff, 0xe0, 0x90, 0x30, 0x3b, 0xf0, + 0x22, 0xe5, 0x4b, 0x64, 0x01, 0x60, 0x03, 0x02, 0x15, 0x71, 0xf5, + 0x4b, 0xe5, 0x44, 0x45, 0x43, 0x70, 0x03, 0x02, 0x15, 0xa0, 0x12, + 0x0c, 0x14, 0x12, 0x0b, 0x86, 0x50, 0xfb, 0x90, 0x00, 0x00, 0xe0, + 0xf5, 0x25, 0x12, 0x15, 0xb4, 0xc2, 0x92, 0xe4, 0xf5, 0x24, 0xe5, + 0x24, 0xc3, 0x95, 0x25, 0x50, 0x49, 0x7e, 0x00, 0x7f, 0x4c, 0x74, + 0x40, 0x25, 0x24, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xad, 0x82, 0xfc, + 0x75, 0x2b, 0x02, 0x7b, 0x10, 0x12, 0x07, 0x1e, 0xc2, 0x93, 0x12, + 0x15, 0xa1, 0x7d, 0xa0, 0x12, 0x15, 0xd0, 0xe5, 0x24, 0x54, 0x0f, + 0x24, 0x4c, 0xf8, 0xe6, 0xfd, 0xaf, 0x4b, 0xae, 0x4a, 0x12, 0x15, + 0xd8, 0x05, 0x4b, 0xe5, 0x4b, 0x70, 0x02, 0x05, 0x4a, 0x12, 0x0a, + 0x5f, 0x05, 0x24, 0xe5, 0x24, 0x54, 0x0f, 0x70, 0xd5, 0xd2, 0x93, + 0x80, 0xb0, 0xc3, 0xe5, 0x44, 0x95, 0x25, 0xf5, 0x44, 0xe5, 0x43, + 0x94, 0x00, 0xf5, 0x43, 0x02, 0x14, 0xf2, 0x12, 0x15, 0xb4, 0xc2, + 0x93, 0xc2, 0x92, 0x12, 0x15, 0xa1, 0x7d, 0x80, 0x12, 0x15, 0xd0, + 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55, + 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x7d, 0x30, 0xaf, 0x4b, + 0xae, 0x4a, 0x12, 0x15, 0xd8, 0x12, 0x0a, 0x5f, 0xd2, 0x93, 0x22, + 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55, + 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x22, 0xad, 0x47, 0x7f, + 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0x7d, 0xff, 0x7f, 0x35, 0x7e, + 0x30, 0x12, 0x15, 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x7e, 0x30, 0x12, + 0x15, 0xd8, 0x22, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x22, + 0x8f, 0x82, 0x8e, 0x83, 0xed, 0xf0, 0x22, 0xe4, 0xfc, 0x90, 0x7f, + 0xf0, 0xe0, 0xaf, 0x09, 0x14, 0x60, 0x14, 0x14, 0x60, 0x15, 0x14, + 0x60, 0x16, 0x14, 0x60, 0x17, 0x14, 0x60, 0x18, 0x24, 0x05, 0x70, + 0x16, 0xe4, 0xfc, 0x80, 0x12, 0x7c, 0x01, 0x80, 0x0e, 0x7c, 0x03, + 0x80, 0x0a, 0x7c, 0x07, 0x80, 0x06, 0x7c, 0x0f, 0x80, 0x02, 0x7c, + 0x1f, 0xec, 0x6f, 0xf4, 0x54, 0x1f, 0xfc, 0x90, 0x30, 0x34, 0xe0, + 0x54, 0xe0, 0x4c, 0xfd, 0xa3, 0xe0, 0xfc, 0x43, 0x04, 0x1f, 0x7f, + 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0xad, 0x04, 0x0f, 0x12, 0x15, + 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x02, 0x15, 0xd8, 0x02, 0x15, 0xdf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, + 0x29, 0xe9 +}; diff --git a/freebsd/sys/dev/usb/wlan/if_rumreg.h b/freebsd/sys/dev/usb/wlan/if_rumreg.h new file mode 100644 index 00000000..96726b14 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_rumreg.h @@ -0,0 +1,307 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define RT2573_NOISE_FLOOR -95 + +#define RT2573_TX_DESC_SIZE (sizeof (struct rum_tx_desc)) +#define RT2573_RX_DESC_SIZE (sizeof (struct rum_rx_desc)) + +#define RT2573_CONFIG_NO 1 +#define RT2573_IFACE_INDEX 0 + +#define RT2573_MCU_CNTL 0x01 +#define RT2573_WRITE_MAC 0x02 +#define RT2573_READ_MAC 0x03 +#define RT2573_WRITE_MULTI_MAC 0x06 +#define RT2573_READ_MULTI_MAC 0x07 +#define RT2573_READ_EEPROM 0x09 +#define RT2573_WRITE_LED 0x0a + +/* + * WME registers. + */ +#define RT2573_AIFSN_CSR 0x0400 +#define RT2573_CWMIN_CSR 0x0404 +#define RT2573_CWMAX_CSR 0x0408 +#define RT2573_TXOP01_CSR 0x040C +#define RT2573_TXOP23_CSR 0x0410 +#define RT2573_MCU_CODE_BASE 0x0800 + +/* + * H/w encryption/decryption support + */ +#define KEY_SIZE (IEEE80211_KEYBUF_SIZE + IEEE80211_MICBUF_SIZE) +#define RT2573_ADDR_MAX 64 +#define RT2573_SKEY_MAX 4 + +#define RT2573_SKEY(vap, kidx) (0x1000 + ((vap) * RT2573_SKEY_MAX + \ + (kidx)) * KEY_SIZE) +#define RT2573_PKEY(id) (0x1200 + (id) * KEY_SIZE) + +#define RT2573_ADDR_ENTRY(id) (0x1a00 + (id) * 8) + +/* + * Shared memory area + */ +#define RT2573_HW_BCN_BASE(id) (0x2400 + (id) * 0x100) + +/* + * Control and status registers. + */ +#define RT2573_MAC_CSR0 0x3000 +#define RT2573_MAC_CSR1 0x3004 +#define RT2573_MAC_CSR2 0x3008 +#define RT2573_MAC_CSR3 0x300c +#define RT2573_MAC_CSR4 0x3010 +#define RT2573_MAC_CSR5 0x3014 +#define RT2573_MAC_CSR6 0x3018 +#define RT2573_MAC_CSR7 0x301c +#define RT2573_MAC_CSR8 0x3020 +#define RT2573_MAC_CSR9 0x3024 +#define RT2573_MAC_CSR10 0x3028 +#define RT2573_MAC_CSR11 0x302c +#define RT2573_MAC_CSR12 0x3030 +#define RT2573_MAC_CSR13 0x3034 +#define RT2573_MAC_CSR14 0x3038 +#define RT2573_MAC_CSR15 0x303c +#define RT2573_TXRX_CSR0 0x3040 +#define RT2573_TXRX_CSR1 0x3044 +#define RT2573_TXRX_CSR2 0x3048 +#define RT2573_TXRX_CSR3 0x304c +#define RT2573_TXRX_CSR4 0x3050 +#define RT2573_TXRX_CSR5 0x3054 +#define RT2573_TXRX_CSR6 0x3058 +#define RT2573_TXRX_CSR7 0x305c +#define RT2573_TXRX_CSR8 0x3060 +#define RT2573_TXRX_CSR9 0x3064 +#define RT2573_TXRX_CSR10 0x3068 +#define RT2573_TXRX_CSR11 0x306c +#define RT2573_TXRX_CSR12 0x3070 +#define RT2573_TXRX_CSR13 0x3074 +#define RT2573_TXRX_CSR14 0x3078 +#define RT2573_TXRX_CSR15 0x307c +#define RT2573_PHY_CSR0 0x3080 +#define RT2573_PHY_CSR1 0x3084 +#define RT2573_PHY_CSR2 0x3088 +#define RT2573_PHY_CSR3 0x308c +#define RT2573_PHY_CSR4 0x3090 +#define RT2573_PHY_CSR5 0x3094 +#define RT2573_PHY_CSR6 0x3098 +#define RT2573_PHY_CSR7 0x309c +#define RT2573_SEC_CSR0 0x30a0 +#define RT2573_SEC_CSR1 0x30a4 +#define RT2573_SEC_CSR2 0x30a8 +#define RT2573_SEC_CSR3 0x30ac +#define RT2573_SEC_CSR4 0x30b0 +#define RT2573_SEC_CSR5 0x30b4 +#define RT2573_STA_CSR0 0x30c0 +#define RT2573_STA_CSR1 0x30c4 +#define RT2573_STA_CSR2 0x30c8 +#define RT2573_STA_CSR3 0x30cc +#define RT2573_STA_CSR4 0x30d0 +#define RT2573_STA_CSR5 0x30d4 + + +/* possible values for register RT2573_ADDR_MODE */ +#define RT2573_MODE_MASK 0x7 +#define RT2573_MODE_NOSEC 0 +#define RT2573_MODE_WEP40 1 +#define RT2573_MODE_WEP104 2 +#define RT2573_MODE_TKIP 3 +#define RT2573_MODE_AES_CCMP 4 +#define RT2573_MODE_CKIP40 5 +#define RT2573_MODE_CKIP104 6 + +/* possible flags for register RT2573_MAC_CSR1 */ +#define RT2573_RESET_ASIC (1 << 0) +#define RT2573_RESET_BBP (1 << 1) +#define RT2573_HOST_READY (1 << 2) + +/* possible flags for register MAC_CSR5 */ +#define RT2573_NUM_BSSID_MSK(n) (((n * 3) & 3) << 16) + +/* possible flags for register MAC_CSR11 */ +#define RT2573_AUTO_WAKEUP (1 << 15) +#define RT2573_TBCN_EXP(n) ((n) << 8) +#define RT2573_TBCN_EXP_MAX 0x7f +#define RT2573_TBCN_DELAY(t) (t) +#define RT2573_TBCN_DELAY_MAX 0xff + +/* possible flags for register TXRX_CSR0 */ +/* Tx filter flags are in the low 16 bits */ +#define RT2573_AUTO_TX_SEQ (1 << 15) +/* Rx filter flags are in the high 16 bits */ +#define RT2573_DISABLE_RX (1 << 16) +#define RT2573_DROP_CRC_ERROR (1 << 17) +#define RT2573_DROP_PHY_ERROR (1 << 18) +#define RT2573_DROP_CTL (1 << 19) +#define RT2573_DROP_NOT_TO_ME (1 << 20) +#define RT2573_DROP_TODS (1 << 21) +#define RT2573_DROP_VER_ERROR (1 << 22) +#define RT2573_DROP_MULTICAST (1 << 23) +#define RT2573_DROP_BROADCAST (1 << 24) +#define RT2573_DROP_ACKCTS (1 << 25) + +/* possible flags for register TXRX_CSR4 */ +#define RT2573_ACKCTS_PWRMGT (1 << 16) +#define RT2573_SHORT_PREAMBLE (1 << 18) +#define RT2573_MRR_ENABLED (1 << 19) +#define RT2573_MRR_CCK_FALLBACK (1 << 22) +#define RT2573_LONG_RETRY(max) ((max) << 24) +#define RT2573_LONG_RETRY_MASK (0xf << 24) +#define RT2573_SHORT_RETRY(max) ((max) << 28) +#define RT2573_SHORT_RETRY_MASK (0xf << 28) + +/* possible flags for register TXRX_CSR9 */ +#define RT2573_TSF_TIMER_EN (1 << 16) +#define RT2573_TSF_SYNC_MODE(x) (((x) & 0x3) << 17) +#define RT2573_TSF_SYNC_MODE_DIS 0 +#define RT2573_TSF_SYNC_MODE_STA 1 +#define RT2573_TSF_SYNC_MODE_IBSS 2 +#define RT2573_TSF_SYNC_MODE_HOSTAP 3 +#define RT2573_TBTT_TIMER_EN (1 << 19) +#define RT2573_BCN_TX_EN (1 << 20) + +/* possible flags for register PHY_CSR0 */ +#define RT2573_PA_PE_2GHZ (1 << 16) +#define RT2573_PA_PE_5GHZ (1 << 17) + +/* possible flags for register PHY_CSR3 */ +#define RT2573_BBP_READ (1 << 15) +#define RT2573_BBP_BUSY (1 << 16) +/* possible flags for register PHY_CSR4 */ +#define RT2573_RF_20BIT (20 << 24) +#define RT2573_RF_BUSY (1U << 31) + +/* LED values */ +#define RT2573_LED_RADIO (1 << 8) +#define RT2573_LED_G (1 << 9) +#define RT2573_LED_A (1 << 10) +#define RT2573_LED_ON 0x1e1e +#define RT2573_LED_OFF 0x0 + +/* USB vendor requests */ +#define RT2573_MCU_SLEEP 7 +#define RT2573_MCU_RUN 8 +#define RT2573_MCU_WAKEUP 9 + +#define RT2573_SMART_MODE (1 << 0) + +#define RT2573_BBPR94_DEFAULT 6 + +#define RT2573_BBP_WRITE (1 << 15) + +/* dual-band RF */ +#define RT2573_RF_5226 1 +#define RT2573_RF_5225 3 +/* single-band RF */ +#define RT2573_RF_2528 2 +#define RT2573_RF_2527 4 + +#define RT2573_BBP_VERSION 0 + +struct rum_tx_desc { + uint32_t flags; +#define RT2573_TX_BURST (1 << 0) +#define RT2573_TX_VALID (1 << 1) +#define RT2573_TX_MORE_FRAG (1 << 2) +#define RT2573_TX_NEED_ACK (1 << 3) +#define RT2573_TX_TIMESTAMP (1 << 4) +#define RT2573_TX_OFDM (1 << 5) +#define RT2573_TX_IFS_SIFS (1 << 6) +#define RT2573_TX_LONG_RETRY (1 << 7) +#define RT2573_TX_TKIPMIC (1 << 8) +#define RT2573_TX_KEY_PAIR (1 << 9) +#define RT2573_TX_KEY_ID(id) (((id) & 0x3f) << 10) +#define RT2573_TX_CIP_MODE(m) ((m) << 29) + + uint16_t wme; +#define RT2573_QID(v) (v) +#define RT2573_AIFSN(v) ((v) << 4) +#define RT2573_LOGCWMIN(v) ((v) << 8) +#define RT2573_LOGCWMAX(v) ((v) << 12) + + uint8_t hdrlen; + uint8_t xflags; +#define RT2573_TX_HWSEQ (1 << 4) + + uint8_t plcp_signal; + uint8_t plcp_service; +#define RT2573_PLCP_LENGEXT 0x80 + + uint8_t plcp_length_lo; + uint8_t plcp_length_hi; + + uint32_t iv; + uint32_t eiv; + + uint8_t offset; + uint8_t qid; + uint8_t txpower; +#define RT2573_DEFAULT_TXPOWER 0 + + uint8_t reserved; +} __packed; + +struct rum_rx_desc { + uint32_t flags; +#define RT2573_RX_BUSY (1 << 0) +#define RT2573_RX_DROP (1 << 1) +#define RT2573_RX_UC2ME (1 << 2) +#define RT2573_RX_MC (1 << 3) +#define RT2573_RX_BC (1 << 4) +#define RT2573_RX_MYBSS (1 << 5) +#define RT2573_RX_CRC_ERROR (1 << 6) +#define RT2573_RX_OFDM (1 << 7) + +#define RT2573_RX_DEC_MASK (3 << 8) +#define RT2573_RX_DEC_OK (0 << 8) + +#define RT2573_RX_IV_ERROR (1 << 8) +#define RT2573_RX_MIC_ERROR (2 << 8) +#define RT2573_RX_KEY_ERROR (3 << 8) + +#define RT2573_RX_KEY_PAIR (1 << 28) + +#define RT2573_RX_CIP_MASK (7 << 29) +#define RT2573_RX_CIP_MODE(m) ((m) << 29) + + uint8_t rate; + uint8_t rssi; + uint8_t reserved1; + uint8_t offset; + uint32_t iv; + uint32_t eiv; + uint32_t reserved2[2]; +} __packed; + +#define RT2573_RF1 0 +#define RT2573_RF2 2 +#define RT2573_RF3 1 +#define RT2573_RF4 3 + +#define RT2573_EEPROM_MACBBP 0x0000 +#define RT2573_EEPROM_ADDRESS 0x0004 +#define RT2573_EEPROM_ANTENNA 0x0020 +#define RT2573_EEPROM_CONFIG2 0x0022 +#define RT2573_EEPROM_BBP_BASE 0x0026 +#define RT2573_EEPROM_TXPOWER 0x0046 +#define RT2573_EEPROM_FREQ_OFFSET 0x005e +#define RT2573_EEPROM_RSSI_2GHZ_OFFSET 0x009a +#define RT2573_EEPROM_RSSI_5GHZ_OFFSET 0x009c diff --git a/freebsd/sys/dev/usb/wlan/if_rumvar.h b/freebsd/sys/dev/usb/wlan/if_rumvar.h new file mode 100644 index 00000000..4ff831f4 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_rumvar.h @@ -0,0 +1,186 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define RUM_TX_LIST_COUNT 8 +#define RUM_TX_MINFREE 2 + +struct rum_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint64_t wr_tsf; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_antsignal; + int8_t wr_antnoise; + uint8_t wr_antenna; +} __packed __aligned(8); + +#define RT2573_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + 0) + +struct rum_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; + uint8_t wt_antenna; +} __packed __aligned(8); + +#define RT2573_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA)) + +struct rum_softc; + +struct rum_tx_data { + STAILQ_ENTRY(rum_tx_data) next; + struct rum_softc *sc; + struct rum_tx_desc desc; + struct mbuf *m; + struct ieee80211_node *ni; + int rate; +}; +typedef STAILQ_HEAD(, rum_tx_data) rum_txdhead; + +union sec_param { + struct ieee80211_key key; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + struct ieee80211vap *vap; +}; +#define CMD_FUNC_PROTO void (*func)(struct rum_softc *, \ + union sec_param *, uint8_t) + +struct rum_cmdq { + union sec_param data; + uint8_t rvp_id; + + CMD_FUNC_PROTO; +}; +#define RUM_CMDQ_SIZE 16 + +struct rum_vap { + struct ieee80211vap vap; + struct mbuf *bcn_mbuf; + struct usb_callout ratectl_ch; + struct task ratectl_task; + uint8_t maxretry; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); + void (*bmiss)(struct ieee80211vap *); + void (*recv_mgmt)(struct ieee80211_node *, + struct mbuf *, int, + const struct ieee80211_rx_stats *, + int, int); +}; +#define RUM_VAP(vap) ((struct rum_vap *)(vap)) + +enum { + RUM_BULK_WR, + RUM_BULK_RD, + RUM_N_TRANSFER = 2, +}; + +struct rum_softc { + struct ieee80211com sc_ic; + struct ieee80211_ratectl_tx_stats sc_txs; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + + struct usb_xfer *sc_xfer[RUM_N_TRANSFER]; + + uint8_t rf_rev; + uint8_t rffreq; + + struct rum_tx_data tx_data[RUM_TX_LIST_COUNT]; + rum_txdhead tx_q; + rum_txdhead tx_free; + int tx_nfree; + struct rum_rx_desc sc_rx_desc; + + struct mtx sc_mtx; + + int sc_sleep_end; + int sc_sleep_time; + uint8_t last_rx_flags; + + struct rum_cmdq cmdq[RUM_CMDQ_SIZE]; + struct mtx cmdq_mtx; + struct task cmdq_task; + uint8_t cmdq_first; + uint8_t cmdq_last; + + uint32_t sta[6]; + uint32_t rf_regs[4]; + uint8_t txpow[44]; + u_int sc_detached:1, + sc_running:1, + sc_sleeping:1, + sc_clr_shkeys:1; + + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + struct wmeParams wme_params[WME_NUM_AC]; + + uint8_t vap_key_count[1]; + uint64_t keys_bmap; + + struct { + uint8_t val; + uint8_t reg; + } __packed bbp_prom[16]; + + int hw_radio; + int rx_ant; + int tx_ant; + int nb_ant; + int ext_2ghz_lna; + int ext_5ghz_lna; + int rssi_2ghz_corr; + int rssi_5ghz_corr; + uint8_t bbp17; + + struct rum_rx_radiotap_header sc_rxtap; + struct rum_tx_radiotap_header sc_txtap; +}; + +#define RUM_LOCK_INIT(sc) \ + mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->sc_dev), \ + MTX_NETWORK_LOCK, MTX_DEF); +#define RUM_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RUM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RUM_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) +#define RUM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) + +#define RUM_CMDQ_LOCK_INIT(sc) \ + mtx_init(&(sc)->cmdq_mtx, "cmdq lock", NULL, MTX_DEF) +#define RUM_CMDQ_LOCK(sc) mtx_lock(&(sc)->cmdq_mtx) +#define RUM_CMDQ_UNLOCK(sc) mtx_unlock(&(sc)->cmdq_mtx) +#define RUM_CMDQ_LOCK_DESTROY(sc) mtx_destroy(&(sc)->cmdq_mtx) diff --git a/freebsd/sys/dev/usb/wlan/if_run.c b/freebsd/sys/dev/usb/wlan/if_run.c new file mode 100644 index 00000000..9983fce2 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_run.c @@ -0,0 +1,6339 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2008,2010 Damien Bergamini <damien.bergamini@free.fr> + * ported to FreeBSD by Akinori Furukoshi <moonlightakkiy@yahoo.ca> + * USB Consulting, Hans Petter Selasky <hselasky@freebsd.org> + * Copyright (c) 2013-2014 Kevin Lo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2700U/RT2800U/RT3000U/RT3900E chipset driver. + * http://www.ralinktech.com/ + */ + +#include <rtems/bsd/sys/param.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/linker.h> +#include <sys/firmware.h> +#include <sys/kdb.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_ratectl.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR run_debug +#include <dev/usb/usb_debug.h> +#include <dev/usb/usb_msctest.h> + +#include <dev/usb/wlan/if_runreg.h> +#include <dev/usb/wlan/if_runvar.h> + +#ifdef USB_DEBUG +#define RUN_DEBUG +#endif + +#ifdef RUN_DEBUG +int run_debug = 0; +static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW, 0, "USB run"); +SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RWTUN, &run_debug, 0, + "run debug level"); + +enum { + RUN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + RUN_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ + RUN_DEBUG_RECV = 0x00000004, /* basic recv operation */ + RUN_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ + RUN_DEBUG_STATE = 0x00000010, /* 802.11 state transitions */ + RUN_DEBUG_RATE = 0x00000020, /* rate adaptation */ + RUN_DEBUG_USB = 0x00000040, /* usb requests */ + RUN_DEBUG_FIRMWARE = 0x00000080, /* firmware(9) loading debug */ + RUN_DEBUG_BEACON = 0x00000100, /* beacon handling */ + RUN_DEBUG_INTR = 0x00000200, /* ISR */ + RUN_DEBUG_TEMP = 0x00000400, /* temperature calibration */ + RUN_DEBUG_ROM = 0x00000800, /* various ROM info */ + RUN_DEBUG_KEY = 0x00001000, /* crypto keys management */ + RUN_DEBUG_TXPWR = 0x00002000, /* dump Tx power values */ + RUN_DEBUG_RSSI = 0x00004000, /* dump RSSI lookups */ + RUN_DEBUG_RESET = 0x00008000, /* initialization progress */ + RUN_DEBUG_CALIB = 0x00010000, /* calibration progress */ + RUN_DEBUG_CMD = 0x00020000, /* command queue */ + RUN_DEBUG_ANY = 0xffffffff +}; + +#define RUN_DPRINTF(_sc, _m, ...) do { \ + if (run_debug & (_m)) \ + device_printf((_sc)->sc_dev, __VA_ARGS__); \ +} while(0) +#else +#define RUN_DPRINTF(_sc, _m, ...) do { (void) _sc; } while (0) +#endif + +#define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh) + +/* + * Because of LOR in run_key_delete(), use atomic instead. + * '& RUN_CMDQ_MASQ' is to loop cmdq[]. + */ +#define RUN_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & RUN_CMDQ_MASQ) + +static const STRUCT_USB_HOST_ID run_devs[] = { +#define RUN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } +#define RUN_DEV_EJECT(v,p) \ + { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RUN_EJECT) } +#define RUN_EJECT 1 + RUN_DEV(ABOCOM, RT2770), + RUN_DEV(ABOCOM, RT2870), + RUN_DEV(ABOCOM, RT3070), + RUN_DEV(ABOCOM, RT3071), + RUN_DEV(ABOCOM, RT3072), + RUN_DEV(ABOCOM2, RT2870_1), + RUN_DEV(ACCTON, RT2770), + RUN_DEV(ACCTON, RT2870_1), + RUN_DEV(ACCTON, RT2870_2), + RUN_DEV(ACCTON, RT2870_3), + RUN_DEV(ACCTON, RT2870_4), + RUN_DEV(ACCTON, RT2870_5), + RUN_DEV(ACCTON, RT3070), + RUN_DEV(ACCTON, RT3070_1), + RUN_DEV(ACCTON, RT3070_2), + RUN_DEV(ACCTON, RT3070_3), + RUN_DEV(ACCTON, RT3070_4), + RUN_DEV(ACCTON, RT3070_5), + RUN_DEV(AIRTIES, RT3070), + RUN_DEV(ALLWIN, RT2070), + RUN_DEV(ALLWIN, RT2770), + RUN_DEV(ALLWIN, RT2870), + RUN_DEV(ALLWIN, RT3070), + RUN_DEV(ALLWIN, RT3071), + RUN_DEV(ALLWIN, RT3072), + RUN_DEV(ALLWIN, RT3572), + RUN_DEV(AMIGO, RT2870_1), + RUN_DEV(AMIGO, RT2870_2), + RUN_DEV(AMIT, CGWLUSB2GNR), + RUN_DEV(AMIT, RT2870_1), + RUN_DEV(AMIT2, RT2870), + RUN_DEV(ASUS, RT2870_1), + RUN_DEV(ASUS, RT2870_2), + RUN_DEV(ASUS, RT2870_3), + RUN_DEV(ASUS, RT2870_4), + RUN_DEV(ASUS, RT2870_5), + RUN_DEV(ASUS, USBN13), + RUN_DEV(ASUS, RT3070_1), + RUN_DEV(ASUS, USBN66), + RUN_DEV(ASUS, USB_N53), + RUN_DEV(ASUS2, USBN11), + RUN_DEV(AZUREWAVE, RT2870_1), + RUN_DEV(AZUREWAVE, RT2870_2), + RUN_DEV(AZUREWAVE, RT3070_1), + RUN_DEV(AZUREWAVE, RT3070_2), + RUN_DEV(AZUREWAVE, RT3070_3), + RUN_DEV(BELKIN, F9L1103), + RUN_DEV(BELKIN, F5D8053V3), + RUN_DEV(BELKIN, F5D8055), + RUN_DEV(BELKIN, F5D8055V2), + RUN_DEV(BELKIN, F6D4050V1), + RUN_DEV(BELKIN, F6D4050V2), + RUN_DEV(BELKIN, RT2870_1), + RUN_DEV(BELKIN, RT2870_2), + RUN_DEV(CISCOLINKSYS, AE1000), + RUN_DEV(CISCOLINKSYS2, RT3070), + RUN_DEV(CISCOLINKSYS3, RT3070), + RUN_DEV(CONCEPTRONIC2, RT2870_1), + RUN_DEV(CONCEPTRONIC2, RT2870_2), + RUN_DEV(CONCEPTRONIC2, RT2870_3), + RUN_DEV(CONCEPTRONIC2, RT2870_4), + RUN_DEV(CONCEPTRONIC2, RT2870_5), + RUN_DEV(CONCEPTRONIC2, RT2870_6), + RUN_DEV(CONCEPTRONIC2, RT2870_7), + RUN_DEV(CONCEPTRONIC2, RT2870_8), + RUN_DEV(CONCEPTRONIC2, RT3070_1), + RUN_DEV(CONCEPTRONIC2, RT3070_2), + RUN_DEV(CONCEPTRONIC2, VIGORN61), + RUN_DEV(COREGA, CGWLUSB300GNM), + RUN_DEV(COREGA, RT2870_1), + RUN_DEV(COREGA, RT2870_2), + RUN_DEV(COREGA, RT2870_3), + RUN_DEV(COREGA, RT3070), + RUN_DEV(CYBERTAN, RT2870), + RUN_DEV(DLINK, RT2870), + RUN_DEV(DLINK, RT3072), + RUN_DEV(DLINK, DWA127), + RUN_DEV(DLINK, DWA140B3), + RUN_DEV(DLINK, DWA160B2), + RUN_DEV(DLINK, DWA140D1), + RUN_DEV(DLINK, DWA162), + RUN_DEV(DLINK2, DWA130), + RUN_DEV(DLINK2, RT2870_1), + RUN_DEV(DLINK2, RT2870_2), + RUN_DEV(DLINK2, RT3070_1), + RUN_DEV(DLINK2, RT3070_2), + RUN_DEV(DLINK2, RT3070_3), + RUN_DEV(DLINK2, RT3070_4), + RUN_DEV(DLINK2, RT3070_5), + RUN_DEV(DLINK2, RT3072), + RUN_DEV(DLINK2, RT3072_1), + RUN_DEV(EDIMAX, EW7717), + RUN_DEV(EDIMAX, EW7718), + RUN_DEV(EDIMAX, EW7733UND), + RUN_DEV(EDIMAX, RT2870_1), + RUN_DEV(ENCORE, RT3070_1), + RUN_DEV(ENCORE, RT3070_2), + RUN_DEV(ENCORE, RT3070_3), + RUN_DEV(GIGABYTE, GNWB31N), + RUN_DEV(GIGABYTE, GNWB32L), + RUN_DEV(GIGABYTE, RT2870_1), + RUN_DEV(GIGASET, RT3070_1), + RUN_DEV(GIGASET, RT3070_2), + RUN_DEV(GUILLEMOT, HWNU300), + RUN_DEV(HAWKING, HWUN2), + RUN_DEV(HAWKING, RT2870_1), + RUN_DEV(HAWKING, RT2870_2), + RUN_DEV(HAWKING, RT3070), + RUN_DEV(IODATA, RT3072_1), + RUN_DEV(IODATA, RT3072_2), + RUN_DEV(IODATA, RT3072_3), + RUN_DEV(IODATA, RT3072_4), + RUN_DEV(LINKSYS4, RT3070), + RUN_DEV(LINKSYS4, WUSB100), + RUN_DEV(LINKSYS4, WUSB54GCV3), + RUN_DEV(LINKSYS4, WUSB600N), + RUN_DEV(LINKSYS4, WUSB600NV2), + RUN_DEV(LOGITEC, RT2870_1), + RUN_DEV(LOGITEC, RT2870_2), + RUN_DEV(LOGITEC, RT2870_3), + RUN_DEV(LOGITEC, LANW300NU2), + RUN_DEV(LOGITEC, LANW150NU2), + RUN_DEV(LOGITEC, LANW300NU2S), + RUN_DEV(MELCO, WLIUCG300HP), + RUN_DEV(MELCO, RT2870_2), + RUN_DEV(MELCO, WLIUCAG300N), + RUN_DEV(MELCO, WLIUCG300N), + RUN_DEV(MELCO, WLIUCG301N), + RUN_DEV(MELCO, WLIUCGN), + RUN_DEV(MELCO, WLIUCGNM), + RUN_DEV(MELCO, WLIUCG300HPV1), + RUN_DEV(MELCO, WLIUCGNM2), + RUN_DEV(MOTOROLA4, RT2770), + RUN_DEV(MOTOROLA4, RT3070), + RUN_DEV(MSI, RT3070_1), + RUN_DEV(MSI, RT3070_2), + RUN_DEV(MSI, RT3070_3), + RUN_DEV(MSI, RT3070_4), + RUN_DEV(MSI, RT3070_5), + RUN_DEV(MSI, RT3070_6), + RUN_DEV(MSI, RT3070_7), + RUN_DEV(MSI, RT3070_8), + RUN_DEV(MSI, RT3070_9), + RUN_DEV(MSI, RT3070_10), + RUN_DEV(MSI, RT3070_11), + RUN_DEV(NETGEAR, WNDA4100), + RUN_DEV(OVISLINK, RT3072), + RUN_DEV(PARA, RT3070), + RUN_DEV(PEGATRON, RT2870), + RUN_DEV(PEGATRON, RT3070), + RUN_DEV(PEGATRON, RT3070_2), + RUN_DEV(PEGATRON, RT3070_3), + RUN_DEV(PHILIPS, RT2870), + RUN_DEV(PLANEX2, GWUS300MINIS), + RUN_DEV(PLANEX2, GWUSMICRON), + RUN_DEV(PLANEX2, RT2870), + RUN_DEV(PLANEX2, RT3070), + RUN_DEV(QCOM, RT2870), + RUN_DEV(QUANTA, RT3070), + RUN_DEV(RALINK, RT2070), + RUN_DEV(RALINK, RT2770), + RUN_DEV(RALINK, RT2870), + RUN_DEV(RALINK, RT3070), + RUN_DEV(RALINK, RT3071), + RUN_DEV(RALINK, RT3072), + RUN_DEV(RALINK, RT3370), + RUN_DEV(RALINK, RT3572), + RUN_DEV(RALINK, RT3573), + RUN_DEV(RALINK, RT5370), + RUN_DEV(RALINK, RT5572), + RUN_DEV(RALINK, RT8070), + RUN_DEV(SAMSUNG, WIS09ABGN), + RUN_DEV(SAMSUNG2, RT2870_1), + RUN_DEV(SENAO, RT2870_1), + RUN_DEV(SENAO, RT2870_2), + RUN_DEV(SENAO, RT2870_3), + RUN_DEV(SENAO, RT2870_4), + RUN_DEV(SENAO, RT3070), + RUN_DEV(SENAO, RT3071), + RUN_DEV(SENAO, RT3072_1), + RUN_DEV(SENAO, RT3072_2), + RUN_DEV(SENAO, RT3072_3), + RUN_DEV(SENAO, RT3072_4), + RUN_DEV(SENAO, RT3072_5), + RUN_DEV(SITECOMEU, RT2770), + RUN_DEV(SITECOMEU, RT2870_1), + RUN_DEV(SITECOMEU, RT2870_2), + RUN_DEV(SITECOMEU, RT2870_3), + RUN_DEV(SITECOMEU, RT2870_4), + RUN_DEV(SITECOMEU, RT3070), + RUN_DEV(SITECOMEU, RT3070_2), + RUN_DEV(SITECOMEU, RT3070_3), + RUN_DEV(SITECOMEU, RT3070_4), + RUN_DEV(SITECOMEU, RT3071), + RUN_DEV(SITECOMEU, RT3072_1), + RUN_DEV(SITECOMEU, RT3072_2), + RUN_DEV(SITECOMEU, RT3072_3), + RUN_DEV(SITECOMEU, RT3072_4), + RUN_DEV(SITECOMEU, RT3072_5), + RUN_DEV(SITECOMEU, RT3072_6), + RUN_DEV(SITECOMEU, WL608), + RUN_DEV(SPARKLAN, RT2870_1), + RUN_DEV(SPARKLAN, RT3070), + RUN_DEV(SWEEX2, LW153), + RUN_DEV(SWEEX2, LW303), + RUN_DEV(SWEEX2, LW313), + RUN_DEV(TOSHIBA, RT3070), + RUN_DEV(UMEDIA, RT2870_1), + RUN_DEV(ZCOM, RT2870_1), + RUN_DEV(ZCOM, RT2870_2), + RUN_DEV(ZINWELL, RT2870_1), + RUN_DEV(ZINWELL, RT2870_2), + RUN_DEV(ZINWELL, RT3070), + RUN_DEV(ZINWELL, RT3072_1), + RUN_DEV(ZINWELL, RT3072_2), + RUN_DEV(ZYXEL, RT2870_1), + RUN_DEV(ZYXEL, RT2870_2), + RUN_DEV(ZYXEL, RT3070), + RUN_DEV_EJECT(ZYXEL, NWD2705), + RUN_DEV_EJECT(RALINK, RT_STOR), +#undef RUN_DEV_EJECT +#undef RUN_DEV +}; + +static device_probe_t run_match; +static device_attach_t run_attach; +static device_detach_t run_detach; + +static usb_callback_t run_bulk_rx_callback; +static usb_callback_t run_bulk_tx_callback0; +static usb_callback_t run_bulk_tx_callback1; +static usb_callback_t run_bulk_tx_callback2; +static usb_callback_t run_bulk_tx_callback3; +static usb_callback_t run_bulk_tx_callback4; +static usb_callback_t run_bulk_tx_callback5; + +static void run_autoinst(void *, struct usb_device *, + struct usb_attach_arg *); +static int run_driver_loaded(struct module *, int, void *); +static void run_bulk_tx_callbackN(struct usb_xfer *xfer, + usb_error_t error, u_int index); +static struct ieee80211vap *run_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, int, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void run_vap_delete(struct ieee80211vap *); +static void run_cmdq_cb(void *, int); +static void run_setup_tx_list(struct run_softc *, + struct run_endpoint_queue *); +static void run_unsetup_tx_list(struct run_softc *, + struct run_endpoint_queue *); +static int run_load_microcode(struct run_softc *); +static int run_reset(struct run_softc *); +static usb_error_t run_do_request(struct run_softc *, + struct usb_device_request *, void *); +static int run_read(struct run_softc *, uint16_t, uint32_t *); +static int run_read_region_1(struct run_softc *, uint16_t, uint8_t *, int); +static int run_write_2(struct run_softc *, uint16_t, uint16_t); +static int run_write(struct run_softc *, uint16_t, uint32_t); +static int run_write_region_1(struct run_softc *, uint16_t, + const uint8_t *, int); +static int run_set_region_4(struct run_softc *, uint16_t, uint32_t, int); +static int run_efuse_read(struct run_softc *, uint16_t, uint16_t *, int); +static int run_efuse_read_2(struct run_softc *, uint16_t, uint16_t *); +static int run_eeprom_read_2(struct run_softc *, uint16_t, uint16_t *); +static int run_rt2870_rf_write(struct run_softc *, uint32_t); +static int run_rt3070_rf_read(struct run_softc *, uint8_t, uint8_t *); +static int run_rt3070_rf_write(struct run_softc *, uint8_t, uint8_t); +static int run_bbp_read(struct run_softc *, uint8_t, uint8_t *); +static int run_bbp_write(struct run_softc *, uint8_t, uint8_t); +static int run_mcu_cmd(struct run_softc *, uint8_t, uint16_t); +static const char *run_get_rf(uint16_t); +static void run_rt3593_get_txpower(struct run_softc *); +static void run_get_txpower(struct run_softc *); +static int run_read_eeprom(struct run_softc *); +static struct ieee80211_node *run_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static int run_media_change(struct ifnet *); +static int run_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int run_wme_update(struct ieee80211com *); +static void run_key_set_cb(void *); +static int run_key_set(struct ieee80211vap *, struct ieee80211_key *); +static void run_key_delete_cb(void *); +static int run_key_delete(struct ieee80211vap *, struct ieee80211_key *); +static void run_ratectl_to(void *); +static void run_ratectl_cb(void *, int); +static void run_drain_fifo(void *); +static void run_iter_func(void *, struct ieee80211_node *); +static void run_newassoc_cb(void *); +static void run_newassoc(struct ieee80211_node *, int); +static void run_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, + const struct ieee80211_rx_stats *, int, int); +static void run_rx_frame(struct run_softc *, struct mbuf *, uint32_t); +static void run_tx_free(struct run_endpoint_queue *pq, + struct run_tx_data *, int); +static void run_set_tx_desc(struct run_softc *, struct run_tx_data *); +static int run_tx(struct run_softc *, struct mbuf *, + struct ieee80211_node *); +static int run_tx_mgt(struct run_softc *, struct mbuf *, + struct ieee80211_node *); +static int run_sendprot(struct run_softc *, const struct mbuf *, + struct ieee80211_node *, int, int); +static int run_tx_param(struct run_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *); +static int run_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static int run_transmit(struct ieee80211com *, struct mbuf *); +static void run_start(struct run_softc *); +static void run_parent(struct ieee80211com *); +static void run_iq_calib(struct run_softc *, u_int); +static void run_set_agc(struct run_softc *, uint8_t); +static void run_select_chan_group(struct run_softc *, int); +static void run_set_rx_antenna(struct run_softc *, int); +static void run_rt2870_set_chan(struct run_softc *, u_int); +static void run_rt3070_set_chan(struct run_softc *, u_int); +static void run_rt3572_set_chan(struct run_softc *, u_int); +static void run_rt3593_set_chan(struct run_softc *, u_int); +static void run_rt5390_set_chan(struct run_softc *, u_int); +static void run_rt5592_set_chan(struct run_softc *, u_int); +static int run_set_chan(struct run_softc *, struct ieee80211_channel *); +static void run_set_channel(struct ieee80211com *); +static void run_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static void run_scan_start(struct ieee80211com *); +static void run_scan_end(struct ieee80211com *); +static void run_update_beacon(struct ieee80211vap *, int); +static void run_update_beacon_cb(void *); +static void run_updateprot(struct ieee80211com *); +static void run_updateprot_cb(void *); +static void run_usb_timeout_cb(void *); +static void run_reset_livelock(struct run_softc *); +static void run_enable_tsf_sync(struct run_softc *); +static void run_enable_tsf(struct run_softc *); +static void run_get_tsf(struct run_softc *, uint64_t *); +static void run_enable_mrr(struct run_softc *); +static void run_set_txpreamble(struct run_softc *); +static void run_set_basicrates(struct run_softc *); +static void run_set_leds(struct run_softc *, uint16_t); +static void run_set_bssid(struct run_softc *, const uint8_t *); +static void run_set_macaddr(struct run_softc *, const uint8_t *); +static void run_updateslot(struct ieee80211com *); +static void run_updateslot_cb(void *); +static void run_update_mcast(struct ieee80211com *); +static int8_t run_rssi2dbm(struct run_softc *, uint8_t, uint8_t); +static void run_update_promisc_locked(struct run_softc *); +static void run_update_promisc(struct ieee80211com *); +static void run_rt5390_bbp_init(struct run_softc *); +static int run_bbp_init(struct run_softc *); +static int run_rt3070_rf_init(struct run_softc *); +static void run_rt3593_rf_init(struct run_softc *); +static void run_rt5390_rf_init(struct run_softc *); +static int run_rt3070_filter_calib(struct run_softc *, uint8_t, uint8_t, + uint8_t *); +static void run_rt3070_rf_setup(struct run_softc *); +static void run_rt3593_rf_setup(struct run_softc *); +static void run_rt5390_rf_setup(struct run_softc *); +static int run_txrx_enable(struct run_softc *); +static void run_adjust_freq_offset(struct run_softc *); +static void run_init_locked(struct run_softc *); +static void run_stop(void *); +static void run_delay(struct run_softc *, u_int); + +static eventhandler_tag run_etag; + +static const struct rt2860_rate { + uint8_t rate; + uint8_t mcs; + enum ieee80211_phytype phy; + uint8_t ctl_ridx; + uint16_t sp_ack_dur; + uint16_t lp_ack_dur; +} rt2860_rates[] = { + { 2, 0, IEEE80211_T_DS, 0, 314, 314 }, + { 4, 1, IEEE80211_T_DS, 1, 258, 162 }, + { 11, 2, IEEE80211_T_DS, 2, 223, 127 }, + { 22, 3, IEEE80211_T_DS, 3, 213, 117 }, + { 12, 0, IEEE80211_T_OFDM, 4, 60, 60 }, + { 18, 1, IEEE80211_T_OFDM, 4, 52, 52 }, + { 24, 2, IEEE80211_T_OFDM, 6, 48, 48 }, + { 36, 3, IEEE80211_T_OFDM, 6, 44, 44 }, + { 48, 4, IEEE80211_T_OFDM, 8, 44, 44 }, + { 72, 5, IEEE80211_T_OFDM, 8, 40, 40 }, + { 96, 6, IEEE80211_T_OFDM, 8, 40, 40 }, + { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 } +}; + +static const struct { + uint16_t reg; + uint32_t val; +} rt2870_def_mac[] = { + RT2870_DEF_MAC +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rt2860_def_bbp[] = { + RT2860_DEF_BBP +},rt5390_def_bbp[] = { + RT5390_DEF_BBP +},rt5592_def_bbp[] = { + RT5592_DEF_BBP +}; + +/* + * Default values for BBP register R196 for RT5592. + */ +static const uint8_t rt5592_bbp_r196[] = { + 0xe0, 0x1f, 0x38, 0x32, 0x08, 0x28, 0x19, 0x0a, 0xff, 0x00, + 0x16, 0x10, 0x10, 0x0b, 0x36, 0x2c, 0x26, 0x24, 0x42, 0x36, + 0x30, 0x2d, 0x4c, 0x46, 0x3d, 0x40, 0x3e, 0x42, 0x3d, 0x40, + 0x3c, 0x34, 0x2c, 0x2f, 0x3c, 0x35, 0x2e, 0x2a, 0x49, 0x41, + 0x36, 0x31, 0x30, 0x30, 0x0e, 0x0d, 0x28, 0x21, 0x1c, 0x16, + 0x50, 0x4a, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7d, 0x14, 0x32, 0x2c, 0x36, 0x4c, 0x43, 0x2c, + 0x2e, 0x36, 0x30, 0x6e +}; + +static const struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; +} rt2860_rf2850[] = { + RT2860_RF2850 +}; + +struct { + uint8_t n, r, k; +} rt3070_freqs[] = { + RT3070_RF3052 +}; + +static const struct rt5592_freqs { + uint16_t n; + uint8_t k, m, r; +} rt5592_freqs_20mhz[] = { + RT5592_RF5592_20MHZ +},rt5592_freqs_40mhz[] = { + RT5592_RF5592_40MHZ +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rt3070_def_rf[] = { + RT3070_DEF_RF +},rt3572_def_rf[] = { + RT3572_DEF_RF +},rt3593_def_rf[] = { + RT3593_DEF_RF +},rt5390_def_rf[] = { + RT5390_DEF_RF +},rt5392_def_rf[] = { + RT5392_DEF_RF +},rt5592_def_rf[] = { + RT5592_DEF_RF +},rt5592_2ghz_def_rf[] = { + RT5592_2GHZ_DEF_RF +},rt5592_5ghz_def_rf[] = { + RT5592_5GHZ_DEF_RF +}; + +static const struct { + u_int firstchan; + u_int lastchan; + uint8_t reg; + uint8_t val; +} rt5592_chan_5ghz[] = { + RT5592_CHAN_5GHZ +}; + +static const struct usb_config run_config[RUN_N_XFER] = { + [RUN_BULK_TX_BE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .ep_index = 0, + .direction = UE_DIR_OUT, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = run_bulk_tx_callback0, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_BK] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 1, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = run_bulk_tx_callback1, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_VI] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 2, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = run_bulk_tx_callback2, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_VO] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 3, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = run_bulk_tx_callback3, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_HCCA] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 4, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, + .callback = run_bulk_tx_callback4, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_PRIO] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 5, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, + .callback = run_bulk_tx_callback5, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = RUN_MAX_RXSZ, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = run_bulk_rx_callback, + } +}; + +static void +run_autoinst(void *arg, struct usb_device *udev, + struct usb_attach_arg *uaa) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *id; + + if (uaa->dev_state != UAA_DEV_READY) + return; + + iface = usbd_get_iface(udev, 0); + if (iface == NULL) + return; + id = iface->idesc; + if (id == NULL || id->bInterfaceClass != UICLASS_MASS) + return; + if (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa)) + return; + + if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0) + uaa->dev_state = UAA_DEV_EJECTING; +} + +static int +run_driver_loaded(struct module *mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + run_etag = EVENTHANDLER_REGISTER(usb_dev_configured, + run_autoinst, NULL, EVENTHANDLER_PRI_ANY); + break; + case MOD_UNLOAD: + EVENTHANDLER_DEREGISTER(usb_dev_configured, run_etag); + break; + default: + return (EOPNOTSUPP); + } + return (0); +} + +static int +run_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != 0) + return (ENXIO); + if (uaa->info.bIfaceIndex != RT2860_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa)); +} + +static int +run_attach(device_t self) +{ + struct run_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + struct ieee80211com *ic = &sc->sc_ic; + uint32_t ver; + uint8_t iface_index; + int ntries, error; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + if (USB_GET_DRIVER_INFO(uaa) != RUN_EJECT) + sc->sc_flags |= RUN_FLAG_FWLOAD_NEEDED; + + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), + MTX_NETWORK_LOCK, MTX_DEF); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + iface_index = RT2860_IFACE_INDEX; + + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, run_config, RUN_N_XFER, sc, &sc->sc_mtx); + if (error) { + device_printf(self, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto detach; + } + + RUN_LOCK(sc); + + /* wait for the chip to settle */ + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_ASIC_VER_ID, &ver) != 0) { + RUN_UNLOCK(sc); + goto detach; + } + if (ver != 0 && ver != 0xffffffff) + break; + run_delay(sc, 10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for NIC to initialize\n"); + RUN_UNLOCK(sc); + goto detach; + } + sc->mac_ver = ver >> 16; + sc->mac_rev = ver & 0xffff; + + /* retrieve RF rev. no and various other things from EEPROM */ + run_read_eeprom(sc); + + device_printf(sc->sc_dev, + "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n", + sc->mac_ver, sc->mac_rev, run_get_rf(sc->rf_rev), + sc->ntxchains, sc->nrxchains, ether_sprintf(ic->ic_macaddr)); + + RUN_UNLOCK(sc); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(self); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA | /* station mode supported */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_IBSS | + IEEE80211_C_HOSTAP | + IEEE80211_C_WDS | /* 4-address traffic works */ + IEEE80211_C_MBSS | + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_WME | /* WME */ + IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */ + + ic->ic_cryptocaps = + IEEE80211_CRYPTO_WEP | + IEEE80211_CRYPTO_AES_CCM | + IEEE80211_CRYPTO_TKIPMIC | + IEEE80211_CRYPTO_TKIP; + + ic->ic_flags |= IEEE80211_F_DATAPAD; + ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; + + run_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + + ic->ic_scan_start = run_scan_start; + ic->ic_scan_end = run_scan_end; + ic->ic_set_channel = run_set_channel; + ic->ic_getradiocaps = run_getradiocaps; + ic->ic_node_alloc = run_node_alloc; + ic->ic_newassoc = run_newassoc; + ic->ic_updateslot = run_updateslot; + ic->ic_update_mcast = run_update_mcast; + ic->ic_wme.wme_update = run_wme_update; + ic->ic_raw_xmit = run_raw_xmit; + ic->ic_update_promisc = run_update_promisc; + ic->ic_vap_create = run_vap_create; + ic->ic_vap_delete = run_vap_delete; + ic->ic_transmit = run_transmit; + ic->ic_parent = run_parent; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + RUN_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RUN_RX_RADIOTAP_PRESENT); + + TASK_INIT(&sc->cmdq_task, 0, run_cmdq_cb, sc); + TASK_INIT(&sc->ratectl_task, 0, run_ratectl_cb, sc); + usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +detach: + run_detach(self); + return (ENXIO); +} + +static void +run_drain_mbufq(struct run_softc *sc) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + ieee80211_free_node(ni); + m_freem(m); + } +} + +static int +run_detach(device_t self) +{ + struct run_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + int i; + + RUN_LOCK(sc); + sc->sc_detached = 1; + RUN_UNLOCK(sc); + + /* stop all USB transfers */ + usbd_transfer_unsetup(sc->sc_xfer, RUN_N_XFER); + + RUN_LOCK(sc); + sc->ratectl_run = RUN_RATECTL_OFF; + sc->cmdq_run = sc->cmdq_key_set = RUN_CMDQ_ABORT; + + /* free TX list, if any */ + for (i = 0; i != RUN_EP_QUEUES; i++) + run_unsetup_tx_list(sc, &sc->sc_epq[i]); + + /* Free TX queue */ + run_drain_mbufq(sc); + RUN_UNLOCK(sc); + + if (sc->sc_ic.ic_softc == sc) { + /* drain tasks */ + usb_callout_drain(&sc->ratectl_ch); + ieee80211_draintask(ic, &sc->cmdq_task); + ieee80211_draintask(ic, &sc->ratectl_task); + ieee80211_ifdetach(ic); + } + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static struct ieee80211vap * +run_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct run_softc *sc = ic->ic_softc; + struct run_vap *rvp; + struct ieee80211vap *vap; + int i; + + if (sc->rvp_cnt >= RUN_VAP_MAX) { + device_printf(sc->sc_dev, "number of VAPs maxed out\n"); + return (NULL); + } + + switch (opmode) { + case IEEE80211_M_STA: + /* enable s/w bmiss handling for sta mode */ + flags |= IEEE80211_CLONE_NOBEACONS; + /* fall though */ + case IEEE80211_M_IBSS: + case IEEE80211_M_MONITOR: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + /* other than WDS vaps, only one at a time */ + if (!TAILQ_EMPTY(&ic->ic_vaps)) + return (NULL); + break; + case IEEE80211_M_WDS: + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next){ + if(vap->iv_opmode != IEEE80211_M_HOSTAP) + continue; + /* WDS vap's always share the local mac address. */ + flags &= ~IEEE80211_CLONE_BSSID; + break; + } + if (vap == NULL) { + device_printf(sc->sc_dev, + "wds only supported in ap mode\n"); + return (NULL); + } + break; + default: + device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); + return (NULL); + } + + rvp = malloc(sizeof(struct run_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &rvp->vap; + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, + bssid) != 0) { + /* out of memory */ + free(rvp, M_80211_VAP); + return (NULL); + } + + vap->iv_update_beacon = run_update_beacon; + vap->iv_max_aid = RT2870_WCID_MAX; + /* + * To delete the right key from h/w, we need wcid. + * Luckily, there is unused space in ieee80211_key{}, wk_pad, + * and matching wcid will be written into there. So, cast + * some spells to remove 'const' from ieee80211_key{} + */ + vap->iv_key_delete = (void *)run_key_delete; + vap->iv_key_set = (void *)run_key_set; + + /* override state transition machine */ + rvp->newstate = vap->iv_newstate; + vap->iv_newstate = run_newstate; + if (opmode == IEEE80211_M_IBSS) { + rvp->recv_mgmt = vap->iv_recv_mgmt; + vap->iv_recv_mgmt = run_recv_mgmt; + } + + ieee80211_ratectl_init(vap); + ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); + + /* complete setup */ + ieee80211_vap_attach(vap, run_media_change, ieee80211_media_status, + mac); + + /* make sure id is always unique */ + for (i = 0; i < RUN_VAP_MAX; i++) { + if((sc->rvp_bmap & 1 << i) == 0){ + sc->rvp_bmap |= 1 << i; + rvp->rvp_id = i; + break; + } + } + if (sc->rvp_cnt++ == 0) + ic->ic_opmode = opmode; + + if (opmode == IEEE80211_M_HOSTAP) + sc->cmdq_run = RUN_CMDQ_GO; + + RUN_DPRINTF(sc, RUN_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n", + rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt); + + return (vap); +} + +static void +run_vap_delete(struct ieee80211vap *vap) +{ + struct run_vap *rvp = RUN_VAP(vap); + struct ieee80211com *ic; + struct run_softc *sc; + uint8_t rvp_id; + + if (vap == NULL) + return; + + ic = vap->iv_ic; + sc = ic->ic_softc; + + RUN_LOCK(sc); + + m_freem(rvp->beacon_mbuf); + rvp->beacon_mbuf = NULL; + + rvp_id = rvp->rvp_id; + sc->ratectl_run &= ~(1 << rvp_id); + sc->rvp_bmap &= ~(1 << rvp_id); + run_set_region_4(sc, RT2860_SKEY(rvp_id, 0), 0, 128); + run_set_region_4(sc, RT2860_BCN_BASE(rvp_id), 0, 512); + --sc->rvp_cnt; + + RUN_DPRINTF(sc, RUN_DEBUG_STATE, + "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n", + vap, rvp_id, sc->rvp_bmap, sc->rvp_cnt); + + RUN_UNLOCK(sc); + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + free(rvp, M_80211_VAP); +} + +/* + * There are numbers of functions need to be called in context thread. + * Rather than creating taskqueue event for each of those functions, + * here is all-for-one taskqueue callback function. This function + * guarantees deferred functions are executed in the same order they + * were enqueued. + * '& RUN_CMDQ_MASQ' is to loop cmdq[]. + */ +static void +run_cmdq_cb(void *arg, int pending) +{ + struct run_softc *sc = arg; + uint8_t i; + + /* call cmdq[].func locked */ + RUN_LOCK(sc); + for (i = sc->cmdq_exec; sc->cmdq[i].func && pending; + i = sc->cmdq_exec, pending--) { + RUN_DPRINTF(sc, RUN_DEBUG_CMD, "cmdq_exec=%d pending=%d\n", + i, pending); + if (sc->cmdq_run == RUN_CMDQ_GO) { + /* + * If arg0 is NULL, callback func needs more + * than one arg. So, pass ptr to cmdq struct. + */ + if (sc->cmdq[i].arg0) + sc->cmdq[i].func(sc->cmdq[i].arg0); + else + sc->cmdq[i].func(&sc->cmdq[i]); + } + sc->cmdq[i].arg0 = NULL; + sc->cmdq[i].func = NULL; + sc->cmdq_exec++; + sc->cmdq_exec &= RUN_CMDQ_MASQ; + } + RUN_UNLOCK(sc); +} + +static void +run_setup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq) +{ + struct run_tx_data *data; + + memset(pq, 0, sizeof(*pq)); + + STAILQ_INIT(&pq->tx_qh); + STAILQ_INIT(&pq->tx_fh); + + for (data = &pq->tx_data[0]; + data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) { + data->sc = sc; + STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); + } + pq->tx_nfree = RUN_TX_RING_COUNT; +} + +static void +run_unsetup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq) +{ + struct run_tx_data *data; + + /* make sure any subsequent use of the queues will fail */ + pq->tx_nfree = 0; + STAILQ_INIT(&pq->tx_fh); + STAILQ_INIT(&pq->tx_qh); + + /* free up all node references and mbufs */ + for (data = &pq->tx_data[0]; + data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) { + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +static int +run_load_microcode(struct run_softc *sc) +{ + usb_device_request_t req; + const struct firmware *fw; + const u_char *base; + uint32_t tmp; + int ntries, error; + const uint64_t *temp; + uint64_t bytes; + + RUN_UNLOCK(sc); + fw = firmware_get("runfw"); + RUN_LOCK(sc); + if (fw == NULL) { + device_printf(sc->sc_dev, + "failed loadfirmware of file %s\n", "runfw"); + return ENOENT; + } + + if (fw->datasize != 8192) { + device_printf(sc->sc_dev, + "invalid firmware size (should be 8KB)\n"); + error = EINVAL; + goto fail; + } + + /* + * RT3071/RT3072 use a different firmware + * run-rt2870 (8KB) contains both, + * first half (4KB) is for rt2870, + * last half is for rt3071. + */ + base = fw->data; + if ((sc->mac_ver) != 0x2860 && + (sc->mac_ver) != 0x2872 && + (sc->mac_ver) != 0x3070) { + base += 4096; + } + + /* cheap sanity check */ + temp = fw->data; + bytes = *temp; + if (bytes != be64toh(0xffffff0210280210ULL)) { + device_printf(sc->sc_dev, "firmware checksum failed\n"); + error = EINVAL; + goto fail; + } + + /* write microcode image */ + if (sc->sc_flags & RUN_FLAG_FWLOAD_NEEDED) { + run_write_region_1(sc, RT2870_FW_BASE, base, 4096); + run_write(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff); + run_write(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff); + } + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2870_RESET; + USETW(req.wValue, 8); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + if ((error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)) + != 0) { + device_printf(sc->sc_dev, "firmware reset failed\n"); + goto fail; + } + + run_delay(sc, 10); + + run_write(sc, RT2860_H2M_BBPAGENT, 0); + run_write(sc, RT2860_H2M_MAILBOX, 0); + run_write(sc, RT2860_H2M_INTSRC, 0); + if ((error = run_mcu_cmd(sc, RT2860_MCU_CMD_RFRESET, 0)) != 0) + goto fail; + + /* wait until microcontroller is ready */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((error = run_read(sc, RT2860_SYS_CTRL, &tmp)) != 0) + goto fail; + if (tmp & RT2860_MCU_READY) + break; + run_delay(sc, 10); + } + if (ntries == 1000) { + device_printf(sc->sc_dev, + "timeout waiting for MCU to initialize\n"); + error = ETIMEDOUT; + goto fail; + } + device_printf(sc->sc_dev, "firmware %s ver. %u.%u loaded\n", + (base == fw->data) ? "RT2870" : "RT3071", + *(base + 4092), *(base + 4093)); + +fail: + firmware_put(fw, FIRMWARE_UNLOAD); + return (error); +} + +static int +run_reset(struct run_softc *sc) +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2870_RESET; + USETW(req.wValue, 1); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); +} + +static usb_error_t +run_do_request(struct run_softc *sc, + struct usb_device_request *req, void *data) +{ + usb_error_t err; + int ntries = 10; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0) + break; + RUN_DPRINTF(sc, RUN_DEBUG_USB, + "Control request failed, %s (retrying)\n", + usbd_errstr(err)); + run_delay(sc, 10); + } + return (err); +} + +static int +run_read(struct run_softc *sc, uint16_t reg, uint32_t *val) +{ + uint32_t tmp; + int error; + + error = run_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp); + if (error == 0) + *val = le32toh(tmp); + else + *val = 0xffffffff; + return (error); +} + +static int +run_read_region_1(struct run_softc *sc, uint16_t reg, uint8_t *buf, int len) +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2870_READ_REGION_1; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + return (run_do_request(sc, &req, buf)); +} + +static int +run_write_2(struct run_softc *sc, uint16_t reg, uint16_t val) +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2870_WRITE_2; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + return (run_do_request(sc, &req, NULL)); +} + +static int +run_write(struct run_softc *sc, uint16_t reg, uint32_t val) +{ + int error; + + if ((error = run_write_2(sc, reg, val & 0xffff)) == 0) + error = run_write_2(sc, reg + 2, val >> 16); + return (error); +} + +static int +run_write_region_1(struct run_softc *sc, uint16_t reg, const uint8_t *buf, + int len) +{ +#if 1 + int i, error = 0; + /* + * NB: the WRITE_REGION_1 command is not stable on RT2860. + * We thus issue multiple WRITE_2 commands instead. + */ + KASSERT((len & 1) == 0, ("run_write_region_1: Data too long.\n")); + for (i = 0; i < len && error == 0; i += 2) + error = run_write_2(sc, reg + i, buf[i] | buf[i + 1] << 8); + return (error); +#else + usb_device_request_t req; + int error = 0; + + /* + * NOTE: It appears the WRITE_REGION_1 command cannot be + * passed a huge amount of data, which will crash the + * firmware. Limit amount of data passed to 64-bytes at a + * time. + */ + while (len > 0) { + int delta = 64; + if (delta > len) + delta = len; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2870_WRITE_REGION_1; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, delta); + error = run_do_request(sc, &req, __DECONST(uint8_t *, buf)); + if (error != 0) + break; + reg += delta; + buf += delta; + len -= delta; + } + return (error); +#endif +} + +static int +run_set_region_4(struct run_softc *sc, uint16_t reg, uint32_t val, int len) +{ + int i, error = 0; + + KASSERT((len & 3) == 0, ("run_set_region_4: Invalid data length.\n")); + for (i = 0; i < len && error == 0; i += 4) + error = run_write(sc, reg + i, val); + return (error); +} + +static int +run_efuse_read(struct run_softc *sc, uint16_t addr, uint16_t *val, int count) +{ + uint32_t tmp; + uint16_t reg; + int error, ntries; + + if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0) + return (error); + + if (count == 2) + addr *= 2; + /*- + * Read one 16-byte block into registers EFUSE_DATA[0-3]: + * DATA0: F E D C + * DATA1: B A 9 8 + * DATA2: 7 6 5 4 + * DATA3: 3 2 1 0 + */ + tmp &= ~(RT3070_EFSROM_MODE_MASK | RT3070_EFSROM_AIN_MASK); + tmp |= (addr & ~0xf) << RT3070_EFSROM_AIN_SHIFT | RT3070_EFSROM_KICK; + run_write(sc, RT3070_EFUSE_CTRL, tmp); + for (ntries = 0; ntries < 100; ntries++) { + if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0) + return (error); + if (!(tmp & RT3070_EFSROM_KICK)) + break; + run_delay(sc, 2); + } + if (ntries == 100) + return (ETIMEDOUT); + + if ((tmp & RT3070_EFUSE_AOUT_MASK) == RT3070_EFUSE_AOUT_MASK) { + *val = 0xffff; /* address not found */ + return (0); + } + /* determine to which 32-bit register our 16-bit word belongs */ + reg = RT3070_EFUSE_DATA3 - (addr & 0xc); + if ((error = run_read(sc, reg, &tmp)) != 0) + return (error); + + tmp >>= (8 * (addr & 0x3)); + *val = (addr & 1) ? tmp >> 16 : tmp & 0xffff; + + return (0); +} + +/* Read 16-bit from eFUSE ROM for RT3xxx. */ +static int +run_efuse_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val) +{ + return (run_efuse_read(sc, addr, val, 2)); +} + +static int +run_eeprom_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val) +{ + usb_device_request_t req; + uint16_t tmp; + int error; + + addr *= 2; + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2870_EEPROM_READ; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, sizeof(tmp)); + + error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &tmp); + if (error == 0) + *val = le16toh(tmp); + else + *val = 0xffff; + return (error); +} + +static __inline int +run_srom_read(struct run_softc *sc, uint16_t addr, uint16_t *val) +{ + /* either eFUSE ROM or EEPROM */ + return sc->sc_srom_read(sc, addr, val); +} + +static int +run_rt2870_rf_write(struct run_softc *sc, uint32_t val) +{ + uint32_t tmp; + int error, ntries; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT2860_RF_CSR_CFG0, &tmp)) != 0) + return (error); + if (!(tmp & RT2860_RF_REG_CTRL)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + return (run_write(sc, RT2860_RF_CSR_CFG0, val)); +} + +static int +run_rt3070_rf_read(struct run_softc *sc, uint8_t reg, uint8_t *val) +{ + uint32_t tmp; + int error, ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT3070_RF_KICK)) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + tmp = RT3070_RF_KICK | reg << 8; + if ((error = run_write(sc, RT3070_RF_CSR_CFG, tmp)) != 0) + return (error); + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT3070_RF_KICK)) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + *val = tmp & 0xff; + return (0); +} + +static int +run_rt3070_rf_write(struct run_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int error, ntries; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT3070_RF_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + tmp = RT3070_RF_WRITE | RT3070_RF_KICK | reg << 8 | val; + return (run_write(sc, RT3070_RF_CSR_CFG, tmp)); +} + +static int +run_bbp_read(struct run_softc *sc, uint8_t reg, uint8_t *val) +{ + uint32_t tmp; + int ntries, error; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT2860_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + tmp = RT2860_BBP_CSR_READ | RT2860_BBP_CSR_KICK | reg << 8; + if ((error = run_write(sc, RT2860_BBP_CSR_CFG, tmp)) != 0) + return (error); + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT2860_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + *val = tmp & 0xff; + return (0); +} + +static int +run_bbp_write(struct run_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int ntries, error; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT2860_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + tmp = RT2860_BBP_CSR_KICK | reg << 8 | val; + return (run_write(sc, RT2860_BBP_CSR_CFG, tmp)); +} + +/* + * Send a command to the 8051 microcontroller unit. + */ +static int +run_mcu_cmd(struct run_softc *sc, uint8_t cmd, uint16_t arg) +{ + uint32_t tmp; + int error, ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = run_read(sc, RT2860_H2M_MAILBOX, &tmp)) != 0) + return error; + if (!(tmp & RT2860_H2M_BUSY)) + break; + } + if (ntries == 100) + return ETIMEDOUT; + + tmp = RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg; + if ((error = run_write(sc, RT2860_H2M_MAILBOX, tmp)) == 0) + error = run_write(sc, RT2860_HOST_CMD, cmd); + return (error); +} + +/* + * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. + * Used to adjust per-rate Tx power registers. + */ +static __inline uint32_t +b4inc(uint32_t b32, int8_t delta) +{ + int8_t i, b4; + + for (i = 0; i < 8; i++) { + b4 = b32 & 0xf; + b4 += delta; + if (b4 < 0) + b4 = 0; + else if (b4 > 0xf) + b4 = 0xf; + b32 = b32 >> 4 | b4 << 28; + } + return (b32); +} + +static const char * +run_get_rf(uint16_t rev) +{ + switch (rev) { + case RT2860_RF_2820: return "RT2820"; + case RT2860_RF_2850: return "RT2850"; + case RT2860_RF_2720: return "RT2720"; + case RT2860_RF_2750: return "RT2750"; + case RT3070_RF_3020: return "RT3020"; + case RT3070_RF_2020: return "RT2020"; + case RT3070_RF_3021: return "RT3021"; + case RT3070_RF_3022: return "RT3022"; + case RT3070_RF_3052: return "RT3052"; + case RT3593_RF_3053: return "RT3053"; + case RT5592_RF_5592: return "RT5592"; + case RT5390_RF_5370: return "RT5370"; + case RT5390_RF_5372: return "RT5372"; + } + return ("unknown"); +} + +static void +run_rt3593_get_txpower(struct run_softc *sc) +{ + uint16_t addr, val; + int i; + + /* Read power settings for 2GHz channels. */ + for (i = 0; i < 14; i += 2) { + addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE1 : + RT2860_EEPROM_PWR2GHZ_BASE1; + run_srom_read(sc, addr + i / 2, &val); + sc->txpow1[i + 0] = (int8_t)(val & 0xff); + sc->txpow1[i + 1] = (int8_t)(val >> 8); + + addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE2 : + RT2860_EEPROM_PWR2GHZ_BASE2; + run_srom_read(sc, addr + i / 2, &val); + sc->txpow2[i + 0] = (int8_t)(val & 0xff); + sc->txpow2[i + 1] = (int8_t)(val >> 8); + + if (sc->ntxchains == 3) { + run_srom_read(sc, RT3593_EEPROM_PWR2GHZ_BASE3 + i / 2, + &val); + sc->txpow3[i + 0] = (int8_t)(val & 0xff); + sc->txpow3[i + 1] = (int8_t)(val >> 8); + } + } + /* Fix broken Tx power entries. */ + for (i = 0; i < 14; i++) { + if (sc->txpow1[i] > 31) + sc->txpow1[i] = 5; + if (sc->txpow2[i] > 31) + sc->txpow2[i] = 5; + if (sc->ntxchains == 3) { + if (sc->txpow3[i] > 31) + sc->txpow3[i] = 5; + } + } + /* Read power settings for 5GHz channels. */ + for (i = 0; i < 40; i += 2) { + run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE1 + i / 2, &val); + sc->txpow1[i + 14] = (int8_t)(val & 0xff); + sc->txpow1[i + 15] = (int8_t)(val >> 8); + + run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE2 + i / 2, &val); + sc->txpow2[i + 14] = (int8_t)(val & 0xff); + sc->txpow2[i + 15] = (int8_t)(val >> 8); + + if (sc->ntxchains == 3) { + run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE3 + i / 2, + &val); + sc->txpow3[i + 14] = (int8_t)(val & 0xff); + sc->txpow3[i + 15] = (int8_t)(val >> 8); + } + } +} + +static void +run_get_txpower(struct run_softc *sc) +{ + uint16_t val; + int i; + + /* Read power settings for 2GHz channels. */ + for (i = 0; i < 14; i += 2) { + run_srom_read(sc, RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2, &val); + sc->txpow1[i + 0] = (int8_t)(val & 0xff); + sc->txpow1[i + 1] = (int8_t)(val >> 8); + + if (sc->mac_ver != 0x5390) { + run_srom_read(sc, + RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2, &val); + sc->txpow2[i + 0] = (int8_t)(val & 0xff); + sc->txpow2[i + 1] = (int8_t)(val >> 8); + } + } + /* Fix broken Tx power entries. */ + for (i = 0; i < 14; i++) { + if (sc->mac_ver >= 0x5390) { + if (sc->txpow1[i] < 0 || sc->txpow1[i] > 39) + sc->txpow1[i] = 5; + } else { + if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31) + sc->txpow1[i] = 5; + } + if (sc->mac_ver > 0x5390) { + if (sc->txpow2[i] < 0 || sc->txpow2[i] > 39) + sc->txpow2[i] = 5; + } else if (sc->mac_ver < 0x5390) { + if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31) + sc->txpow2[i] = 5; + } + RUN_DPRINTF(sc, RUN_DEBUG_TXPWR, + "chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]); + } + /* Read power settings for 5GHz channels. */ + for (i = 0; i < 40; i += 2) { + run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2, &val); + sc->txpow1[i + 14] = (int8_t)(val & 0xff); + sc->txpow1[i + 15] = (int8_t)(val >> 8); + + run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2, &val); + sc->txpow2[i + 14] = (int8_t)(val & 0xff); + sc->txpow2[i + 15] = (int8_t)(val >> 8); + } + /* Fix broken Tx power entries. */ + for (i = 0; i < 40; i++ ) { + if (sc->mac_ver != 0x5592) { + if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15) + sc->txpow1[14 + i] = 5; + if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15) + sc->txpow2[14 + i] = 5; + } + RUN_DPRINTF(sc, RUN_DEBUG_TXPWR, + "chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i], + sc->txpow2[14 + i]); + } +} + +static int +run_read_eeprom(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int8_t delta_2ghz, delta_5ghz; + uint32_t tmp; + uint16_t val; + int ridx, ant, i; + + /* check whether the ROM is eFUSE ROM or EEPROM */ + sc->sc_srom_read = run_eeprom_read_2; + if (sc->mac_ver >= 0x3070) { + run_read(sc, RT3070_EFUSE_CTRL, &tmp); + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EFUSE_CTRL=0x%08x\n", tmp); + if ((tmp & RT3070_SEL_EFUSE) || sc->mac_ver == 0x3593) + sc->sc_srom_read = run_efuse_read_2; + } + + /* read ROM version */ + run_srom_read(sc, RT2860_EEPROM_VERSION, &val); + RUN_DPRINTF(sc, RUN_DEBUG_ROM, + "EEPROM rev=%d, FAE=%d\n", val >> 8, val & 0xff); + + /* read MAC address */ + run_srom_read(sc, RT2860_EEPROM_MAC01, &val); + ic->ic_macaddr[0] = val & 0xff; + ic->ic_macaddr[1] = val >> 8; + run_srom_read(sc, RT2860_EEPROM_MAC23, &val); + ic->ic_macaddr[2] = val & 0xff; + ic->ic_macaddr[3] = val >> 8; + run_srom_read(sc, RT2860_EEPROM_MAC45, &val); + ic->ic_macaddr[4] = val & 0xff; + ic->ic_macaddr[5] = val >> 8; + + if (sc->mac_ver < 0x3593) { + /* read vender BBP settings */ + for (i = 0; i < 10; i++) { + run_srom_read(sc, RT2860_EEPROM_BBP_BASE + i, &val); + sc->bbp[i].val = val & 0xff; + sc->bbp[i].reg = val >> 8; + RUN_DPRINTF(sc, RUN_DEBUG_ROM, + "BBP%d=0x%02x\n", sc->bbp[i].reg, sc->bbp[i].val); + } + if (sc->mac_ver >= 0x3071) { + /* read vendor RF settings */ + for (i = 0; i < 10; i++) { + run_srom_read(sc, RT3071_EEPROM_RF_BASE + i, + &val); + sc->rf[i].val = val & 0xff; + sc->rf[i].reg = val >> 8; + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "RF%d=0x%02x\n", + sc->rf[i].reg, sc->rf[i].val); + } + } + } + + /* read RF frequency offset from EEPROM */ + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS : + RT3593_EEPROM_FREQ, &val); + sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM freq offset %d\n", + sc->freq & 0xff); + + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS : + RT3593_EEPROM_FREQ_LEDS, &val); + if (val >> 8 != 0xff) { + /* read LEDs operating mode */ + sc->leds = val >> 8; + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED1 : + RT3593_EEPROM_LED1, &sc->led[0]); + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED2 : + RT3593_EEPROM_LED2, &sc->led[1]); + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED3 : + RT3593_EEPROM_LED3, &sc->led[2]); + } else { + /* broken EEPROM, use default settings */ + sc->leds = 0x01; + sc->led[0] = 0x5555; + sc->led[1] = 0x2221; + sc->led[2] = 0x5627; /* differs from RT2860 */ + } + RUN_DPRINTF(sc, RUN_DEBUG_ROM, + "EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", + sc->leds, sc->led[0], sc->led[1], sc->led[2]); + + /* read RF information */ + if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) + run_srom_read(sc, 0x00, &val); + else + run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val); + + if (val == 0xffff) { + device_printf(sc->sc_dev, + "invalid EEPROM antenna info, using default\n"); + if (sc->mac_ver == 0x3572) { + /* default to RF3052 2T2R */ + sc->rf_rev = RT3070_RF_3052; + sc->ntxchains = 2; + sc->nrxchains = 2; + } else if (sc->mac_ver >= 0x3070) { + /* default to RF3020 1T1R */ + sc->rf_rev = RT3070_RF_3020; + sc->ntxchains = 1; + sc->nrxchains = 1; + } else { + /* default to RF2820 1T2R */ + sc->rf_rev = RT2860_RF_2820; + sc->ntxchains = 1; + sc->nrxchains = 2; + } + } else { + if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) { + sc->rf_rev = val; + run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val); + } else + sc->rf_rev = (val >> 8) & 0xf; + sc->ntxchains = (val >> 4) & 0xf; + sc->nrxchains = val & 0xf; + } + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM RF rev=0x%04x chains=%dT%dR\n", + sc->rf_rev, sc->ntxchains, sc->nrxchains); + + /* check if RF supports automatic Tx access gain control */ + run_srom_read(sc, RT2860_EEPROM_CONFIG, &val); + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM CFG 0x%04x\n", val); + /* check if driver should patch the DAC issue */ + if ((val >> 8) != 0xff) + sc->patch_dac = (val >> 15) & 1; + if ((val & 0xff) != 0xff) { + sc->ext_5ghz_lna = (val >> 3) & 1; + sc->ext_2ghz_lna = (val >> 2) & 1; + /* check if RF supports automatic Tx access gain control */ + sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1; + /* check if we have a hardware radio switch */ + sc->rfswitch = val & 1; + } + + /* Read Tx power settings. */ + if (sc->mac_ver == 0x3593) + run_rt3593_get_txpower(sc); + else + run_get_txpower(sc); + + /* read Tx power compensation for each Tx rate */ + run_srom_read(sc, RT2860_EEPROM_DELTAPWR, &val); + delta_2ghz = delta_5ghz = 0; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_2ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_2ghz = -delta_2ghz; + } + val >>= 8; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_5ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_5ghz = -delta_5ghz; + } + RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR, + "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz, delta_5ghz); + + for (ridx = 0; ridx < 5; ridx++) { + uint32_t reg; + + run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2, &val); + reg = val; + run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2 + 1, &val); + reg |= (uint32_t)val << 16; + + sc->txpow20mhz[ridx] = reg; + sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); + sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); + + RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR, + "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " + "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx], + sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]); + } + + /* Read RSSI offsets and LNA gains from EEPROM. */ + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_2GHZ : + RT3593_EEPROM_RSSI1_2GHZ, &val); + sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_2ghz[1] = val >> 8; /* Ant B */ + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_2GHZ : + RT3593_EEPROM_RSSI2_2GHZ, &val); + if (sc->mac_ver >= 0x3070) { + if (sc->mac_ver == 0x3593) { + sc->txmixgain_2ghz = 0; + sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ + } else { + /* + * On RT3070 chips (limited to 2 Rx chains), this ROM + * field contains the Tx mixer gain for the 2GHz band. + */ + if ((val & 0xff) != 0xff) + sc->txmixgain_2ghz = val & 0x7; + } + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n", + sc->txmixgain_2ghz); + } else + sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ + if (sc->mac_ver == 0x3593) + run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val); + sc->lna[2] = val >> 8; /* channel group 2 */ + + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_5GHZ : + RT3593_EEPROM_RSSI1_5GHZ, &val); + sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_5ghz[1] = val >> 8; /* Ant B */ + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_5GHZ : + RT3593_EEPROM_RSSI2_5GHZ, &val); + if (sc->mac_ver == 0x3572) { + /* + * On RT3572 chips (limited to 2 Rx chains), this ROM + * field contains the Tx mixer gain for the 5GHz band. + */ + if ((val & 0xff) != 0xff) + sc->txmixgain_5ghz = val & 0x7; + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (5GHz)\n", + sc->txmixgain_5ghz); + } else + sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ + if (sc->mac_ver == 0x3593) { + sc->txmixgain_5ghz = 0; + run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val); + } + sc->lna[3] = val >> 8; /* channel group 3 */ + + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LNA : + RT3593_EEPROM_LNA, &val); + sc->lna[0] = val & 0xff; /* channel group 0 */ + sc->lna[1] = val >> 8; /* channel group 1 */ + + /* fix broken 5GHz LNA entries */ + if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { + RUN_DPRINTF(sc, RUN_DEBUG_ROM, + "invalid LNA for channel group %d\n", 2); + sc->lna[2] = sc->lna[1]; + } + if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { + RUN_DPRINTF(sc, RUN_DEBUG_ROM, + "invalid LNA for channel group %d\n", 3); + sc->lna[3] = sc->lna[1]; + } + + /* fix broken RSSI offset entries */ + for (ant = 0; ant < 3; ant++) { + if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { + RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI, + "invalid RSSI%d offset: %d (2GHz)\n", + ant + 1, sc->rssi_2ghz[ant]); + sc->rssi_2ghz[ant] = 0; + } + if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { + RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI, + "invalid RSSI%d offset: %d (5GHz)\n", + ant + 1, sc->rssi_5ghz[ant]); + sc->rssi_5ghz[ant] = 0; + } + } + return (0); +} + +static struct ieee80211_node * +run_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + return malloc(sizeof (struct run_node), M_DEVBUF, M_NOWAIT | M_ZERO); +} + +static int +run_media_change(struct ifnet *ifp) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + const struct ieee80211_txparam *tp; + struct run_softc *sc = ic->ic_softc; + uint8_t rate, ridx; + int error; + + RUN_LOCK(sc); + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) { + RUN_UNLOCK(sc); + return (error); + } + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { + struct ieee80211_node *ni; + struct run_node *rn; + + rate = ic->ic_sup_rates[ic->ic_curmode]. + rs_rates[tp->ucastrate] & IEEE80211_RATE_VAL; + for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + ni = ieee80211_ref_node(vap->iv_bss); + rn = RUN_NODE(ni); + rn->fix_ridx = ridx; + RUN_DPRINTF(sc, RUN_DEBUG_RATE, "rate=%d, fix_ridx=%d\n", + rate, rn->fix_ridx); + ieee80211_free_node(ni); + } + +#if 0 + if ((ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & RUN_RUNNING)){ + run_init_locked(sc); + } +#endif + + RUN_UNLOCK(sc); + + return (0); +} + +static int +run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + const struct ieee80211_txparam *tp; + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + struct run_vap *rvp = RUN_VAP(vap); + enum ieee80211_state ostate; + uint32_t sta[3]; + uint32_t tmp; + uint8_t ratectl; + uint8_t restart_ratectl = 0; + uint8_t bid = 1 << rvp->rvp_id; + + ostate = vap->iv_state; + RUN_DPRINTF(sc, RUN_DEBUG_STATE, "%s -> %s\n", + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + RUN_LOCK(sc); + + ratectl = sc->ratectl_run; /* remember current state */ + sc->ratectl_run = RUN_RATECTL_OFF; + usb_callout_stop(&sc->ratectl_ch); + + if (ostate == IEEE80211_S_RUN) { + /* turn link LED off */ + run_set_leds(sc, RT2860_LED_RADIO); + } + + switch (nstate) { + case IEEE80211_S_INIT: + restart_ratectl = 1; + + if (ostate != IEEE80211_S_RUN) + break; + + ratectl &= ~bid; + sc->runbmap &= ~bid; + + /* abort TSF synchronization if there is no vap running */ + if (--sc->running == 0) { + run_read(sc, RT2860_BCN_TIME_CFG, &tmp); + run_write(sc, RT2860_BCN_TIME_CFG, + tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | + RT2860_TBTT_TIMER_EN)); + } + break; + + case IEEE80211_S_RUN: + if (!(sc->runbmap & bid)) { + if(sc->running++) + restart_ratectl = 1; + sc->runbmap |= bid; + } + + m_freem(rvp->beacon_mbuf); + rvp->beacon_mbuf = NULL; + + switch (vap->iv_opmode) { + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + sc->ap_running |= bid; + ic->ic_opmode = vap->iv_opmode; + run_update_beacon_cb(vap); + break; + case IEEE80211_M_IBSS: + sc->adhoc_running |= bid; + if (!sc->ap_running) + ic->ic_opmode = vap->iv_opmode; + run_update_beacon_cb(vap); + break; + case IEEE80211_M_STA: + sc->sta_running |= bid; + if (!sc->ap_running && !sc->adhoc_running) + ic->ic_opmode = vap->iv_opmode; + + /* read statistic counters (clear on read) */ + run_read_region_1(sc, RT2860_TX_STA_CNT0, + (uint8_t *)sta, sizeof sta); + + break; + default: + ic->ic_opmode = vap->iv_opmode; + break; + } + + if (vap->iv_opmode != IEEE80211_M_MONITOR) { + struct ieee80211_node *ni; + + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { + RUN_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (-1); + } + run_updateslot(ic); + run_enable_mrr(sc); + run_set_txpreamble(sc); + run_set_basicrates(sc); + ni = ieee80211_ref_node(vap->iv_bss); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + run_set_bssid(sc, sc->sc_bssid); + ieee80211_free_node(ni); + run_enable_tsf_sync(sc); + + /* enable automatic rate adaptation */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + ratectl |= bid; + } else + run_enable_tsf(sc); + + /* turn link LED on */ + run_set_leds(sc, RT2860_LED_RADIO | + (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? + RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ)); + + break; + default: + RUN_DPRINTF(sc, RUN_DEBUG_STATE, "undefined state\n"); + break; + } + + /* restart amrr for running VAPs */ + if ((sc->ratectl_run = ratectl) && restart_ratectl) + usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); + + RUN_UNLOCK(sc); + IEEE80211_LOCK(ic); + + return(rvp->newstate(vap, nstate, arg)); +} + +static int +run_wme_update(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + const struct wmeParams *ac = + ic->ic_wme.wme_chanParams.cap_wmeParams; + int aci, error = 0; + + /* update MAC TX configuration registers */ + RUN_LOCK(sc); + for (aci = 0; aci < WME_NUM_AC; aci++) { + error = run_write(sc, RT2860_EDCA_AC_CFG(aci), + ac[aci].wmep_logcwmax << 16 | + ac[aci].wmep_logcwmin << 12 | + ac[aci].wmep_aifsn << 8 | + ac[aci].wmep_txopLimit); + if (error) goto err; + } + + /* update SCH/DMA registers too */ + error = run_write(sc, RT2860_WMM_AIFSN_CFG, + ac[WME_AC_VO].wmep_aifsn << 12 | + ac[WME_AC_VI].wmep_aifsn << 8 | + ac[WME_AC_BK].wmep_aifsn << 4 | + ac[WME_AC_BE].wmep_aifsn); + if (error) goto err; + error = run_write(sc, RT2860_WMM_CWMIN_CFG, + ac[WME_AC_VO].wmep_logcwmin << 12 | + ac[WME_AC_VI].wmep_logcwmin << 8 | + ac[WME_AC_BK].wmep_logcwmin << 4 | + ac[WME_AC_BE].wmep_logcwmin); + if (error) goto err; + error = run_write(sc, RT2860_WMM_CWMAX_CFG, + ac[WME_AC_VO].wmep_logcwmax << 12 | + ac[WME_AC_VI].wmep_logcwmax << 8 | + ac[WME_AC_BK].wmep_logcwmax << 4 | + ac[WME_AC_BE].wmep_logcwmax); + if (error) goto err; + error = run_write(sc, RT2860_WMM_TXOP0_CFG, + ac[WME_AC_BK].wmep_txopLimit << 16 | + ac[WME_AC_BE].wmep_txopLimit); + if (error) goto err; + error = run_write(sc, RT2860_WMM_TXOP1_CFG, + ac[WME_AC_VO].wmep_txopLimit << 16 | + ac[WME_AC_VI].wmep_txopLimit); + +err: + RUN_UNLOCK(sc); + if (error) + RUN_DPRINTF(sc, RUN_DEBUG_USB, "WME update failed\n"); + + return (error); +} + +static void +run_key_set_cb(void *arg) +{ + struct run_cmdq *cmdq = arg; + struct ieee80211vap *vap = cmdq->arg1; + struct ieee80211_key *k = cmdq->k; + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + struct ieee80211_node *ni; + u_int cipher = k->wk_cipher->ic_cipher; + uint32_t attr; + uint16_t base, associd; + uint8_t mode, wcid, iv[8]; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac); + else + ni = vap->iv_bss; + associd = (ni != NULL) ? ni->ni_associd : 0; + + /* map net80211 cipher to RT2860 security mode */ + switch (cipher) { + case IEEE80211_CIPHER_WEP: + if(k->wk_keylen < 8) + mode = RT2860_MODE_WEP40; + else + mode = RT2860_MODE_WEP104; + break; + case IEEE80211_CIPHER_TKIP: + mode = RT2860_MODE_TKIP; + break; + case IEEE80211_CIPHER_AES_CCM: + mode = RT2860_MODE_AES_CCMP; + break; + default: + RUN_DPRINTF(sc, RUN_DEBUG_KEY, "undefined case\n"); + return; + } + + RUN_DPRINTF(sc, RUN_DEBUG_KEY, + "associd=%x, keyix=%d, mode=%x, type=%s, tx=%s, rx=%s\n", + associd, k->wk_keyix, mode, + (k->wk_flags & IEEE80211_KEY_GROUP) ? "group" : "pairwise", + (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off", + (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off"); + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + wcid = 0; /* NB: update WCID0 for group keys */ + base = RT2860_SKEY(RUN_VAP(vap)->rvp_id, k->wk_keyix); + } else { + wcid = (vap->iv_opmode == IEEE80211_M_STA) ? + 1 : RUN_AID2WCID(associd); + base = RT2860_PKEY(wcid); + } + + if (cipher == IEEE80211_CIPHER_TKIP) { + if(run_write_region_1(sc, base, k->wk_key, 16)) + return; + if(run_write_region_1(sc, base + 16, &k->wk_key[16], 8)) /* wk_txmic */ + return; + if(run_write_region_1(sc, base + 24, &k->wk_key[24], 8)) /* wk_rxmic */ + return; + } else { + /* roundup len to 16-bit: XXX fix write_region_1() instead */ + if(run_write_region_1(sc, base, k->wk_key, (k->wk_keylen + 1) & ~1)) + return; + } + + if (!(k->wk_flags & IEEE80211_KEY_GROUP) || + (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) { + /* set initial packet number in IV+EIV */ + if (cipher == IEEE80211_CIPHER_WEP) { + memset(iv, 0, sizeof iv); + iv[3] = vap->iv_def_txkey << 6; + } else { + if (cipher == IEEE80211_CIPHER_TKIP) { + iv[0] = k->wk_keytsc >> 8; + iv[1] = (iv[0] | 0x20) & 0x7f; + iv[2] = k->wk_keytsc; + } else /* CCMP */ { + iv[0] = k->wk_keytsc; + iv[1] = k->wk_keytsc >> 8; + iv[2] = 0; + } + iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV; + iv[4] = k->wk_keytsc >> 16; + iv[5] = k->wk_keytsc >> 24; + iv[6] = k->wk_keytsc >> 32; + iv[7] = k->wk_keytsc >> 40; + } + if (run_write_region_1(sc, RT2860_IVEIV(wcid), iv, 8)) + return; + } + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + /* install group key */ + if (run_read(sc, RT2860_SKEY_MODE_0_7, &attr)) + return; + attr &= ~(0xf << (k->wk_keyix * 4)); + attr |= mode << (k->wk_keyix * 4); + if (run_write(sc, RT2860_SKEY_MODE_0_7, attr)) + return; + } else { + /* install pairwise key */ + if (run_read(sc, RT2860_WCID_ATTR(wcid), &attr)) + return; + attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN; + if (run_write(sc, RT2860_WCID_ATTR(wcid), attr)) + return; + } + + /* TODO create a pass-thru key entry? */ + + /* need wcid to delete the right key later */ + k->wk_pad = wcid; +} + +/* + * Don't have to be deferred, but in order to keep order of + * execution, i.e. with run_key_delete(), defer this and let + * run_cmdq_cb() maintain the order. + * + * return 0 on error + */ +static int +run_key_set(struct ieee80211vap *vap, struct ieee80211_key *k) +{ + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + uint32_t i; + + i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_key_set_cb; + sc->cmdq[i].arg0 = NULL; + sc->cmdq[i].arg1 = vap; + sc->cmdq[i].k = k; + IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr); + ieee80211_runtask(ic, &sc->cmdq_task); + + /* + * To make sure key will be set when hostapd + * calls iv_key_set() before if_init(). + */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + RUN_LOCK(sc); + sc->cmdq_key_set = RUN_CMDQ_GO; + RUN_UNLOCK(sc); + } + + return (1); +} + +/* + * If wlan is destroyed without being brought down i.e. without + * wlan down or wpa_cli terminate, this function is called after + * vap is gone. Don't refer it. + */ +static void +run_key_delete_cb(void *arg) +{ + struct run_cmdq *cmdq = arg; + struct run_softc *sc = cmdq->arg1; + struct ieee80211_key *k = &cmdq->key; + uint32_t attr; + uint8_t wcid; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + /* remove group key */ + RUN_DPRINTF(sc, RUN_DEBUG_KEY, "removing group key\n"); + run_read(sc, RT2860_SKEY_MODE_0_7, &attr); + attr &= ~(0xf << (k->wk_keyix * 4)); + run_write(sc, RT2860_SKEY_MODE_0_7, attr); + } else { + /* remove pairwise key */ + RUN_DPRINTF(sc, RUN_DEBUG_KEY, + "removing key for wcid %x\n", k->wk_pad); + /* matching wcid was written to wk_pad in run_key_set() */ + wcid = k->wk_pad; + run_read(sc, RT2860_WCID_ATTR(wcid), &attr); + attr &= ~0xf; + run_write(sc, RT2860_WCID_ATTR(wcid), attr); + run_set_region_4(sc, RT2860_WCID_ENTRY(wcid), 0, 8); + } + + k->wk_pad = 0; +} + +/* + * return 0 on error + */ +static int +run_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k) +{ + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + struct ieee80211_key *k0; + uint32_t i; + + /* + * When called back, key might be gone. So, make a copy + * of some values need to delete keys before deferring. + * But, because of LOR with node lock, cannot use lock here. + * So, use atomic instead. + */ + i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_key_delete_cb; + sc->cmdq[i].arg0 = NULL; + sc->cmdq[i].arg1 = sc; + k0 = &sc->cmdq[i].key; + k0->wk_flags = k->wk_flags; + k0->wk_keyix = k->wk_keyix; + /* matching wcid was written to wk_pad in run_key_set() */ + k0->wk_pad = k->wk_pad; + ieee80211_runtask(ic, &sc->cmdq_task); + return (1); /* return fake success */ + +} + +static void +run_ratectl_to(void *arg) +{ + struct run_softc *sc = arg; + + /* do it in a process context, so it can go sleep */ + ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task); + /* next timeout will be rescheduled in the callback task */ +} + +/* ARGSUSED */ +static void +run_ratectl_cb(void *arg, int pending) +{ + struct run_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap == NULL) + return; + + if (sc->rvp_cnt > 1 || vap->iv_opmode != IEEE80211_M_STA) { + /* + * run_reset_livelock() doesn't do anything with AMRR, + * but Ralink wants us to call it every 1 sec. So, we + * piggyback here rather than creating another callout. + * Livelock may occur only in HOSTAP or IBSS mode + * (when h/w is sending beacons). + */ + RUN_LOCK(sc); + run_reset_livelock(sc); + /* just in case, there are some stats to drain */ + run_drain_fifo(sc); + RUN_UNLOCK(sc); + } + + ieee80211_iterate_nodes(&ic->ic_sta, run_iter_func, sc); + + RUN_LOCK(sc); + if(sc->ratectl_run != RUN_RATECTL_OFF) + usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); + RUN_UNLOCK(sc); +} + +static void +run_drain_fifo(void *arg) +{ + struct run_softc *sc = arg; + uint32_t stat; + uint16_t (*wstat)[3]; + uint8_t wcid, mcs, pid; + int8_t retry; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + for (;;) { + /* drain Tx status FIFO (maxsize = 16) */ + run_read(sc, RT2860_TX_STAT_FIFO, &stat); + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx stat 0x%08x\n", stat); + if (!(stat & RT2860_TXQ_VLD)) + break; + + wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff; + + /* if no ACK was requested, no feedback is available */ + if (!(stat & RT2860_TXQ_ACKREQ) || wcid > RT2870_WCID_MAX || + wcid == 0) + continue; + + /* + * Even though each stat is Tx-complete-status like format, + * the device can poll stats. Because there is no guarantee + * that the referring node is still around when read the stats. + * So that, if we use ieee80211_ratectl_tx_update(), we will + * have hard time not to refer already freed node. + * + * To eliminate such page faults, we poll stats in softc. + * Then, update the rates later with ieee80211_ratectl_tx_update(). + */ + wstat = &(sc->wcid_stats[wcid]); + (*wstat)[RUN_TXCNT]++; + if (stat & RT2860_TXQ_OK) + (*wstat)[RUN_SUCCESS]++; + else + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + /* + * Check if there were retries, ie if the Tx success rate is + * different from the requested rate. Note that it works only + * because we do not allow rate fallback from OFDM to CCK. + */ + mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f; + pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf; + if ((retry = pid -1 - mcs) > 0) { + (*wstat)[RUN_TXCNT] += retry; + (*wstat)[RUN_RETRY] += retry; + } + } + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt); + + sc->fifo_cnt = 0; +} + +static void +run_iter_func(void *arg, struct ieee80211_node *ni) +{ + struct run_softc *sc = arg; + struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; + struct ieee80211vap *vap = ni->ni_vap; + struct run_node *rn = RUN_NODE(ni); + union run_stats sta[2]; + uint16_t (*wstat)[3]; + int error; + + RUN_LOCK(sc); + + /* Check for special case */ + if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA && + ni != vap->iv_bss) + goto fail; + + txs->flags = IEEE80211_RATECTL_TX_STATS_NODE | + IEEE80211_RATECTL_TX_STATS_RETRIES; + txs->ni = ni; + if (sc->rvp_cnt <= 1 && (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_STA)) { + /* read statistic counters (clear on read) and update AMRR state */ + error = run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta, + sizeof sta); + if (error != 0) + goto fail; + + /* count failed TX as errors */ + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, + le16toh(sta[0].error.fail)); + + txs->nretries = le16toh(sta[1].tx.retry); + txs->nsuccess = le16toh(sta[1].tx.success); + /* nretries??? */ + txs->nframes = txs->nretries + txs->nsuccess + + le16toh(sta[0].error.fail); + + RUN_DPRINTF(sc, RUN_DEBUG_RATE, + "retrycnt=%d success=%d failcnt=%d\n", + txs->nretries, txs->nsuccess, le16toh(sta[0].error.fail)); + } else { + wstat = &(sc->wcid_stats[RUN_AID2WCID(ni->ni_associd)]); + + if (wstat == &(sc->wcid_stats[0]) || + wstat > &(sc->wcid_stats[RT2870_WCID_MAX])) + goto fail; + + txs->nretries = (*wstat)[RUN_RETRY]; + txs->nsuccess = (*wstat)[RUN_SUCCESS]; + txs->nframes = (*wstat)[RUN_TXCNT]; + RUN_DPRINTF(sc, RUN_DEBUG_RATE, + "retrycnt=%d txcnt=%d success=%d\n", + txs->nretries, txs->nframes, txs->nsuccess); + + memset(wstat, 0, sizeof(*wstat)); + } + + ieee80211_ratectl_tx_update(vap, txs); + rn->amrr_ridx = ieee80211_ratectl_rate(ni, NULL, 0); + +fail: + RUN_UNLOCK(sc); + + RUN_DPRINTF(sc, RUN_DEBUG_RATE, "ridx=%d\n", rn->amrr_ridx); +} + +static void +run_newassoc_cb(void *arg) +{ + struct run_cmdq *cmdq = arg; + struct ieee80211_node *ni = cmdq->arg1; + struct run_softc *sc = ni->ni_vap->iv_ic->ic_softc; + uint8_t wcid = cmdq->wcid; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + run_write_region_1(sc, RT2860_WCID_ENTRY(wcid), + ni->ni_macaddr, IEEE80211_ADDR_LEN); + + memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid])); +} + +static void +run_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct run_node *rn = RUN_NODE(ni); + struct ieee80211_rateset *rs = &ni->ni_rates; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + uint8_t rate; + uint8_t ridx; + uint8_t wcid; + int i, j; + + wcid = (vap->iv_opmode == IEEE80211_M_STA) ? + 1 : RUN_AID2WCID(ni->ni_associd); + + if (wcid > RT2870_WCID_MAX) { + device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid); + return; + } + + /* only interested in true associations */ + if (isnew && ni->ni_associd != 0) { + + /* + * This function could is called though timeout function. + * Need to defer. + */ + uint32_t cnt = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_STATE, "cmdq_store=%d\n", cnt); + sc->cmdq[cnt].func = run_newassoc_cb; + sc->cmdq[cnt].arg0 = NULL; + sc->cmdq[cnt].arg1 = ni; + sc->cmdq[cnt].wcid = wcid; + ieee80211_runtask(ic, &sc->cmdq_task); + } + + RUN_DPRINTF(sc, RUN_DEBUG_STATE, + "new assoc isnew=%d associd=%x addr=%s\n", + isnew, ni->ni_associd, ether_sprintf(ni->ni_macaddr)); + + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i] & IEEE80211_RATE_VAL; + /* convert 802.11 rate to hardware rate index */ + for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + rn->ridx[i] = ridx; + /* determine rate of control response frames */ + for (j = i; j >= 0; j--) { + if ((rs->rs_rates[j] & IEEE80211_RATE_BASIC) && + rt2860_rates[rn->ridx[i]].phy == + rt2860_rates[rn->ridx[j]].phy) + break; + } + if (j >= 0) { + rn->ctl_ridx[i] = rn->ridx[j]; + } else { + /* no basic rate found, use mandatory one */ + rn->ctl_ridx[i] = rt2860_rates[ridx].ctl_ridx; + } + RUN_DPRINTF(sc, RUN_DEBUG_STATE | RUN_DEBUG_RATE, + "rate=0x%02x ridx=%d ctl_ridx=%d\n", + rs->rs_rates[i], rn->ridx[i], rn->ctl_ridx[i]); + } + rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate; + for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + rn->mgt_ridx = ridx; + RUN_DPRINTF(sc, RUN_DEBUG_STATE | RUN_DEBUG_RATE, + "rate=%d, mgmt_ridx=%d\n", rate, rn->mgt_ridx); + + RUN_LOCK(sc); + if(sc->ratectl_run != RUN_RATECTL_OFF) + usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); + RUN_UNLOCK(sc); +} + +/* + * Return the Rx chain with the highest RSSI for a given frame. + */ +static __inline uint8_t +run_maxrssi_chain(struct run_softc *sc, const struct rt2860_rxwi *rxwi) +{ + uint8_t rxchain = 0; + + if (sc->nrxchains > 1) { + if (rxwi->rssi[1] > rxwi->rssi[rxchain]) + rxchain = 1; + if (sc->nrxchains > 2) + if (rxwi->rssi[2] > rxwi->rssi[rxchain]) + rxchain = 2; + } + return (rxchain); +} + +static void +run_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, + const struct ieee80211_rx_stats *rxs, int rssi, int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct run_softc *sc = vap->iv_ic->ic_softc; + struct run_vap *rvp = RUN_VAP(vap); + uint64_t ni_tstamp, rx_tstamp; + + rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); + + if (vap->iv_state == IEEE80211_S_RUN && + (subtype == IEEE80211_FC0_SUBTYPE_BEACON || + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { + ni_tstamp = le64toh(ni->ni_tstamp.tsf); + RUN_LOCK(sc); + run_get_tsf(sc, &rx_tstamp); + RUN_UNLOCK(sc); + rx_tstamp = le64toh(rx_tstamp); + + if (ni_tstamp >= rx_tstamp) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV | RUN_DEBUG_BEACON, + "ibss merge, tsf %ju tstamp %ju\n", + (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); + (void) ieee80211_ibss_merge(ni); + } + } +} + +static void +run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct rt2870_rxd *rxd; + struct rt2860_rxwi *rxwi; + uint32_t flags; + uint16_t len, rxwisize; + uint8_t ant, rssi; + int8_t nf; + + rxwi = mtod(m, struct rt2860_rxwi *); + len = le16toh(rxwi->len) & 0xfff; + rxwisize = sizeof(struct rt2860_rxwi); + if (sc->mac_ver == 0x5592) + rxwisize += sizeof(uint64_t); + else if (sc->mac_ver == 0x3593) + rxwisize += sizeof(uint32_t); + if (__predict_false(len > dmalen)) { + m_freem(m); + counter_u64_add(ic->ic_ierrors, 1); + RUN_DPRINTF(sc, RUN_DEBUG_RECV, + "bad RXWI length %u > %u\n", len, dmalen); + return; + } + /* Rx descriptor is located at the end */ + rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen); + flags = le32toh(rxd->flags); + + if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) { + m_freem(m); + counter_u64_add(ic->ic_ierrors, 1); + RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s error.\n", + (flags & RT2860_RX_CRCERR)?"CRC":"ICV"); + return; + } + + m->m_data += rxwisize; + m->m_pkthdr.len = m->m_len -= rxwisize; + + wh = mtod(m, struct ieee80211_frame *); + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + m->m_flags |= M_WEP; + } + + if (flags & RT2860_RX_L2PAD) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV, + "received RT2860_RX_L2PAD frame\n"); + len += 2; + } + + ni = ieee80211_find_rxnode(ic, + mtod(m, struct ieee80211_frame_min *)); + + if (__predict_false(flags & RT2860_RX_MICERR)) { + /* report MIC failures to net80211 for TKIP */ + if (ni != NULL) + ieee80211_notify_michael_failure(ni->ni_vap, wh, + rxwi->keyidx); + m_freem(m); + counter_u64_add(ic->ic_ierrors, 1); + RUN_DPRINTF(sc, RUN_DEBUG_RECV, + "MIC error. Someone is lying.\n"); + return; + } + + ant = run_maxrssi_chain(sc, rxwi); + rssi = rxwi->rssi[ant]; + nf = run_rssi2dbm(sc, rssi, ant); + + m->m_pkthdr.len = m->m_len = len; + + if (__predict_false(ieee80211_radiotap_active(ic))) { + struct run_rx_radiotap_header *tap = &sc->sc_rxtap; + uint16_t phy; + + tap->wr_flags = 0; + tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + tap->wr_antsignal = rssi; + tap->wr_antenna = ant; + tap->wr_dbm_antsignal = run_rssi2dbm(sc, rssi, ant); + tap->wr_rate = 2; /* in case it can't be found below */ + RUN_LOCK(sc); + run_get_tsf(sc, &tap->wr_tsf); + RUN_UNLOCK(sc); + phy = le16toh(rxwi->phy); + switch (phy & RT2860_PHY_MODE) { + case RT2860_PHY_CCK: + switch ((phy & RT2860_PHY_MCS) & ~RT2860_PHY_SHPRE) { + case 0: tap->wr_rate = 2; break; + case 1: tap->wr_rate = 4; break; + case 2: tap->wr_rate = 11; break; + case 3: tap->wr_rate = 22; break; + } + if (phy & RT2860_PHY_SHPRE) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + break; + case RT2860_PHY_OFDM: + switch (phy & RT2860_PHY_MCS) { + case 0: tap->wr_rate = 12; break; + case 1: tap->wr_rate = 18; break; + case 2: tap->wr_rate = 24; break; + case 3: tap->wr_rate = 36; break; + case 4: tap->wr_rate = 48; break; + case 5: tap->wr_rate = 72; break; + case 6: tap->wr_rate = 96; break; + case 7: tap->wr_rate = 108; break; + } + break; + } + } + + if (ni != NULL) { + (void)ieee80211_input(ni, m, rssi, nf); + ieee80211_free_node(ni); + } else { + (void)ieee80211_input_all(ic, m, rssi, nf); + } +} + +static void +run_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct run_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct mbuf *m = NULL; + struct mbuf *m0; + uint32_t dmalen; + uint16_t rxwisize; + int xferlen; + + rxwisize = sizeof(struct rt2860_rxwi); + if (sc->mac_ver == 0x5592) + rxwisize += sizeof(uint64_t); + else if (sc->mac_ver == 0x3593) + rxwisize += sizeof(uint32_t); + + usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + RUN_DPRINTF(sc, RUN_DEBUG_RECV, + "rx done, actlen=%d\n", xferlen); + + if (xferlen < (int)(sizeof(uint32_t) + rxwisize + + sizeof(struct rt2870_rxd))) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, + "xfer too short %d\n", xferlen); + goto tr_setup; + } + + m = sc->rx_m; + sc->rx_m = NULL; + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + if (sc->rx_m == NULL) { + sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, + MJUMPAGESIZE /* xfer can be bigger than MCLBYTES */); + } + if (sc->rx_m == NULL) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV | RUN_DEBUG_RECV_DESC, + "could not allocate mbuf - idle with stall\n"); + counter_u64_add(ic->ic_ierrors, 1); + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frames(xfer, 0); + } else { + /* + * Directly loading a mbuf cluster into DMA to + * save some data copying. This works because + * there is only one cluster. + */ + usbd_xfer_set_frame_data(xfer, 0, + mtod(sc->rx_m, caddr_t), RUN_MAX_RXSZ); + usbd_xfer_set_frames(xfer, 1); + } + usbd_transfer_submit(xfer); + break; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + if (error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + if (sc->rx_m != NULL) { + m_freem(sc->rx_m); + sc->rx_m = NULL; + } + break; + } + + if (m == NULL) + return; + + /* inputting all the frames must be last */ + + RUN_UNLOCK(sc); + + m->m_pkthdr.len = m->m_len = xferlen; + + /* HW can aggregate multiple 802.11 frames in a single USB xfer */ + for(;;) { + dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff; + + if ((dmalen >= (uint32_t)-8) || (dmalen == 0) || + ((dmalen & 3) != 0)) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, + "bad DMA length %u\n", dmalen); + break; + } + if ((dmalen + 8) > (uint32_t)xferlen) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, + "bad DMA length %u > %d\n", + dmalen + 8, xferlen); + break; + } + + /* If it is the last one or a single frame, we won't copy. */ + if ((xferlen -= dmalen + 8) <= 8) { + /* trim 32-bit DMA-len header */ + m->m_data += 4; + m->m_pkthdr.len = m->m_len -= 4; + run_rx_frame(sc, m, dmalen); + m = NULL; /* don't free source buffer */ + break; + } + + /* copy aggregated frames to another mbuf */ + m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (__predict_false(m0 == NULL)) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC, + "could not allocate mbuf\n"); + counter_u64_add(ic->ic_ierrors, 1); + break; + } + m_copydata(m, 4 /* skip 32-bit DMA-len header */, + dmalen + sizeof(struct rt2870_rxd), mtod(m0, caddr_t)); + m0->m_pkthdr.len = m0->m_len = + dmalen + sizeof(struct rt2870_rxd); + run_rx_frame(sc, m0, dmalen); + + /* update data ptr */ + m->m_data += dmalen + 8; + m->m_pkthdr.len = m->m_len -= dmalen + 8; + } + + /* make sure we free the source buffer, if any */ + m_freem(m); + + RUN_LOCK(sc); +} + +static void +run_tx_free(struct run_endpoint_queue *pq, + struct run_tx_data *data, int txerr) +{ + + ieee80211_tx_complete(data->ni, data->m, txerr); + + data->m = NULL; + data->ni = NULL; + + STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); + pq->tx_nfree++; +} + +static void +run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index) +{ + struct run_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct run_tx_data *data; + struct ieee80211vap *vap = NULL; + struct usb_page_cache *pc; + struct run_endpoint_queue *pq = &sc->sc_epq[index]; + struct mbuf *m; + usb_frlength_t size; + int actlen; + int sumlen; + + usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, + "transfer complete: %d bytes @ index %d\n", actlen, index); + + data = usbd_xfer_get_priv(xfer); + run_tx_free(pq, data, 0); + usbd_xfer_set_priv(xfer, NULL); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&pq->tx_qh); + if (data == NULL) + break; + + STAILQ_REMOVE_HEAD(&pq->tx_qh, next); + + m = data->m; + size = (sc->mac_ver == 0x5592) ? + sizeof(data->desc) + sizeof(uint32_t) : sizeof(data->desc); + if ((m->m_pkthdr.len + + size + 3 + 8) > RUN_MAX_TXSZ) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT_DESC | RUN_DEBUG_USB, + "data overflow, %u bytes\n", m->m_pkthdr.len); + run_tx_free(pq, data, 1); + goto tr_setup; + } + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &data->desc, size); + usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len); + size += m->m_pkthdr.len; + /* + * Align end on a 4-byte boundary, pad 8 bytes (CRC + + * 4-byte padding), and be sure to zero those trailing + * bytes: + */ + usbd_frame_zero(pc, size, ((-size) & 3) + 8); + size += ((-size) & 3) + 8; + + vap = data->ni->ni_vap; + if (ieee80211_radiotap_active_vap(vap)) { + struct run_tx_radiotap_header *tap = &sc->sc_txtap; + struct rt2860_txwi *txwi = + (struct rt2860_txwi *)(&data->desc + sizeof(struct rt2870_txd)); + tap->wt_flags = 0; + tap->wt_rate = rt2860_rates[data->ridx].rate; + tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); + tap->wt_hwqueue = index; + if (le16toh(txwi->phy) & RT2860_PHY_SHPRE) + tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + ieee80211_radiotap_tx(vap, m); + } + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, + "sending frame len=%u/%u @ index %d\n", + m->m_pkthdr.len, size, index); + + usbd_xfer_set_frame_len(xfer, 0, size); + usbd_xfer_set_priv(xfer, data); + usbd_transfer_submit(xfer); + run_start(sc); + + break; + + default: + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, + "USB transfer error, %s\n", usbd_errstr(error)); + + data = usbd_xfer_get_priv(xfer); + + if (data != NULL) { + if(data->ni != NULL) + vap = data->ni->ni_vap; + run_tx_free(pq, data, error); + usbd_xfer_set_priv(xfer, NULL); + } + + if (vap == NULL) + vap = TAILQ_FIRST(&ic->ic_vaps); + + if (error != USB_ERR_CANCELLED) { + if (error == USB_ERR_TIMEOUT) { + device_printf(sc->sc_dev, "device timeout\n"); + uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, + "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_usb_timeout_cb; + sc->cmdq[i].arg0 = vap; + ieee80211_runtask(ic, &sc->cmdq_task); + } + + /* + * Try to clear stall first, also if other + * errors occur, hence clearing stall + * introduces a 50 ms delay: + */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +run_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 0); +} + +static void +run_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 1); +} + +static void +run_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 2); +} + +static void +run_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 3); +} + +static void +run_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 4); +} + +static void +run_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 5); +} + +static void +run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data) +{ + struct mbuf *m = data->m; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = data->ni->ni_vap; + struct ieee80211_frame *wh; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + uint16_t xferlen, txwisize; + uint16_t mcs; + uint8_t ridx = data->ridx; + uint8_t pad; + + /* get MCS code from rate index */ + mcs = rt2860_rates[ridx].mcs; + + txwisize = (sc->mac_ver == 0x5592) ? + sizeof(*txwi) + sizeof(uint32_t) : sizeof(*txwi); + xferlen = txwisize + m->m_pkthdr.len; + + /* roundup to 32-bit alignment */ + xferlen = (xferlen + 3) & ~3; + + txd = (struct rt2870_txd *)&data->desc; + txd->len = htole16(xferlen); + + wh = mtod(m, struct ieee80211_frame *); + + /* + * Ether both are true or both are false, the header + * are nicely aligned to 32-bit. So, no L2 padding. + */ + if(IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh)) + pad = 0; + else + pad = 2; + + /* setup TX Wireless Information */ + txwi = (struct rt2860_txwi *)(txd + 1); + txwi->len = htole16(m->m_pkthdr.len - pad); + if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { + mcs |= RT2860_PHY_CCK; + if (ridx != RT2860_RIDX_CCK1 && + (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_PHY_SHPRE; + } else + mcs |= RT2860_PHY_OFDM; + txwi->phy = htole16(mcs); + + /* check if RTS/CTS or CTS-to-self protection is required */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold || + ((ic->ic_flags & IEEE80211_F_USEPROT) && + rt2860_rates[ridx].phy == IEEE80211_T_OFDM))) + txwi->txop |= RT2860_TX_TXOP_HT; + else + txwi->txop |= RT2860_TX_TXOP_BACKOFF; + + if (vap->iv_opmode != IEEE80211_M_STA && !IEEE80211_QOS_HAS_SEQ(wh)) + txwi->xflags |= RT2860_TX_NSEQ; +} + +/* This function must be called locked */ +static int +run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame *wh; + struct ieee80211_channel *chan; + const struct ieee80211_txparam *tp; + struct run_node *rn = RUN_NODE(ni); + struct run_tx_data *data; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + uint16_t qos; + uint16_t dur; + uint16_t qid; + uint8_t type; + uint8_t tid; + uint8_t ridx; + uint8_t ctl_ridx; + uint8_t qflags; + uint8_t xflags = 0; + int hasqos; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m, struct ieee80211_frame *); + + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* + * There are 7 bulk endpoints: 1 for RX + * and 6 for TX (4 EDCAs + HCCA + Prio). + * Update 03-14-2009: some devices like the Planex GW-US300MiniS + * seem to have only 4 TX bulk endpoints (Fukaumi Naoki). + */ + if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) { + uint8_t *frm; + + if(IEEE80211_HAS_ADDR4(wh)) + frm = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos; + else + frm =((struct ieee80211_qosframe *)wh)->i_qos; + + qos = le16toh(*(const uint16_t *)frm); + tid = qos & IEEE80211_QOS_TID; + qid = TID_TO_WME_AC(tid); + } else { + qos = 0; + tid = 0; + qid = WME_AC_BE; + } + qflags = (qid < 4) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_HCCA; + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "qos %d\tqid %d\ttid %d\tqflags %x\n", + qos, qid, tid, qflags); + + chan = (ni->ni_chan != IEEE80211_CHAN_ANYC)?ni->ni_chan:ic->ic_curchan; + tp = &vap->iv_txparms[ieee80211_chan2mode(chan)]; + + /* pickup a rate index */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) { + ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? + RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; + ctl_ridx = rt2860_rates[ridx].ctl_ridx; + } else { + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + ridx = rn->fix_ridx; + else + ridx = rn->amrr_ridx; + ctl_ridx = rt2860_rates[ridx].ctl_ridx; + } + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK)) { + xflags |= RT2860_TX_ACK; + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + dur = rt2860_rates[ctl_ridx].sp_ack_dur; + else + dur = rt2860_rates[ctl_ridx].lp_ack_dur; + USETW(wh->i_dur, dur); + } + + /* reserve slots for mgmt packets, just in case */ + if (sc->sc_epq[qid].tx_nfree < 3) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx ring %d is full\n", qid); + return (-1); + } + + data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh); + STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next); + sc->sc_epq[qid].tx_nfree--; + + txd = (struct rt2870_txd *)&data->desc; + txd->flags = qflags; + txwi = (struct rt2860_txwi *)(txd + 1); + txwi->xflags = xflags; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + txwi->wcid = 0; + else + txwi->wcid = (vap->iv_opmode == IEEE80211_M_STA) ? + 1 : RUN_AID2WCID(ni->ni_associd); + + /* clear leftover garbage bits */ + txwi->flags = 0; + txwi->txop = 0; + + data->m = m; + data->ni = ni; + data->ridx = ridx; + + run_set_tx_desc(sc, data); + + /* + * The chip keeps track of 2 kind of Tx stats, + * * TX_STAT_FIFO, for per WCID stats, and + * * TX_STA_CNT0 for all-TX-in-one stats. + * + * To use FIFO stats, we need to store MCS into the driver-private + * PacketID field. So that, we can tell whose stats when we read them. + * We add 1 to the MCS because setting the PacketID field to 0 means + * that we don't want feedback in TX_STAT_FIFO. + * And, that's what we want for STA mode, since TX_STA_CNT0 does the job. + * + * FIFO stats doesn't count Tx with WCID 0xff, so we do this in run_tx(). + */ + if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_MBSS) { + uint16_t pid = (rt2860_rates[ridx].mcs + 1) & 0xf; + txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT); + + /* + * Unlike PCI based devices, we don't get any interrupt from + * USB devices, so we simulate FIFO-is-full interrupt here. + * Ralink recommends to drain FIFO stats every 100 ms, but 16 slots + * quickly get fulled. To prevent overflow, increment a counter on + * every FIFO stat request, so we know how many slots are left. + * We do this only in HOSTAP or multiple vap mode since FIFO stats + * are used only in those modes. + * We just drain stats. AMRR gets updated every 1 sec by + * run_ratectl_cb() via callout. + * Call it early. Otherwise overflow. + */ + if (sc->fifo_cnt++ == 10) { + /* + * With multiple vaps or if_bridge, if_start() is called + * with a non-sleepable lock, tcpinp. So, need to defer. + */ + uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_drain_fifo; + sc->cmdq[i].arg0 = sc; + ieee80211_runtask(ic, &sc->cmdq_task); + } + } + + STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next); + + usbd_transfer_start(sc->sc_xfer[qid]); + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, + "sending data frame len=%d rate=%d qid=%d\n", + m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) + + sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate, qid); + + return (0); +} + +static int +run_tx_mgt(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct run_node *rn = RUN_NODE(ni); + struct run_tx_data *data; + struct ieee80211_frame *wh; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + uint16_t dur; + uint8_t ridx = rn->mgt_ridx; + uint8_t type; + uint8_t xflags = 0; + uint8_t wflags = 0; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m, struct ieee80211_frame *); + + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* tell hardware to add timestamp for probe responses */ + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) + wflags |= RT2860_TX_TS; + else if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + xflags |= RT2860_TX_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + USETW(wh->i_dur, dur); + } + + if (sc->sc_epq[0].tx_nfree == 0) + /* let caller free mbuf */ + return (EIO); + data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); + STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); + sc->sc_epq[0].tx_nfree--; + + txd = (struct rt2870_txd *)&data->desc; + txd->flags = RT2860_TX_QSEL_EDCA; + txwi = (struct rt2860_txwi *)(txd + 1); + txwi->wcid = 0xff; + txwi->flags = wflags; + txwi->xflags = xflags; + txwi->txop = 0; /* clear leftover garbage bits */ + + data->m = m; + data->ni = ni; + data->ridx = ridx; + + run_set_tx_desc(sc, data); + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n", + m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) + + sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate); + + STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); + + usbd_transfer_start(sc->sc_xfer[0]); + + return (0); +} + +static int +run_sendprot(struct run_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + struct run_tx_data *data; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + struct mbuf *mprot; + int ridx; + int protrate; + int ackrate; + int pktlen; + int isshort; + uint16_t dur; + uint8_t type; + uint8_t wflags = 0; + uint8_t xflags = 0; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, + ("protection %d", prot)); + + wh = mtod(m, struct ieee80211_frame *); + pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + protrate = ieee80211_ctl_rate(ic->ic_rt, rate); + ackrate = ieee80211_ack_rate(ic->ic_rt, rate); + + isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; + dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort) + + ieee80211_ack_duration(ic->ic_rt, rate, isshort); + wflags = RT2860_TX_FRAG; + + /* check that there are free slots before allocating the mbuf */ + if (sc->sc_epq[0].tx_nfree == 0) + /* let caller free mbuf */ + return (ENOBUFS); + + if (prot == IEEE80211_PROT_RTSCTS) { + /* NB: CTS is the same size as an ACK */ + dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); + xflags |= RT2860_TX_ACK; + mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); + } else { + mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); + } + if (mprot == NULL) { + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "could not allocate mbuf\n"); + return (ENOBUFS); + } + + data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); + STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); + sc->sc_epq[0].tx_nfree--; + + txd = (struct rt2870_txd *)&data->desc; + txd->flags = RT2860_TX_QSEL_EDCA; + txwi = (struct rt2860_txwi *)(txd + 1); + txwi->wcid = 0xff; + txwi->flags = wflags; + txwi->xflags = xflags; + txwi->txop = 0; /* clear leftover garbage bits */ + + data->m = mprot; + data->ni = ieee80211_ref_node(ni); + + for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == protrate) + break; + data->ridx = ridx; + + run_set_tx_desc(sc, data); + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending prot len=%u rate=%u\n", + m->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); + + usbd_transfer_start(sc->sc_xfer[0]); + + return (0); +} + +static int +run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + struct run_tx_data *data; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + uint8_t type; + uint8_t ridx; + uint8_t rate; + uint8_t opflags = 0; + uint8_t xflags = 0; + int error; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + KASSERT(params != NULL, ("no raw xmit params")); + + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { + /* let caller free mbuf */ + return (EINVAL); + } + + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + xflags |= RT2860_TX_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { + error = run_sendprot(sc, m, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + if (error) { + /* let caller free mbuf */ + return error; + } + opflags |= /*XXX RT2573_TX_LONG_RETRY |*/ RT2860_TX_TXOP_SIFS; + } + + if (sc->sc_epq[0].tx_nfree == 0) { + /* let caller free mbuf */ + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, + "sending raw frame, but tx ring is full\n"); + return (EIO); + } + data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); + STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); + sc->sc_epq[0].tx_nfree--; + + txd = (struct rt2870_txd *)&data->desc; + txd->flags = RT2860_TX_QSEL_EDCA; + txwi = (struct rt2860_txwi *)(txd + 1); + txwi->wcid = 0xff; + txwi->xflags = xflags; + txwi->txop = opflags; + txwi->flags = 0; /* clear leftover garbage bits */ + + data->m = m; + data->ni = ni; + for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + data->ridx = ridx; + + run_set_tx_desc(sc, data); + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending raw frame len=%u rate=%u\n", + m->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); + + usbd_transfer_start(sc->sc_xfer[0]); + + return (0); +} + +static int +run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct run_softc *sc = ni->ni_ic->ic_softc; + int error = 0; + + RUN_LOCK(sc); + + /* prevent management frames from being sent if we're not ready */ + if (!(sc->sc_flags & RUN_RUNNING)) { + error = ENETDOWN; + goto done; + } + + if (params == NULL) { + /* tx mgt packet */ + if ((error = run_tx_mgt(sc, m, ni)) != 0) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "mgt tx failed\n"); + goto done; + } + } else { + /* tx raw packet with param */ + if ((error = run_tx_param(sc, m, ni, params)) != 0) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx with param failed\n"); + goto done; + } + } + +done: + RUN_UNLOCK(sc); + + if (error != 0) { + if(m != NULL) + m_freem(m); + } + + return (error); +} + +static int +run_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct run_softc *sc = ic->ic_softc; + int error; + + RUN_LOCK(sc); + if ((sc->sc_flags & RUN_RUNNING) == 0) { + RUN_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RUN_UNLOCK(sc); + return (error); + } + run_start(sc); + RUN_UNLOCK(sc); + + return (0); +} + +static void +run_start(struct run_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if ((sc->sc_flags & RUN_RUNNING) == 0) + return; + + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (run_tx(sc, m, ni) != 0) { + mbufq_prepend(&sc->sc_snd, m); + break; + } + } +} + +static void +run_parent(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + int startall = 0; + + RUN_LOCK(sc); + if (sc->sc_detached) { + RUN_UNLOCK(sc); + return; + } + + if (ic->ic_nrunning > 0) { + if (!(sc->sc_flags & RUN_RUNNING)) { + startall = 1; + run_init_locked(sc); + } else + run_update_promisc_locked(sc); + } else if ((sc->sc_flags & RUN_RUNNING) && sc->rvp_cnt <= 1) + run_stop(sc); + RUN_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static void +run_iq_calib(struct run_softc *sc, u_int chan) +{ + uint16_t val; + + /* Tx0 IQ gain. */ + run_bbp_write(sc, 158, 0x2c); + if (chan <= 14) + run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ, &val, 1); + else if (chan <= 64) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ, + &val, 1); + } else if (chan <= 138) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ, + &val, 1); + } else if (chan <= 165) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ, + &val, 1); + } else + val = 0; + run_bbp_write(sc, 159, val); + + /* Tx0 IQ phase. */ + run_bbp_write(sc, 158, 0x2d); + if (chan <= 14) { + run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ, + &val, 1); + } else if (chan <= 64) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ, + &val, 1); + } else if (chan <= 138) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ, + &val, 1); + } else if (chan <= 165) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ, + &val, 1); + } else + val = 0; + run_bbp_write(sc, 159, val); + + /* Tx1 IQ gain. */ + run_bbp_write(sc, 158, 0x4a); + if (chan <= 14) { + run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ, + &val, 1); + } else if (chan <= 64) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ, + &val, 1); + } else if (chan <= 138) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ, + &val, 1); + } else if (chan <= 165) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ, + &val, 1); + } else + val = 0; + run_bbp_write(sc, 159, val); + + /* Tx1 IQ phase. */ + run_bbp_write(sc, 158, 0x4b); + if (chan <= 14) { + run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ, + &val, 1); + } else if (chan <= 64) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ, + &val, 1); + } else if (chan <= 138) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ, + &val, 1); + } else if (chan <= 165) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ, + &val, 1); + } else + val = 0; + run_bbp_write(sc, 159, val); + + /* RF IQ compensation control. */ + run_bbp_write(sc, 158, 0x04); + run_efuse_read(sc, RT5390_EEPROM_RF_IQ_COMPENSATION_CTL, + &val, 1); + run_bbp_write(sc, 159, val); + + /* RF IQ imbalance compensation control. */ + run_bbp_write(sc, 158, 0x03); + run_efuse_read(sc, + RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL, &val, 1); + run_bbp_write(sc, 159, val); +} + +static void +run_set_agc(struct run_softc *sc, uint8_t agc) +{ + uint8_t bbp; + + if (sc->mac_ver == 0x3572) { + run_bbp_read(sc, 27, &bbp); + bbp &= ~(0x3 << 5); + run_bbp_write(sc, 27, bbp | 0 << 5); /* select Rx0 */ + run_bbp_write(sc, 66, agc); + run_bbp_write(sc, 27, bbp | 1 << 5); /* select Rx1 */ + run_bbp_write(sc, 66, agc); + } else + run_bbp_write(sc, 66, agc); +} + +static void +run_select_chan_group(struct run_softc *sc, int group) +{ + uint32_t tmp; + uint8_t agc; + + run_bbp_write(sc, 62, 0x37 - sc->lna[group]); + run_bbp_write(sc, 63, 0x37 - sc->lna[group]); + run_bbp_write(sc, 64, 0x37 - sc->lna[group]); + if (sc->mac_ver < 0x3572) + run_bbp_write(sc, 86, 0x00); + + if (sc->mac_ver == 0x3593) { + run_bbp_write(sc, 77, 0x98); + run_bbp_write(sc, 83, (group == 0) ? 0x8a : 0x9a); + } + + if (group == 0) { + if (sc->ext_2ghz_lna) { + if (sc->mac_ver >= 0x5390) + run_bbp_write(sc, 75, 0x52); + else { + run_bbp_write(sc, 82, 0x62); + run_bbp_write(sc, 75, 0x46); + } + } else { + if (sc->mac_ver == 0x5592) { + run_bbp_write(sc, 79, 0x1c); + run_bbp_write(sc, 80, 0x0e); + run_bbp_write(sc, 81, 0x3a); + run_bbp_write(sc, 82, 0x62); + + run_bbp_write(sc, 195, 0x80); + run_bbp_write(sc, 196, 0xe0); + run_bbp_write(sc, 195, 0x81); + run_bbp_write(sc, 196, 0x1f); + run_bbp_write(sc, 195, 0x82); + run_bbp_write(sc, 196, 0x38); + run_bbp_write(sc, 195, 0x83); + run_bbp_write(sc, 196, 0x32); + run_bbp_write(sc, 195, 0x85); + run_bbp_write(sc, 196, 0x28); + run_bbp_write(sc, 195, 0x86); + run_bbp_write(sc, 196, 0x19); + } else if (sc->mac_ver >= 0x5390) + run_bbp_write(sc, 75, 0x50); + else { + run_bbp_write(sc, 82, + (sc->mac_ver == 0x3593) ? 0x62 : 0x84); + run_bbp_write(sc, 75, 0x50); + } + } + } else { + if (sc->mac_ver == 0x5592) { + run_bbp_write(sc, 79, 0x18); + run_bbp_write(sc, 80, 0x08); + run_bbp_write(sc, 81, 0x38); + run_bbp_write(sc, 82, 0x92); + + run_bbp_write(sc, 195, 0x80); + run_bbp_write(sc, 196, 0xf0); + run_bbp_write(sc, 195, 0x81); + run_bbp_write(sc, 196, 0x1e); + run_bbp_write(sc, 195, 0x82); + run_bbp_write(sc, 196, 0x28); + run_bbp_write(sc, 195, 0x83); + run_bbp_write(sc, 196, 0x20); + run_bbp_write(sc, 195, 0x85); + run_bbp_write(sc, 196, 0x7f); + run_bbp_write(sc, 195, 0x86); + run_bbp_write(sc, 196, 0x7f); + } else if (sc->mac_ver == 0x3572) + run_bbp_write(sc, 82, 0x94); + else + run_bbp_write(sc, 82, + (sc->mac_ver == 0x3593) ? 0x82 : 0xf2); + if (sc->ext_5ghz_lna) + run_bbp_write(sc, 75, 0x46); + else + run_bbp_write(sc, 75, 0x50); + } + + run_read(sc, RT2860_TX_BAND_CFG, &tmp); + tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P); + tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P; + run_write(sc, RT2860_TX_BAND_CFG, tmp); + + /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ + tmp = RT2860_RFTR_EN | RT2860_TRSW_EN | RT2860_LNA_PE0_EN; + if (sc->mac_ver == 0x3593) + tmp |= 1 << 29 | 1 << 28; + if (sc->nrxchains > 1) + tmp |= RT2860_LNA_PE1_EN; + if (group == 0) { /* 2GHz */ + tmp |= RT2860_PA_PE_G0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_G1_EN; + if (sc->mac_ver == 0x3593) { + if (sc->ntxchains > 2) + tmp |= 1 << 25; + } + } else { /* 5GHz */ + tmp |= RT2860_PA_PE_A0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_A1_EN; + } + if (sc->mac_ver == 0x3572) { + run_rt3070_rf_write(sc, 8, 0x00); + run_write(sc, RT2860_TX_PIN_CFG, tmp); + run_rt3070_rf_write(sc, 8, 0x80); + } else + run_write(sc, RT2860_TX_PIN_CFG, tmp); + + if (sc->mac_ver == 0x5592) { + run_bbp_write(sc, 195, 0x8d); + run_bbp_write(sc, 196, 0x1a); + } + + if (sc->mac_ver == 0x3593) { + run_read(sc, RT2860_GPIO_CTRL, &tmp); + tmp &= ~0x01010000; + if (group == 0) + tmp |= 0x00010000; + tmp = (tmp & ~0x00009090) | 0x00000090; + run_write(sc, RT2860_GPIO_CTRL, tmp); + } + + /* set initial AGC value */ + if (group == 0) { /* 2GHz band */ + if (sc->mac_ver >= 0x3070) + agc = 0x1c + sc->lna[0] * 2; + else + agc = 0x2e + sc->lna[0]; + } else { /* 5GHz band */ + if (sc->mac_ver == 0x5592) + agc = 0x24 + sc->lna[group] * 2; + else if (sc->mac_ver == 0x3572 || sc->mac_ver == 0x3593) + agc = 0x22 + (sc->lna[group] * 5) / 3; + else + agc = 0x32 + (sc->lna[group] * 5) / 3; + } + run_set_agc(sc, agc); +} + +static void +run_rt2870_set_chan(struct run_softc *sc, u_int chan) +{ + const struct rfprog *rfprog = rt2860_rf2850; + uint32_t r2, r3, r4; + int8_t txpow1, txpow2; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rfprog[i].chan != chan; i++); + + r2 = rfprog[i].r2; + if (sc->ntxchains == 1) + r2 |= 1 << 14; /* 1T: disable Tx chain 2 */ + if (sc->nrxchains == 1) + r2 |= 1 << 17 | 1 << 6; /* 1R: disable Rx chains 2 & 3 */ + else if (sc->nrxchains == 2) + r2 |= 1 << 6; /* 2R: disable Rx chain 3 */ + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + /* Initialize RF R3 and R4. */ + r3 = rfprog[i].r3 & 0xffffc1ff; + r4 = (rfprog[i].r4 & ~(0x001f87c0)) | (sc->freq << 15); + if (chan > 14) { + if (txpow1 >= 0) { + txpow1 = (txpow1 > 0xf) ? (0xf) : (txpow1); + r3 |= (txpow1 << 10) | (1 << 9); + } else { + txpow1 += 7; + + /* txpow1 is not possible larger than 15. */ + r3 |= (txpow1 << 10); + } + if (txpow2 >= 0) { + txpow2 = (txpow2 > 0xf) ? (0xf) : (txpow2); + r4 |= (txpow2 << 7) | (1 << 6); + } else { + txpow2 += 7; + r4 |= (txpow2 << 7); + } + } else { + /* Set Tx0 power. */ + r3 |= (txpow1 << 9); + + /* Set frequency offset and Tx1 power. */ + r4 |= (txpow2 << 6); + } + + run_rt2870_rf_write(sc, rfprog[i].r1); + run_rt2870_rf_write(sc, r2); + run_rt2870_rf_write(sc, r3 & ~(1 << 2)); + run_rt2870_rf_write(sc, r4); + + run_delay(sc, 10); + + run_rt2870_rf_write(sc, rfprog[i].r1); + run_rt2870_rf_write(sc, r2); + run_rt2870_rf_write(sc, r3 | (1 << 2)); + run_rt2870_rf_write(sc, r4); + + run_delay(sc, 10); + + run_rt2870_rf_write(sc, rfprog[i].r1); + run_rt2870_rf_write(sc, r2); + run_rt2870_rf_write(sc, r3 & ~(1 << 2)); + run_rt2870_rf_write(sc, r4); +} + +static void +run_rt3070_set_chan(struct run_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2; + uint8_t rf; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n); + + /* RT3370/RT3390: RF R3 [7:4] is not reserved bits. */ + run_rt3070_rf_read(sc, 3, &rf); + rf = (rf & ~0x0f) | rt3070_freqs[i].k; + run_rt3070_rf_write(sc, 3, rf); + + run_rt3070_rf_read(sc, 6, &rf); + rf = (rf & ~0x03) | rt3070_freqs[i].r; + run_rt3070_rf_write(sc, 6, rf); + + /* set Tx0 power */ + run_rt3070_rf_read(sc, 12, &rf); + rf = (rf & ~0x1f) | txpow1; + run_rt3070_rf_write(sc, 12, rf); + + /* set Tx1 power */ + run_rt3070_rf_read(sc, 13, &rf); + rf = (rf & ~0x1f) | txpow2; + run_rt3070_rf_write(sc, 13, rf); + + run_rt3070_rf_read(sc, 1, &rf); + rf &= ~0xfc; + if (sc->ntxchains == 1) + rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */ + else if (sc->ntxchains == 2) + rf |= 1 << 7; /* 2T: disable Tx chain 3 */ + if (sc->nrxchains == 1) + rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ + else if (sc->nrxchains == 2) + rf |= 1 << 6; /* 2R: disable Rx chain 3 */ + run_rt3070_rf_write(sc, 1, rf); + + /* set RF offset */ + run_rt3070_rf_read(sc, 23, &rf); + rf = (rf & ~0x7f) | sc->freq; + run_rt3070_rf_write(sc, 23, rf); + + /* program RF filter */ + run_rt3070_rf_read(sc, 24, &rf); /* Tx */ + rf = (rf & ~0x3f) | sc->rf24_20mhz; + run_rt3070_rf_write(sc, 24, rf); + run_rt3070_rf_read(sc, 31, &rf); /* Rx */ + rf = (rf & ~0x3f) | sc->rf24_20mhz; + run_rt3070_rf_write(sc, 31, rf); + + /* enable RF tuning */ + run_rt3070_rf_read(sc, 7, &rf); + run_rt3070_rf_write(sc, 7, rf | 0x01); +} + +static void +run_rt3572_set_chan(struct run_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2; + uint32_t tmp; + uint8_t rf; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + if (chan <= 14) { + run_bbp_write(sc, 25, sc->bbp25); + run_bbp_write(sc, 26, sc->bbp26); + } else { + /* enable IQ phase correction */ + run_bbp_write(sc, 25, 0x09); + run_bbp_write(sc, 26, 0xff); + } + + run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n); + run_rt3070_rf_write(sc, 3, rt3070_freqs[i].k); + run_rt3070_rf_read(sc, 6, &rf); + rf = (rf & ~0x0f) | rt3070_freqs[i].r; + rf |= (chan <= 14) ? 0x08 : 0x04; + run_rt3070_rf_write(sc, 6, rf); + + /* set PLL mode */ + run_rt3070_rf_read(sc, 5, &rf); + rf &= ~(0x08 | 0x04); + rf |= (chan <= 14) ? 0x04 : 0x08; + run_rt3070_rf_write(sc, 5, rf); + + /* set Tx power for chain 0 */ + if (chan <= 14) + rf = 0x60 | txpow1; + else + rf = 0xe0 | (txpow1 & 0xc) << 1 | (txpow1 & 0x3); + run_rt3070_rf_write(sc, 12, rf); + + /* set Tx power for chain 1 */ + if (chan <= 14) + rf = 0x60 | txpow2; + else + rf = 0xe0 | (txpow2 & 0xc) << 1 | (txpow2 & 0x3); + run_rt3070_rf_write(sc, 13, rf); + + /* set Tx/Rx streams */ + run_rt3070_rf_read(sc, 1, &rf); + rf &= ~0xfc; + if (sc->ntxchains == 1) + rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */ + else if (sc->ntxchains == 2) + rf |= 1 << 7; /* 2T: disable Tx chain 3 */ + if (sc->nrxchains == 1) + rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ + else if (sc->nrxchains == 2) + rf |= 1 << 6; /* 2R: disable Rx chain 3 */ + run_rt3070_rf_write(sc, 1, rf); + + /* set RF offset */ + run_rt3070_rf_read(sc, 23, &rf); + rf = (rf & ~0x7f) | sc->freq; + run_rt3070_rf_write(sc, 23, rf); + + /* program RF filter */ + rf = sc->rf24_20mhz; + run_rt3070_rf_write(sc, 24, rf); /* Tx */ + run_rt3070_rf_write(sc, 31, rf); /* Rx */ + + /* enable RF tuning */ + run_rt3070_rf_read(sc, 7, &rf); + rf = (chan <= 14) ? 0xd8 : ((rf & ~0xc8) | 0x14); + run_rt3070_rf_write(sc, 7, rf); + + /* TSSI */ + rf = (chan <= 14) ? 0xc3 : 0xc0; + run_rt3070_rf_write(sc, 9, rf); + + /* set loop filter 1 */ + run_rt3070_rf_write(sc, 10, 0xf1); + /* set loop filter 2 */ + run_rt3070_rf_write(sc, 11, (chan <= 14) ? 0xb9 : 0x00); + + /* set tx_mx2_ic */ + run_rt3070_rf_write(sc, 15, (chan <= 14) ? 0x53 : 0x43); + /* set tx_mx1_ic */ + if (chan <= 14) + rf = 0x48 | sc->txmixgain_2ghz; + else + rf = 0x78 | sc->txmixgain_5ghz; + run_rt3070_rf_write(sc, 16, rf); + + /* set tx_lo1 */ + run_rt3070_rf_write(sc, 17, 0x23); + /* set tx_lo2 */ + if (chan <= 14) + rf = 0x93; + else if (chan <= 64) + rf = 0xb7; + else if (chan <= 128) + rf = 0x74; + else + rf = 0x72; + run_rt3070_rf_write(sc, 19, rf); + + /* set rx_lo1 */ + if (chan <= 14) + rf = 0xb3; + else if (chan <= 64) + rf = 0xf6; + else if (chan <= 128) + rf = 0xf4; + else + rf = 0xf3; + run_rt3070_rf_write(sc, 20, rf); + + /* set pfd_delay */ + if (chan <= 14) + rf = 0x15; + else if (chan <= 64) + rf = 0x3d; + else + rf = 0x01; + run_rt3070_rf_write(sc, 25, rf); + + /* set rx_lo2 */ + run_rt3070_rf_write(sc, 26, (chan <= 14) ? 0x85 : 0x87); + /* set ldo_rf_vc */ + run_rt3070_rf_write(sc, 27, (chan <= 14) ? 0x00 : 0x01); + /* set drv_cc */ + run_rt3070_rf_write(sc, 29, (chan <= 14) ? 0x9b : 0x9f); + + run_read(sc, RT2860_GPIO_CTRL, &tmp); + tmp &= ~0x8080; + if (chan <= 14) + tmp |= 0x80; + run_write(sc, RT2860_GPIO_CTRL, tmp); + + /* enable RF tuning */ + run_rt3070_rf_read(sc, 7, &rf); + run_rt3070_rf_write(sc, 7, rf | 0x01); + + run_delay(sc, 2); +} + +static void +run_rt3593_set_chan(struct run_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2, txpow3; + uint8_t h20mhz, rf; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + txpow3 = (sc->ntxchains == 3) ? sc->txpow3[i] : 0; + + if (chan <= 14) { + run_bbp_write(sc, 25, sc->bbp25); + run_bbp_write(sc, 26, sc->bbp26); + } else { + /* Enable IQ phase correction. */ + run_bbp_write(sc, 25, 0x09); + run_bbp_write(sc, 26, 0xff); + } + + run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n); + run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f); + run_rt3070_rf_read(sc, 11, &rf); + rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03); + run_rt3070_rf_write(sc, 11, rf); + + /* Set pll_idoh. */ + run_rt3070_rf_read(sc, 11, &rf); + rf &= ~0x4c; + rf |= (chan <= 14) ? 0x44 : 0x48; + run_rt3070_rf_write(sc, 11, rf); + + if (chan <= 14) + rf = txpow1 & 0x1f; + else + rf = 0x40 | ((txpow1 & 0x18) << 1) | (txpow1 & 0x07); + run_rt3070_rf_write(sc, 53, rf); + + if (chan <= 14) + rf = txpow2 & 0x1f; + else + rf = 0x40 | ((txpow2 & 0x18) << 1) | (txpow2 & 0x07); + run_rt3070_rf_write(sc, 55, rf); + + if (chan <= 14) + rf = txpow3 & 0x1f; + else + rf = 0x40 | ((txpow3 & 0x18) << 1) | (txpow3 & 0x07); + run_rt3070_rf_write(sc, 54, rf); + + rf = RT3070_RF_BLOCK | RT3070_PLL_PD; + if (sc->ntxchains == 3) + rf |= RT3070_TX0_PD | RT3070_TX1_PD | RT3070_TX2_PD; + else + rf |= RT3070_TX0_PD | RT3070_TX1_PD; + rf |= RT3070_RX0_PD | RT3070_RX1_PD | RT3070_RX2_PD; + run_rt3070_rf_write(sc, 1, rf); + + run_adjust_freq_offset(sc); + + run_rt3070_rf_write(sc, 31, (chan <= 14) ? 0xa0 : 0x80); + + h20mhz = (sc->rf24_20mhz & 0x20) >> 5; + run_rt3070_rf_read(sc, 30, &rf); + rf = (rf & ~0x06) | (h20mhz << 1) | (h20mhz << 2); + run_rt3070_rf_write(sc, 30, rf); + + run_rt3070_rf_read(sc, 36, &rf); + if (chan <= 14) + rf |= 0x80; + else + rf &= ~0x80; + run_rt3070_rf_write(sc, 36, rf); + + /* Set vcolo_bs. */ + run_rt3070_rf_write(sc, 34, (chan <= 14) ? 0x3c : 0x20); + /* Set pfd_delay. */ + run_rt3070_rf_write(sc, 12, (chan <= 14) ? 0x1a : 0x12); + + /* Set vco bias current control. */ + run_rt3070_rf_read(sc, 6, &rf); + rf &= ~0xc0; + if (chan <= 14) + rf |= 0x40; + else if (chan <= 128) + rf |= 0x80; + else + rf |= 0x40; + run_rt3070_rf_write(sc, 6, rf); + + run_rt3070_rf_read(sc, 30, &rf); + rf = (rf & ~0x18) | 0x10; + run_rt3070_rf_write(sc, 30, rf); + + run_rt3070_rf_write(sc, 10, (chan <= 14) ? 0xd3 : 0xd8); + run_rt3070_rf_write(sc, 13, (chan <= 14) ? 0x12 : 0x23); + + run_rt3070_rf_read(sc, 51, &rf); + rf = (rf & ~0x03) | 0x01; + run_rt3070_rf_write(sc, 51, rf); + /* Set tx_mx1_cc. */ + run_rt3070_rf_read(sc, 51, &rf); + rf &= ~0x1c; + rf |= (chan <= 14) ? 0x14 : 0x10; + run_rt3070_rf_write(sc, 51, rf); + /* Set tx_mx1_ic. */ + run_rt3070_rf_read(sc, 51, &rf); + rf &= ~0xe0; + rf |= (chan <= 14) ? 0x60 : 0x40; + run_rt3070_rf_write(sc, 51, rf); + /* Set tx_lo1_ic. */ + run_rt3070_rf_read(sc, 49, &rf); + rf &= ~0x1c; + rf |= (chan <= 14) ? 0x0c : 0x08; + run_rt3070_rf_write(sc, 49, rf); + /* Set tx_lo1_en. */ + run_rt3070_rf_read(sc, 50, &rf); + run_rt3070_rf_write(sc, 50, rf & ~0x20); + /* Set drv_cc. */ + run_rt3070_rf_read(sc, 57, &rf); + rf &= ~0xfc; + rf |= (chan <= 14) ? 0x6c : 0x3c; + run_rt3070_rf_write(sc, 57, rf); + /* Set rx_mix1_ic, rxa_lnactr, lna_vc, lna_inbias_en and lna_en. */ + run_rt3070_rf_write(sc, 44, (chan <= 14) ? 0x93 : 0x9b); + /* Set drv_gnd_a, tx_vga_cc_a and tx_mx2_gain. */ + run_rt3070_rf_write(sc, 52, (chan <= 14) ? 0x45 : 0x05); + /* Enable VCO calibration. */ + run_rt3070_rf_read(sc, 3, &rf); + rf &= ~RT5390_VCOCAL; + rf |= (chan <= 14) ? RT5390_VCOCAL : 0xbe; + run_rt3070_rf_write(sc, 3, rf); + + if (chan <= 14) + rf = 0x23; + else if (chan <= 64) + rf = 0x36; + else if (chan <= 128) + rf = 0x32; + else + rf = 0x30; + run_rt3070_rf_write(sc, 39, rf); + if (chan <= 14) + rf = 0xbb; + else if (chan <= 64) + rf = 0xeb; + else if (chan <= 128) + rf = 0xb3; + else + rf = 0x9b; + run_rt3070_rf_write(sc, 45, rf); + + /* Set FEQ/AEQ control. */ + run_bbp_write(sc, 105, 0x34); +} + +static void +run_rt5390_set_chan(struct run_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2; + uint8_t rf; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n); + run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f); + run_rt3070_rf_read(sc, 11, &rf); + rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03); + run_rt3070_rf_write(sc, 11, rf); + + run_rt3070_rf_read(sc, 49, &rf); + rf = (rf & ~0x3f) | (txpow1 & 0x3f); + /* The valid range of the RF R49 is 0x00 to 0x27. */ + if ((rf & 0x3f) > 0x27) + rf = (rf & ~0x3f) | 0x27; + run_rt3070_rf_write(sc, 49, rf); + + if (sc->mac_ver == 0x5392) { + run_rt3070_rf_read(sc, 50, &rf); + rf = (rf & ~0x3f) | (txpow2 & 0x3f); + /* The valid range of the RF R50 is 0x00 to 0x27. */ + if ((rf & 0x3f) > 0x27) + rf = (rf & ~0x3f) | 0x27; + run_rt3070_rf_write(sc, 50, rf); + } + + run_rt3070_rf_read(sc, 1, &rf); + rf |= RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD; + if (sc->mac_ver == 0x5392) + rf |= RT3070_RX1_PD | RT3070_TX1_PD; + run_rt3070_rf_write(sc, 1, rf); + + if (sc->mac_ver != 0x5392) { + run_rt3070_rf_read(sc, 2, &rf); + rf |= 0x80; + run_rt3070_rf_write(sc, 2, rf); + run_delay(sc, 10); + rf &= 0x7f; + run_rt3070_rf_write(sc, 2, rf); + } + + run_adjust_freq_offset(sc); + + if (sc->mac_ver == 0x5392) { + /* Fix for RT5392C. */ + if (sc->mac_rev >= 0x0223) { + if (chan <= 4) + rf = 0x0f; + else if (chan >= 5 && chan <= 7) + rf = 0x0e; + else + rf = 0x0d; + run_rt3070_rf_write(sc, 23, rf); + + if (chan <= 4) + rf = 0x0c; + else if (chan == 5) + rf = 0x0b; + else if (chan >= 6 && chan <= 7) + rf = 0x0a; + else if (chan >= 8 && chan <= 10) + rf = 0x09; + else + rf = 0x08; + run_rt3070_rf_write(sc, 59, rf); + } else { + if (chan <= 11) + rf = 0x0f; + else + rf = 0x0b; + run_rt3070_rf_write(sc, 59, rf); + } + } else { + /* Fix for RT5390F. */ + if (sc->mac_rev >= 0x0502) { + if (chan <= 11) + rf = 0x43; + else + rf = 0x23; + run_rt3070_rf_write(sc, 55, rf); + + if (chan <= 11) + rf = 0x0f; + else if (chan == 12) + rf = 0x0d; + else + rf = 0x0b; + run_rt3070_rf_write(sc, 59, rf); + } else { + run_rt3070_rf_write(sc, 55, 0x44); + run_rt3070_rf_write(sc, 59, 0x8f); + } + } + + /* Enable VCO calibration. */ + run_rt3070_rf_read(sc, 3, &rf); + rf |= RT5390_VCOCAL; + run_rt3070_rf_write(sc, 3, rf); +} + +static void +run_rt5592_set_chan(struct run_softc *sc, u_int chan) +{ + const struct rt5592_freqs *freqs; + uint32_t tmp; + uint8_t reg, rf, txpow_bound; + int8_t txpow1, txpow2; + int i; + + run_read(sc, RT5592_DEBUG_INDEX, &tmp); + freqs = (tmp & RT5592_SEL_XTAL) ? + rt5592_freqs_40mhz : rt5592_freqs_20mhz; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++, freqs++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp &= ~0x1c000000; + if (chan > 14) + tmp |= 0x14000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + + /* N setting. */ + run_rt3070_rf_write(sc, 8, freqs->n & 0xff); + run_rt3070_rf_read(sc, 9, &rf); + rf &= ~(1 << 4); + rf |= ((freqs->n & 0x0100) >> 8) << 4; + run_rt3070_rf_write(sc, 9, rf); + + /* K setting. */ + run_rt3070_rf_read(sc, 9, &rf); + rf &= ~0x0f; + rf |= (freqs->k & 0x0f); + run_rt3070_rf_write(sc, 9, rf); + + /* Mode setting. */ + run_rt3070_rf_read(sc, 11, &rf); + rf &= ~0x0c; + rf |= ((freqs->m - 0x8) & 0x3) << 2; + run_rt3070_rf_write(sc, 11, rf); + run_rt3070_rf_read(sc, 9, &rf); + rf &= ~(1 << 7); + rf |= (((freqs->m - 0x8) & 0x4) >> 2) << 7; + run_rt3070_rf_write(sc, 9, rf); + + /* R setting. */ + run_rt3070_rf_read(sc, 11, &rf); + rf &= ~0x03; + rf |= (freqs->r - 0x1); + run_rt3070_rf_write(sc, 11, rf); + + if (chan <= 14) { + /* Initialize RF registers for 2GHZ. */ + for (i = 0; i < nitems(rt5592_2ghz_def_rf); i++) { + run_rt3070_rf_write(sc, rt5592_2ghz_def_rf[i].reg, + rt5592_2ghz_def_rf[i].val); + } + + rf = (chan <= 10) ? 0x07 : 0x06; + run_rt3070_rf_write(sc, 23, rf); + run_rt3070_rf_write(sc, 59, rf); + + run_rt3070_rf_write(sc, 55, 0x43); + + /* + * RF R49/R50 Tx power ALC code. + * G-band bit<7:6>=1:0, bit<5:0> range from 0x0 ~ 0x27. + */ + reg = 2; + txpow_bound = 0x27; + } else { + /* Initialize RF registers for 5GHZ. */ + for (i = 0; i < nitems(rt5592_5ghz_def_rf); i++) { + run_rt3070_rf_write(sc, rt5592_5ghz_def_rf[i].reg, + rt5592_5ghz_def_rf[i].val); + } + for (i = 0; i < nitems(rt5592_chan_5ghz); i++) { + if (chan >= rt5592_chan_5ghz[i].firstchan && + chan <= rt5592_chan_5ghz[i].lastchan) { + run_rt3070_rf_write(sc, rt5592_chan_5ghz[i].reg, + rt5592_chan_5ghz[i].val); + } + } + + /* + * RF R49/R50 Tx power ALC code. + * A-band bit<7:6>=1:1, bit<5:0> range from 0x0 ~ 0x2b. + */ + reg = 3; + txpow_bound = 0x2b; + } + + /* RF R49 ch0 Tx power ALC code. */ + run_rt3070_rf_read(sc, 49, &rf); + rf &= ~0xc0; + rf |= (reg << 6); + rf = (rf & ~0x3f) | (txpow1 & 0x3f); + if ((rf & 0x3f) > txpow_bound) + rf = (rf & ~0x3f) | txpow_bound; + run_rt3070_rf_write(sc, 49, rf); + + /* RF R50 ch1 Tx power ALC code. */ + run_rt3070_rf_read(sc, 50, &rf); + rf &= ~(1 << 7 | 1 << 6); + rf |= (reg << 6); + rf = (rf & ~0x3f) | (txpow2 & 0x3f); + if ((rf & 0x3f) > txpow_bound) + rf = (rf & ~0x3f) | txpow_bound; + run_rt3070_rf_write(sc, 50, rf); + + /* Enable RF_BLOCK, PLL_PD, RX0_PD, and TX0_PD. */ + run_rt3070_rf_read(sc, 1, &rf); + rf |= (RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD); + if (sc->ntxchains > 1) + rf |= RT3070_TX1_PD; + if (sc->nrxchains > 1) + rf |= RT3070_RX1_PD; + run_rt3070_rf_write(sc, 1, rf); + + run_rt3070_rf_write(sc, 6, 0xe4); + + run_rt3070_rf_write(sc, 30, 0x10); + run_rt3070_rf_write(sc, 31, 0x80); + run_rt3070_rf_write(sc, 32, 0x80); + + run_adjust_freq_offset(sc); + + /* Enable VCO calibration. */ + run_rt3070_rf_read(sc, 3, &rf); + rf |= RT5390_VCOCAL; + run_rt3070_rf_write(sc, 3, rf); +} + +static void +run_set_rx_antenna(struct run_softc *sc, int aux) +{ + uint32_t tmp; + uint8_t bbp152; + + if (aux) { + if (sc->rf_rev == RT5390_RF_5370) { + run_bbp_read(sc, 152, &bbp152); + run_bbp_write(sc, 152, bbp152 & ~0x80); + } else { + run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 0); + run_read(sc, RT2860_GPIO_CTRL, &tmp); + run_write(sc, RT2860_GPIO_CTRL, (tmp & ~0x0808) | 0x08); + } + } else { + if (sc->rf_rev == RT5390_RF_5370) { + run_bbp_read(sc, 152, &bbp152); + run_bbp_write(sc, 152, bbp152 | 0x80); + } else { + run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 1); + run_read(sc, RT2860_GPIO_CTRL, &tmp); + run_write(sc, RT2860_GPIO_CTRL, tmp & ~0x0808); + } + } +} + +static int +run_set_chan(struct run_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + u_int chan, group; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return (EINVAL); + + if (sc->mac_ver == 0x5592) + run_rt5592_set_chan(sc, chan); + else if (sc->mac_ver >= 0x5390) + run_rt5390_set_chan(sc, chan); + else if (sc->mac_ver == 0x3593) + run_rt3593_set_chan(sc, chan); + else if (sc->mac_ver == 0x3572) + run_rt3572_set_chan(sc, chan); + else if (sc->mac_ver >= 0x3070) + run_rt3070_set_chan(sc, chan); + else + run_rt2870_set_chan(sc, chan); + + /* determine channel group */ + if (chan <= 14) + group = 0; + else if (chan <= 64) + group = 1; + else if (chan <= 128) + group = 2; + else + group = 3; + + /* XXX necessary only when group has changed! */ + run_select_chan_group(sc, group); + + run_delay(sc, 10); + + /* Perform IQ calibration. */ + if (sc->mac_ver >= 0x5392) + run_iq_calib(sc, chan); + + return (0); +} + +static void +run_set_channel(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + + RUN_LOCK(sc); + run_set_chan(sc, ic->ic_curchan); + RUN_UNLOCK(sc); + + return; +} + +static void +run_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct run_softc *sc = ic->ic_softc; + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + run_chan_2ghz, nitems(run_chan_2ghz), bands, 0); + + if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850 || + sc->rf_rev == RT3070_RF_3052 || sc->rf_rev == RT3593_RF_3053 || + sc->rf_rev == RT5592_RF_5592) { + setbit(bands, IEEE80211_MODE_11A); + ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, + run_chan_5ghz, nitems(run_chan_5ghz), bands, 0); + } +} + +static void +run_scan_start(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + uint32_t tmp; + + RUN_LOCK(sc); + + /* abort TSF synchronization */ + run_read(sc, RT2860_BCN_TIME_CFG, &tmp); + run_write(sc, RT2860_BCN_TIME_CFG, + tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | + RT2860_TBTT_TIMER_EN)); + run_set_bssid(sc, ieee80211broadcastaddr); + + RUN_UNLOCK(sc); + + return; +} + +static void +run_scan_end(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + + RUN_LOCK(sc); + + run_enable_tsf_sync(sc); + run_set_bssid(sc, sc->sc_bssid); + + RUN_UNLOCK(sc); + + return; +} + +/* + * Could be called from ieee80211_node_timeout() + * (non-sleepable thread) + */ +static void +run_update_beacon(struct ieee80211vap *vap, int item) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; + struct ieee80211_node *ni = vap->iv_bss; + struct run_softc *sc = ic->ic_softc; + struct run_vap *rvp = RUN_VAP(vap); + int mcast = 0; + uint32_t i; + + switch (item) { + case IEEE80211_BEACON_ERP: + run_updateslot(ic); + break; + case IEEE80211_BEACON_HTINFO: + run_updateprot(ic); + break; + case IEEE80211_BEACON_TIM: + mcast = 1; /*TODO*/ + break; + default: + break; + } + + setbit(bo->bo_flags, item); + if (rvp->beacon_mbuf == NULL) { + rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); + if (rvp->beacon_mbuf == NULL) + return; + } + ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast); + + i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_update_beacon_cb; + sc->cmdq[i].arg0 = vap; + ieee80211_runtask(ic, &sc->cmdq_task); + + return; +} + +static void +run_update_beacon_cb(void *arg) +{ + struct ieee80211vap *vap = arg; + struct ieee80211_node *ni = vap->iv_bss; + struct run_vap *rvp = RUN_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + struct rt2860_txwi txwi; + struct mbuf *m; + uint16_t txwisize; + uint8_t ridx; + + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return; + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) + return; + + /* + * No need to call ieee80211_beacon_update(), run_update_beacon() + * is taking care of appropriate calls. + */ + if (rvp->beacon_mbuf == NULL) { + rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); + if (rvp->beacon_mbuf == NULL) + return; + } + m = rvp->beacon_mbuf; + + memset(&txwi, 0, sizeof(txwi)); + txwi.wcid = 0xff; + txwi.len = htole16(m->m_pkthdr.len); + + /* send beacons at the lowest available rate */ + ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? + RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; + txwi.phy = htole16(rt2860_rates[ridx].mcs); + if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) + txwi.phy |= htole16(RT2860_PHY_OFDM); + txwi.txop = RT2860_TX_TXOP_HT; + txwi.flags = RT2860_TX_TS; + txwi.xflags = RT2860_TX_NSEQ; + + txwisize = (sc->mac_ver == 0x5592) ? + sizeof(txwi) + sizeof(uint32_t) : sizeof(txwi); + run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id), (uint8_t *)&txwi, + txwisize); + run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id) + txwisize, + mtod(m, uint8_t *), (m->m_pkthdr.len + 1) & ~1); +} + +static void +run_updateprot(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + uint32_t i; + + i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_updateprot_cb; + sc->cmdq[i].arg0 = ic; + ieee80211_runtask(ic, &sc->cmdq_task); +} + +static void +run_updateprot_cb(void *arg) +{ + struct ieee80211com *ic = arg; + struct run_softc *sc = ic->ic_softc; + uint32_t tmp; + + tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; + /* setup protection frame rate (MCS code) */ + tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? + rt2860_rates[RT2860_RIDX_OFDM6].mcs | RT2860_PHY_OFDM : + rt2860_rates[RT2860_RIDX_CCK11].mcs; + + /* CCK frames don't require protection */ + run_write(sc, RT2860_CCK_PROT_CFG, tmp); + if (ic->ic_flags & IEEE80211_F_USEPROT) { + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + tmp |= RT2860_PROT_CTRL_RTS_CTS; + else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + tmp |= RT2860_PROT_CTRL_CTS; + } + run_write(sc, RT2860_OFDM_PROT_CFG, tmp); +} + +static void +run_usb_timeout_cb(void *arg) +{ + struct ieee80211vap *vap = arg; + struct run_softc *sc = vap->iv_ic->ic_softc; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if(vap->iv_state == IEEE80211_S_RUN && + vap->iv_opmode != IEEE80211_M_STA) + run_reset_livelock(sc); + else if (vap->iv_state == IEEE80211_S_SCAN) { + RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE, + "timeout caused by scan\n"); + /* cancel bgscan */ + ieee80211_cancel_scan(vap); + } else + RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE, + "timeout by unknown cause\n"); +} + +static void +run_reset_livelock(struct run_softc *sc) +{ + uint32_t tmp; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + /* + * In IBSS or HostAP modes (when the hardware sends beacons), the MAC + * can run into a livelock and start sending CTS-to-self frames like + * crazy if protection is enabled. Reset MAC/BBP for a while + */ + run_read(sc, RT2860_DEBUG, &tmp); + RUN_DPRINTF(sc, RUN_DEBUG_RESET, "debug reg %08x\n", tmp); + if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) { + RUN_DPRINTF(sc, RUN_DEBUG_RESET, + "CTS-to-self livelock detected\n"); + run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST); + run_delay(sc, 1); + run_write(sc, RT2860_MAC_SYS_CTRL, + RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + } +} + +static void +run_update_promisc_locked(struct run_softc *sc) +{ + uint32_t tmp; + + run_read(sc, RT2860_RX_FILTR_CFG, &tmp); + + tmp |= RT2860_DROP_UC_NOME; + if (sc->sc_ic.ic_promisc > 0) + tmp &= ~RT2860_DROP_UC_NOME; + + run_write(sc, RT2860_RX_FILTR_CFG, tmp); + + RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s promiscuous mode\n", + (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving"); +} + +static void +run_update_promisc(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + + if ((sc->sc_flags & RUN_RUNNING) == 0) + return; + + RUN_LOCK(sc); + run_update_promisc_locked(sc); + RUN_UNLOCK(sc); +} + +static void +run_enable_tsf_sync(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "rvp_id=%d ic_opmode=%d\n", + RUN_VAP(vap)->rvp_id, ic->ic_opmode); + + run_read(sc, RT2860_BCN_TIME_CFG, &tmp); + tmp &= ~0x1fffff; + tmp |= vap->iv_bss->ni_intval * 16; + tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; + + if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * Local TSF is always updated with remote TSF on beacon + * reception. + */ + tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; + } else if (ic->ic_opmode == IEEE80211_M_IBSS) { + tmp |= RT2860_BCN_TX_EN; + /* + * Local TSF is updated with remote TSF on beacon reception + * only if the remote TSF is greater than local TSF. + */ + tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT; + } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_MBSS) { + tmp |= RT2860_BCN_TX_EN; + /* SYNC with nobody */ + tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT; + } else { + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, + "Enabling TSF failed. undefined opmode\n"); + return; + } + + run_write(sc, RT2860_BCN_TIME_CFG, tmp); +} + +static void +run_enable_tsf(struct run_softc *sc) +{ + uint32_t tmp; + + if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) { + tmp &= ~(RT2860_BCN_TX_EN | RT2860_TBTT_TIMER_EN); + tmp |= RT2860_TSF_TIMER_EN; + run_write(sc, RT2860_BCN_TIME_CFG, tmp); + } +} + +static void +run_get_tsf(struct run_softc *sc, uint64_t *buf) +{ + run_read_region_1(sc, RT2860_TSF_TIMER_DW0, (uint8_t *)buf, + sizeof(*buf)); +} + +static void +run_enable_mrr(struct run_softc *sc) +{ +#define CCK(mcs) (mcs) +#define OFDM(mcs) (1 << 3 | (mcs)) + run_write(sc, RT2860_LG_FBK_CFG0, + OFDM(6) << 28 | /* 54->48 */ + OFDM(5) << 24 | /* 48->36 */ + OFDM(4) << 20 | /* 36->24 */ + OFDM(3) << 16 | /* 24->18 */ + OFDM(2) << 12 | /* 18->12 */ + OFDM(1) << 8 | /* 12-> 9 */ + OFDM(0) << 4 | /* 9-> 6 */ + OFDM(0)); /* 6-> 6 */ + + run_write(sc, RT2860_LG_FBK_CFG1, + CCK(2) << 12 | /* 11->5.5 */ + CCK(1) << 8 | /* 5.5-> 2 */ + CCK(0) << 4 | /* 2-> 1 */ + CCK(0)); /* 1-> 1 */ +#undef OFDM +#undef CCK +} + +static void +run_set_txpreamble(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + + run_read(sc, RT2860_AUTO_RSP_CFG, &tmp); + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RT2860_CCK_SHORT_EN; + else + tmp &= ~RT2860_CCK_SHORT_EN; + run_write(sc, RT2860_AUTO_RSP_CFG, tmp); +} + +static void +run_set_basicrates(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + /* set basic rates mask */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x003); + else if (ic->ic_curmode == IEEE80211_MODE_11A) + run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x150); + else /* 11g */ + run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x15f); +} + +static void +run_set_leds(struct run_softc *sc, uint16_t which) +{ + (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LEDS, + which | (sc->leds & 0x7f)); +} + +static void +run_set_bssid(struct run_softc *sc, const uint8_t *bssid) +{ + run_write(sc, RT2860_MAC_BSSID_DW0, + bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); + run_write(sc, RT2860_MAC_BSSID_DW1, + bssid[4] | bssid[5] << 8); +} + +static void +run_set_macaddr(struct run_softc *sc, const uint8_t *addr) +{ + run_write(sc, RT2860_MAC_ADDR_DW0, + addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + run_write(sc, RT2860_MAC_ADDR_DW1, + addr[4] | addr[5] << 8 | 0xff << 16); +} + +static void +run_updateslot(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + uint32_t i; + + i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_updateslot_cb; + sc->cmdq[i].arg0 = ic; + ieee80211_runtask(ic, &sc->cmdq_task); + + return; +} + +/* ARGSUSED */ +static void +run_updateslot_cb(void *arg) +{ + struct ieee80211com *ic = arg; + struct run_softc *sc = ic->ic_softc; + uint32_t tmp; + + run_read(sc, RT2860_BKOFF_SLOT_CFG, &tmp); + tmp &= ~0xff; + tmp |= IEEE80211_GET_SLOTTIME(ic); + run_write(sc, RT2860_BKOFF_SLOT_CFG, tmp); +} + +static void +run_update_mcast(struct ieee80211com *ic) +{ +} + +static int8_t +run_rssi2dbm(struct run_softc *sc, uint8_t rssi, uint8_t rxchain) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *c = ic->ic_curchan; + int delta; + + if (IEEE80211_IS_CHAN_5GHZ(c)) { + u_int chan = ieee80211_chan2ieee(ic, c); + delta = sc->rssi_5ghz[rxchain]; + + /* determine channel group */ + if (chan <= 64) + delta -= sc->lna[1]; + else if (chan <= 128) + delta -= sc->lna[2]; + else + delta -= sc->lna[3]; + } else + delta = sc->rssi_2ghz[rxchain] - sc->lna[0]; + + return (-12 - delta - rssi); +} + +static void +run_rt5390_bbp_init(struct run_softc *sc) +{ + u_int i; + uint8_t bbp; + + /* Apply maximum likelihood detection for 2 stream case. */ + run_bbp_read(sc, 105, &bbp); + if (sc->nrxchains > 1) + run_bbp_write(sc, 105, bbp | RT5390_MLD); + + /* Avoid data lost and CRC error. */ + run_bbp_read(sc, 4, &bbp); + run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); + + if (sc->mac_ver == 0x5592) { + for (i = 0; i < nitems(rt5592_def_bbp); i++) { + run_bbp_write(sc, rt5592_def_bbp[i].reg, + rt5592_def_bbp[i].val); + } + for (i = 0; i < nitems(rt5592_bbp_r196); i++) { + run_bbp_write(sc, 195, i + 0x80); + run_bbp_write(sc, 196, rt5592_bbp_r196[i]); + } + } else { + for (i = 0; i < nitems(rt5390_def_bbp); i++) { + run_bbp_write(sc, rt5390_def_bbp[i].reg, + rt5390_def_bbp[i].val); + } + } + if (sc->mac_ver == 0x5392) { + run_bbp_write(sc, 88, 0x90); + run_bbp_write(sc, 95, 0x9a); + run_bbp_write(sc, 98, 0x12); + run_bbp_write(sc, 106, 0x12); + run_bbp_write(sc, 134, 0xd0); + run_bbp_write(sc, 135, 0xf6); + run_bbp_write(sc, 148, 0x84); + } + + run_bbp_read(sc, 152, &bbp); + run_bbp_write(sc, 152, bbp | 0x80); + + /* Fix BBP254 for RT5592C. */ + if (sc->mac_ver == 0x5592 && sc->mac_rev >= 0x0221) { + run_bbp_read(sc, 254, &bbp); + run_bbp_write(sc, 254, bbp | 0x80); + } + + /* Disable hardware antenna diversity. */ + if (sc->mac_ver == 0x5390) + run_bbp_write(sc, 154, 0); + + /* Initialize Rx CCK/OFDM frequency offset report. */ + run_bbp_write(sc, 142, 1); + run_bbp_write(sc, 143, 57); +} + +static int +run_bbp_init(struct run_softc *sc) +{ + int i, error, ntries; + uint8_t bbp0; + + /* wait for BBP to wake up */ + for (ntries = 0; ntries < 20; ntries++) { + if ((error = run_bbp_read(sc, 0, &bbp0)) != 0) + return error; + if (bbp0 != 0 && bbp0 != 0xff) + break; + } + if (ntries == 20) + return (ETIMEDOUT); + + /* initialize BBP registers to default values */ + if (sc->mac_ver >= 0x5390) + run_rt5390_bbp_init(sc); + else { + for (i = 0; i < nitems(rt2860_def_bbp); i++) { + run_bbp_write(sc, rt2860_def_bbp[i].reg, + rt2860_def_bbp[i].val); + } + } + + if (sc->mac_ver == 0x3593) { + run_bbp_write(sc, 79, 0x13); + run_bbp_write(sc, 80, 0x05); + run_bbp_write(sc, 81, 0x33); + run_bbp_write(sc, 86, 0x46); + run_bbp_write(sc, 137, 0x0f); + } + + /* fix BBP84 for RT2860E */ + if (sc->mac_ver == 0x2860 && sc->mac_rev != 0x0101) + run_bbp_write(sc, 84, 0x19); + + if (sc->mac_ver >= 0x3070 && (sc->mac_ver != 0x3593 && + sc->mac_ver != 0x5592)) { + run_bbp_write(sc, 79, 0x13); + run_bbp_write(sc, 80, 0x05); + run_bbp_write(sc, 81, 0x33); + } else if (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) { + run_bbp_write(sc, 69, 0x16); + run_bbp_write(sc, 73, 0x12); + } + return (0); +} + +static int +run_rt3070_rf_init(struct run_softc *sc) +{ + uint32_t tmp; + uint8_t bbp4, mingain, rf, target; + u_int i; + + run_rt3070_rf_read(sc, 30, &rf); + /* toggle RF R30 bit 7 */ + run_rt3070_rf_write(sc, 30, rf | 0x80); + run_delay(sc, 10); + run_rt3070_rf_write(sc, 30, rf & ~0x80); + + /* initialize RF registers to default value */ + if (sc->mac_ver == 0x3572) { + for (i = 0; i < nitems(rt3572_def_rf); i++) { + run_rt3070_rf_write(sc, rt3572_def_rf[i].reg, + rt3572_def_rf[i].val); + } + } else { + for (i = 0; i < nitems(rt3070_def_rf); i++) { + run_rt3070_rf_write(sc, rt3070_def_rf[i].reg, + rt3070_def_rf[i].val); + } + } + + if (sc->mac_ver == 0x3070 && sc->mac_rev < 0x0201) { + /* + * Change voltage from 1.2V to 1.35V for RT3070. + * The DAC issue (RT3070_LDO_CFG0) has been fixed + * in RT3070(F). + */ + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp = (tmp & ~0x0f000000) | 0x0d000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + + } else if (sc->mac_ver == 0x3071) { + run_rt3070_rf_read(sc, 6, &rf); + run_rt3070_rf_write(sc, 6, rf | 0x40); + run_rt3070_rf_write(sc, 31, 0x14); + + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp &= ~0x1f000000; + if (sc->mac_rev < 0x0211) + tmp |= 0x0d000000; /* 1.3V */ + else + tmp |= 0x01000000; /* 1.2V */ + run_write(sc, RT3070_LDO_CFG0, tmp); + + /* patch LNA_PE_G1 */ + run_read(sc, RT3070_GPIO_SWITCH, &tmp); + run_write(sc, RT3070_GPIO_SWITCH, tmp & ~0x20); + + } else if (sc->mac_ver == 0x3572) { + run_rt3070_rf_read(sc, 6, &rf); + run_rt3070_rf_write(sc, 6, rf | 0x40); + + /* increase voltage from 1.2V to 1.35V */ + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp = (tmp & ~0x1f000000) | 0x0d000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + + if (sc->mac_rev < 0x0211 || !sc->patch_dac) { + run_delay(sc, 1); /* wait for 1msec */ + /* decrease voltage back to 1.2V */ + tmp = (tmp & ~0x1f000000) | 0x01000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + } + } + + /* select 20MHz bandwidth */ + run_rt3070_rf_read(sc, 31, &rf); + run_rt3070_rf_write(sc, 31, rf & ~0x20); + + /* calibrate filter for 20MHz bandwidth */ + sc->rf24_20mhz = 0x1f; /* default value */ + target = (sc->mac_ver < 0x3071) ? 0x16 : 0x13; + run_rt3070_filter_calib(sc, 0x07, target, &sc->rf24_20mhz); + + /* select 40MHz bandwidth */ + run_bbp_read(sc, 4, &bbp4); + run_bbp_write(sc, 4, (bbp4 & ~0x18) | 0x10); + run_rt3070_rf_read(sc, 31, &rf); + run_rt3070_rf_write(sc, 31, rf | 0x20); + + /* calibrate filter for 40MHz bandwidth */ + sc->rf24_40mhz = 0x2f; /* default value */ + target = (sc->mac_ver < 0x3071) ? 0x19 : 0x15; + run_rt3070_filter_calib(sc, 0x27, target, &sc->rf24_40mhz); + + /* go back to 20MHz bandwidth */ + run_bbp_read(sc, 4, &bbp4); + run_bbp_write(sc, 4, bbp4 & ~0x18); + + if (sc->mac_ver == 0x3572) { + /* save default BBP registers 25 and 26 values */ + run_bbp_read(sc, 25, &sc->bbp25); + run_bbp_read(sc, 26, &sc->bbp26); + } else if (sc->mac_rev < 0x0201 || sc->mac_rev < 0x0211) + run_rt3070_rf_write(sc, 27, 0x03); + + run_read(sc, RT3070_OPT_14, &tmp); + run_write(sc, RT3070_OPT_14, tmp | 1); + + if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) { + run_rt3070_rf_read(sc, 17, &rf); + rf &= ~RT3070_TX_LO1; + if ((sc->mac_ver == 0x3070 || + (sc->mac_ver == 0x3071 && sc->mac_rev >= 0x0211)) && + !sc->ext_2ghz_lna) + rf |= 0x20; /* fix for long range Rx issue */ + mingain = (sc->mac_ver == 0x3070) ? 1 : 2; + if (sc->txmixgain_2ghz >= mingain) + rf = (rf & ~0x7) | sc->txmixgain_2ghz; + run_rt3070_rf_write(sc, 17, rf); + } + + if (sc->mac_ver == 0x3071) { + run_rt3070_rf_read(sc, 1, &rf); + rf &= ~(RT3070_RX0_PD | RT3070_TX0_PD); + rf |= RT3070_RF_BLOCK | RT3070_RX1_PD | RT3070_TX1_PD; + run_rt3070_rf_write(sc, 1, rf); + + run_rt3070_rf_read(sc, 15, &rf); + run_rt3070_rf_write(sc, 15, rf & ~RT3070_TX_LO2); + + run_rt3070_rf_read(sc, 20, &rf); + run_rt3070_rf_write(sc, 20, rf & ~RT3070_RX_LO1); + + run_rt3070_rf_read(sc, 21, &rf); + run_rt3070_rf_write(sc, 21, rf & ~RT3070_RX_LO2); + } + + if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) { + /* fix Tx to Rx IQ glitch by raising RF voltage */ + run_rt3070_rf_read(sc, 27, &rf); + rf &= ~0x77; + if (sc->mac_rev < 0x0211) + rf |= 0x03; + run_rt3070_rf_write(sc, 27, rf); + } + return (0); +} + +static void +run_rt3593_rf_init(struct run_softc *sc) +{ + uint32_t tmp; + uint8_t rf; + u_int i; + + /* Disable the GPIO bits 4 and 7 for LNA PE control. */ + run_read(sc, RT3070_GPIO_SWITCH, &tmp); + tmp &= ~(1 << 4 | 1 << 7); + run_write(sc, RT3070_GPIO_SWITCH, tmp); + + /* Initialize RF registers to default value. */ + for (i = 0; i < nitems(rt3593_def_rf); i++) { + run_rt3070_rf_write(sc, rt3593_def_rf[i].reg, + rt3593_def_rf[i].val); + } + + /* Toggle RF R2 to initiate calibration. */ + run_rt3070_rf_write(sc, 2, RT5390_RESCAL); + + /* Initialize RF frequency offset. */ + run_adjust_freq_offset(sc); + + run_rt3070_rf_read(sc, 18, &rf); + run_rt3070_rf_write(sc, 18, rf | RT3593_AUTOTUNE_BYPASS); + + /* + * Increase voltage from 1.2V to 1.35V, wait for 1 msec to + * decrease voltage back to 1.2V. + */ + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp = (tmp & ~0x1f000000) | 0x0d000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + run_delay(sc, 1); + tmp = (tmp & ~0x1f000000) | 0x01000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + + sc->rf24_20mhz = 0x1f; + sc->rf24_40mhz = 0x2f; + + /* Save default BBP registers 25 and 26 values. */ + run_bbp_read(sc, 25, &sc->bbp25); + run_bbp_read(sc, 26, &sc->bbp26); + + run_read(sc, RT3070_OPT_14, &tmp); + run_write(sc, RT3070_OPT_14, tmp | 1); +} + +static void +run_rt5390_rf_init(struct run_softc *sc) +{ + uint32_t tmp; + uint8_t rf; + u_int i; + + /* Toggle RF R2 to initiate calibration. */ + if (sc->mac_ver == 0x5390) { + run_rt3070_rf_read(sc, 2, &rf); + run_rt3070_rf_write(sc, 2, rf | RT5390_RESCAL); + run_delay(sc, 10); + run_rt3070_rf_write(sc, 2, rf & ~RT5390_RESCAL); + } else { + run_rt3070_rf_write(sc, 2, RT5390_RESCAL); + run_delay(sc, 10); + } + + /* Initialize RF registers to default value. */ + if (sc->mac_ver == 0x5592) { + for (i = 0; i < nitems(rt5592_def_rf); i++) { + run_rt3070_rf_write(sc, rt5592_def_rf[i].reg, + rt5592_def_rf[i].val); + } + /* Initialize RF frequency offset. */ + run_adjust_freq_offset(sc); + } else if (sc->mac_ver == 0x5392) { + for (i = 0; i < nitems(rt5392_def_rf); i++) { + run_rt3070_rf_write(sc, rt5392_def_rf[i].reg, + rt5392_def_rf[i].val); + } + if (sc->mac_rev >= 0x0223) { + run_rt3070_rf_write(sc, 23, 0x0f); + run_rt3070_rf_write(sc, 24, 0x3e); + run_rt3070_rf_write(sc, 51, 0x32); + run_rt3070_rf_write(sc, 53, 0x22); + run_rt3070_rf_write(sc, 56, 0xc1); + run_rt3070_rf_write(sc, 59, 0x0f); + } + } else { + for (i = 0; i < nitems(rt5390_def_rf); i++) { + run_rt3070_rf_write(sc, rt5390_def_rf[i].reg, + rt5390_def_rf[i].val); + } + if (sc->mac_rev >= 0x0502) { + run_rt3070_rf_write(sc, 6, 0xe0); + run_rt3070_rf_write(sc, 25, 0x80); + run_rt3070_rf_write(sc, 46, 0x73); + run_rt3070_rf_write(sc, 53, 0x00); + run_rt3070_rf_write(sc, 56, 0x42); + run_rt3070_rf_write(sc, 61, 0xd1); + } + } + + sc->rf24_20mhz = 0x1f; /* default value */ + sc->rf24_40mhz = (sc->mac_ver == 0x5592) ? 0 : 0x2f; + + if (sc->mac_rev < 0x0211) + run_rt3070_rf_write(sc, 27, 0x3); + + run_read(sc, RT3070_OPT_14, &tmp); + run_write(sc, RT3070_OPT_14, tmp | 1); +} + +static int +run_rt3070_filter_calib(struct run_softc *sc, uint8_t init, uint8_t target, + uint8_t *val) +{ + uint8_t rf22, rf24; + uint8_t bbp55_pb, bbp55_sb, delta; + int ntries; + + /* program filter */ + run_rt3070_rf_read(sc, 24, &rf24); + rf24 = (rf24 & 0xc0) | init; /* initial filter value */ + run_rt3070_rf_write(sc, 24, rf24); + + /* enable baseband loopback mode */ + run_rt3070_rf_read(sc, 22, &rf22); + run_rt3070_rf_write(sc, 22, rf22 | 0x01); + + /* set power and frequency of passband test tone */ + run_bbp_write(sc, 24, 0x00); + for (ntries = 0; ntries < 100; ntries++) { + /* transmit test tone */ + run_bbp_write(sc, 25, 0x90); + run_delay(sc, 10); + /* read received power */ + run_bbp_read(sc, 55, &bbp55_pb); + if (bbp55_pb != 0) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + /* set power and frequency of stopband test tone */ + run_bbp_write(sc, 24, 0x06); + for (ntries = 0; ntries < 100; ntries++) { + /* transmit test tone */ + run_bbp_write(sc, 25, 0x90); + run_delay(sc, 10); + /* read received power */ + run_bbp_read(sc, 55, &bbp55_sb); + + delta = bbp55_pb - bbp55_sb; + if (delta > target) + break; + + /* reprogram filter */ + rf24++; + run_rt3070_rf_write(sc, 24, rf24); + } + if (ntries < 100) { + if (rf24 != init) + rf24--; /* backtrack */ + *val = rf24; + run_rt3070_rf_write(sc, 24, rf24); + } + + /* restore initial state */ + run_bbp_write(sc, 24, 0x00); + + /* disable baseband loopback mode */ + run_rt3070_rf_read(sc, 22, &rf22); + run_rt3070_rf_write(sc, 22, rf22 & ~0x01); + + return (0); +} + +static void +run_rt3070_rf_setup(struct run_softc *sc) +{ + uint8_t bbp, rf; + int i; + + if (sc->mac_ver == 0x3572) { + /* enable DC filter */ + if (sc->mac_rev >= 0x0201) + run_bbp_write(sc, 103, 0xc0); + + run_bbp_read(sc, 138, &bbp); + if (sc->ntxchains == 1) + bbp |= 0x20; /* turn off DAC1 */ + if (sc->nrxchains == 1) + bbp &= ~0x02; /* turn off ADC1 */ + run_bbp_write(sc, 138, bbp); + + if (sc->mac_rev >= 0x0211) { + /* improve power consumption */ + run_bbp_read(sc, 31, &bbp); + run_bbp_write(sc, 31, bbp & ~0x03); + } + + run_rt3070_rf_read(sc, 16, &rf); + rf = (rf & ~0x07) | sc->txmixgain_2ghz; + run_rt3070_rf_write(sc, 16, rf); + + } else if (sc->mac_ver == 0x3071) { + if (sc->mac_rev >= 0x0211) { + /* enable DC filter */ + run_bbp_write(sc, 103, 0xc0); + + /* improve power consumption */ + run_bbp_read(sc, 31, &bbp); + run_bbp_write(sc, 31, bbp & ~0x03); + } + + run_bbp_read(sc, 138, &bbp); + if (sc->ntxchains == 1) + bbp |= 0x20; /* turn off DAC1 */ + if (sc->nrxchains == 1) + bbp &= ~0x02; /* turn off ADC1 */ + run_bbp_write(sc, 138, bbp); + + run_write(sc, RT2860_TX_SW_CFG1, 0); + if (sc->mac_rev < 0x0211) { + run_write(sc, RT2860_TX_SW_CFG2, + sc->patch_dac ? 0x2c : 0x0f); + } else + run_write(sc, RT2860_TX_SW_CFG2, 0); + + } else if (sc->mac_ver == 0x3070) { + if (sc->mac_rev >= 0x0201) { + /* enable DC filter */ + run_bbp_write(sc, 103, 0xc0); + + /* improve power consumption */ + run_bbp_read(sc, 31, &bbp); + run_bbp_write(sc, 31, bbp & ~0x03); + } + + if (sc->mac_rev < 0x0201) { + run_write(sc, RT2860_TX_SW_CFG1, 0); + run_write(sc, RT2860_TX_SW_CFG2, 0x2c); + } else + run_write(sc, RT2860_TX_SW_CFG2, 0); + } + + /* initialize RF registers from ROM for >=RT3071*/ + if (sc->mac_ver >= 0x3071) { + for (i = 0; i < 10; i++) { + if (sc->rf[i].reg == 0 || sc->rf[i].reg == 0xff) + continue; + run_rt3070_rf_write(sc, sc->rf[i].reg, sc->rf[i].val); + } + } +} + +static void +run_rt3593_rf_setup(struct run_softc *sc) +{ + uint8_t bbp, rf; + + if (sc->mac_rev >= 0x0211) { + /* Enable DC filter. */ + run_bbp_write(sc, 103, 0xc0); + } + run_write(sc, RT2860_TX_SW_CFG1, 0); + if (sc->mac_rev < 0x0211) { + run_write(sc, RT2860_TX_SW_CFG2, + sc->patch_dac ? 0x2c : 0x0f); + } else + run_write(sc, RT2860_TX_SW_CFG2, 0); + + run_rt3070_rf_read(sc, 50, &rf); + run_rt3070_rf_write(sc, 50, rf & ~RT3593_TX_LO2); + + run_rt3070_rf_read(sc, 51, &rf); + rf = (rf & ~(RT3593_TX_LO1 | 0x0c)) | + ((sc->txmixgain_2ghz & 0x07) << 2); + run_rt3070_rf_write(sc, 51, rf); + + run_rt3070_rf_read(sc, 38, &rf); + run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1); + + run_rt3070_rf_read(sc, 39, &rf); + run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2); + + run_rt3070_rf_read(sc, 1, &rf); + run_rt3070_rf_write(sc, 1, rf & ~(RT3070_RF_BLOCK | RT3070_PLL_PD)); + + run_rt3070_rf_read(sc, 30, &rf); + rf = (rf & ~0x18) | 0x10; + run_rt3070_rf_write(sc, 30, rf); + + /* Apply maximum likelihood detection for 2 stream case. */ + run_bbp_read(sc, 105, &bbp); + if (sc->nrxchains > 1) + run_bbp_write(sc, 105, bbp | RT5390_MLD); + + /* Avoid data lost and CRC error. */ + run_bbp_read(sc, 4, &bbp); + run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); + + run_bbp_write(sc, 92, 0x02); + run_bbp_write(sc, 82, 0x82); + run_bbp_write(sc, 106, 0x05); + run_bbp_write(sc, 104, 0x92); + run_bbp_write(sc, 88, 0x90); + run_bbp_write(sc, 148, 0xc8); + run_bbp_write(sc, 47, 0x48); + run_bbp_write(sc, 120, 0x50); + + run_bbp_write(sc, 163, 0x9d); + + /* SNR mapping. */ + run_bbp_write(sc, 142, 0x06); + run_bbp_write(sc, 143, 0xa0); + run_bbp_write(sc, 142, 0x07); + run_bbp_write(sc, 143, 0xa1); + run_bbp_write(sc, 142, 0x08); + run_bbp_write(sc, 143, 0xa2); + + run_bbp_write(sc, 31, 0x08); + run_bbp_write(sc, 68, 0x0b); + run_bbp_write(sc, 105, 0x04); +} + +static void +run_rt5390_rf_setup(struct run_softc *sc) +{ + uint8_t bbp, rf; + + if (sc->mac_rev >= 0x0211) { + /* Enable DC filter. */ + run_bbp_write(sc, 103, 0xc0); + + if (sc->mac_ver != 0x5592) { + /* Improve power consumption. */ + run_bbp_read(sc, 31, &bbp); + run_bbp_write(sc, 31, bbp & ~0x03); + } + } + + run_bbp_read(sc, 138, &bbp); + if (sc->ntxchains == 1) + bbp |= 0x20; /* turn off DAC1 */ + if (sc->nrxchains == 1) + bbp &= ~0x02; /* turn off ADC1 */ + run_bbp_write(sc, 138, bbp); + + run_rt3070_rf_read(sc, 38, &rf); + run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1); + + run_rt3070_rf_read(sc, 39, &rf); + run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2); + + /* Avoid data lost and CRC error. */ + run_bbp_read(sc, 4, &bbp); + run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); + + run_rt3070_rf_read(sc, 30, &rf); + rf = (rf & ~0x18) | 0x10; + run_rt3070_rf_write(sc, 30, rf); + + if (sc->mac_ver != 0x5592) { + run_write(sc, RT2860_TX_SW_CFG1, 0); + if (sc->mac_rev < 0x0211) { + run_write(sc, RT2860_TX_SW_CFG2, + sc->patch_dac ? 0x2c : 0x0f); + } else + run_write(sc, RT2860_TX_SW_CFG2, 0); + } +} + +static int +run_txrx_enable(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + int error, ntries; + + run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); + for (ntries = 0; ntries < 200; ntries++) { + if ((error = run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp)) != 0) + return (error); + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + run_delay(sc, 50); + } + if (ntries == 200) + return (ETIMEDOUT); + + run_delay(sc, 50); + + tmp |= RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | RT2860_TX_WB_DDONE; + run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* enable Rx bulk aggregation (set timeout and limit) */ + tmp = RT2860_USB_TX_EN | RT2860_USB_RX_EN | RT2860_USB_RX_AGG_EN | + RT2860_USB_RX_AGG_TO(128) | RT2860_USB_RX_AGG_LMT(2); + run_write(sc, RT2860_USB_DMA_CFG, tmp); + + /* set Rx filter */ + tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL | + RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK | + RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV | + RT2860_DROP_CFACK | RT2860_DROP_CFEND; + if (ic->ic_opmode == IEEE80211_M_STA) + tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL; + } + run_write(sc, RT2860_RX_FILTR_CFG, tmp); + + run_write(sc, RT2860_MAC_SYS_CTRL, + RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + + return (0); +} + +static void +run_adjust_freq_offset(struct run_softc *sc) +{ + uint8_t rf, tmp; + + run_rt3070_rf_read(sc, 17, &rf); + tmp = rf; + rf = (rf & ~0x7f) | (sc->freq & 0x7f); + rf = MIN(rf, 0x5f); + + if (tmp != rf) + run_mcu_cmd(sc, 0x74, (tmp << 8 ) | rf); +} + +static void +run_init_locked(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + uint8_t bbp1, bbp3; + int i; + int ridx; + int ntries; + + if (ic->ic_nrunning > 1) + return; + + run_stop(sc); + + if (run_load_microcode(sc) != 0) { + device_printf(sc->sc_dev, "could not load 8051 microcode\n"); + goto fail; + } + + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_ASIC_VER_ID, &tmp) != 0) + goto fail; + if (tmp != 0 && tmp != 0xffffffff) + break; + run_delay(sc, 10); + } + if (ntries == 100) + goto fail; + + for (i = 0; i != RUN_EP_QUEUES; i++) + run_setup_tx_list(sc, &sc->sc_epq[i]); + + run_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) + goto fail; + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + run_delay(sc, 10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + goto fail; + } + tmp &= 0xff0; + tmp |= RT2860_TX_WB_DDONE; + run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* turn off PME_OEN to solve high-current issue */ + run_read(sc, RT2860_SYS_CTRL, &tmp); + run_write(sc, RT2860_SYS_CTRL, tmp & ~RT2860_PME_OEN); + + run_write(sc, RT2860_MAC_SYS_CTRL, + RT2860_BBP_HRST | RT2860_MAC_SRST); + run_write(sc, RT2860_USB_DMA_CFG, 0); + + if (run_reset(sc) != 0) { + device_printf(sc->sc_dev, "could not reset chipset\n"); + goto fail; + } + + run_write(sc, RT2860_MAC_SYS_CTRL, 0); + + /* init Tx power for all Tx rates (from EEPROM) */ + for (ridx = 0; ridx < 5; ridx++) { + if (sc->txpow20mhz[ridx] == 0xffffffff) + continue; + run_write(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); + } + + for (i = 0; i < nitems(rt2870_def_mac); i++) + run_write(sc, rt2870_def_mac[i].reg, rt2870_def_mac[i].val); + run_write(sc, RT2860_WMM_AIFSN_CFG, 0x00002273); + run_write(sc, RT2860_WMM_CWMIN_CFG, 0x00002344); + run_write(sc, RT2860_WMM_CWMAX_CFG, 0x000034aa); + + if (sc->mac_ver >= 0x5390) { + run_write(sc, RT2860_TX_SW_CFG0, + 4 << RT2860_DLY_PAPE_EN_SHIFT | 4); + if (sc->mac_ver >= 0x5392) { + run_write(sc, RT2860_MAX_LEN_CFG, 0x00002fff); + if (sc->mac_ver == 0x5592) { + run_write(sc, RT2860_HT_FBK_CFG1, 0xedcba980); + run_write(sc, RT2860_TXOP_HLDR_ET, 0x00000082); + } else { + run_write(sc, RT2860_HT_FBK_CFG1, 0xedcb4980); + run_write(sc, RT2860_LG_FBK_CFG0, 0xedcba322); + } + } + } else if (sc->mac_ver == 0x3593) { + run_write(sc, RT2860_TX_SW_CFG0, + 4 << RT2860_DLY_PAPE_EN_SHIFT | 2); + } else if (sc->mac_ver >= 0x3070) { + /* set delay of PA_PE assertion to 1us (unit of 0.25us) */ + run_write(sc, RT2860_TX_SW_CFG0, + 4 << RT2860_DLY_PAPE_EN_SHIFT); + } + + /* wait while MAC is busy */ + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_MAC_STATUS_REG, &tmp) != 0) + goto fail; + if (!(tmp & (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY))) + break; + run_delay(sc, 10); + } + if (ntries == 100) + goto fail; + + /* clear Host to MCU mailbox */ + run_write(sc, RT2860_H2M_BBPAGENT, 0); + run_write(sc, RT2860_H2M_MAILBOX, 0); + run_delay(sc, 10); + + if (run_bbp_init(sc) != 0) { + device_printf(sc->sc_dev, "could not initialize BBP\n"); + goto fail; + } + + /* abort TSF synchronization */ + run_read(sc, RT2860_BCN_TIME_CFG, &tmp); + tmp &= ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | + RT2860_TBTT_TIMER_EN); + run_write(sc, RT2860_BCN_TIME_CFG, tmp); + + /* clear RX WCID search table */ + run_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512); + /* clear WCID attribute table */ + run_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 8 * 32); + + /* hostapd sets a key before init. So, don't clear it. */ + if (sc->cmdq_key_set != RUN_CMDQ_GO) { + /* clear shared key table */ + run_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32); + /* clear shared key mode */ + run_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4); + } + + run_read(sc, RT2860_US_CYC_CNT, &tmp); + tmp = (tmp & ~0xff) | 0x1e; + run_write(sc, RT2860_US_CYC_CNT, tmp); + + if (sc->mac_rev != 0x0101) + run_write(sc, RT2860_TXOP_CTRL_CFG, 0x0000583f); + + run_write(sc, RT2860_WMM_TXOP0_CFG, 0); + run_write(sc, RT2860_WMM_TXOP1_CFG, 48 << 16 | 96); + + /* write vendor-specific BBP values (from EEPROM) */ + if (sc->mac_ver < 0x3593) { + for (i = 0; i < 10; i++) { + if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff) + continue; + run_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val); + } + } + + /* select Main antenna for 1T1R devices */ + if (sc->rf_rev == RT3070_RF_3020 || sc->rf_rev == RT5390_RF_5370) + run_set_rx_antenna(sc, 0); + + /* send LEDs operating mode to microcontroller */ + (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]); + (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]); + (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]); + + if (sc->mac_ver >= 0x5390) + run_rt5390_rf_init(sc); + else if (sc->mac_ver == 0x3593) + run_rt3593_rf_init(sc); + else if (sc->mac_ver >= 0x3070) + run_rt3070_rf_init(sc); + + /* disable non-existing Rx chains */ + run_bbp_read(sc, 3, &bbp3); + bbp3 &= ~(1 << 3 | 1 << 4); + if (sc->nrxchains == 2) + bbp3 |= 1 << 3; + else if (sc->nrxchains == 3) + bbp3 |= 1 << 4; + run_bbp_write(sc, 3, bbp3); + + /* disable non-existing Tx chains */ + run_bbp_read(sc, 1, &bbp1); + if (sc->ntxchains == 1) + bbp1 &= ~(1 << 3 | 1 << 4); + run_bbp_write(sc, 1, bbp1); + + if (sc->mac_ver >= 0x5390) + run_rt5390_rf_setup(sc); + else if (sc->mac_ver == 0x3593) + run_rt3593_rf_setup(sc); + else if (sc->mac_ver >= 0x3070) + run_rt3070_rf_setup(sc); + + /* select default channel */ + run_set_chan(sc, ic->ic_curchan); + + /* setup initial protection mode */ + run_updateprot_cb(ic); + + /* turn radio LED on */ + run_set_leds(sc, RT2860_LED_RADIO); + + sc->sc_flags |= RUN_RUNNING; + sc->cmdq_run = RUN_CMDQ_GO; + + for (i = 0; i != RUN_N_XFER; i++) + usbd_xfer_set_stall(sc->sc_xfer[i]); + + usbd_transfer_start(sc->sc_xfer[RUN_BULK_RX]); + + if (run_txrx_enable(sc) != 0) + goto fail; + + return; + +fail: + run_stop(sc); +} + +static void +run_stop(void *arg) +{ + struct run_softc *sc = (struct run_softc *)arg; + uint32_t tmp; + int i; + int ntries; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if (sc->sc_flags & RUN_RUNNING) + run_set_leds(sc, 0); /* turn all LEDs off */ + + sc->sc_flags &= ~RUN_RUNNING; + + sc->ratectl_run = RUN_RATECTL_OFF; + sc->cmdq_run = sc->cmdq_key_set; + + RUN_UNLOCK(sc); + + for(i = 0; i < RUN_N_XFER; i++) + usbd_transfer_drain(sc->sc_xfer[i]); + + RUN_LOCK(sc); + + run_drain_mbufq(sc); + + if (sc->rx_m != NULL) { + m_free(sc->rx_m); + sc->rx_m = NULL; + } + + /* Disable Tx/Rx DMA. */ + if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) + return; + tmp &= ~(RT2860_RX_DMA_EN | RT2860_TX_DMA_EN); + run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); + + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) + return; + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + run_delay(sc, 10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + return; + } + + /* disable Tx/Rx */ + run_read(sc, RT2860_MAC_SYS_CTRL, &tmp); + tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + run_write(sc, RT2860_MAC_SYS_CTRL, tmp); + + /* wait for pending Tx to complete */ + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_TXRXQ_PCNT, &tmp) != 0) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, + "Cannot read Tx queue count\n"); + break; + } + if ((tmp & RT2860_TX2Q_PCNT_MASK) == 0) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, + "All Tx cleared\n"); + break; + } + run_delay(sc, 10); + } + if (ntries >= 100) + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, + "There are still pending Tx\n"); + run_delay(sc, 10); + run_write(sc, RT2860_USB_DMA_CFG, 0); + + run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); + run_write(sc, RT2860_MAC_SYS_CTRL, 0); + + for (i = 0; i != RUN_EP_QUEUES; i++) + run_unsetup_tx_list(sc, &sc->sc_epq[i]); +} + +static void +run_delay(struct run_softc *sc, u_int ms) +{ + usb_pause_mtx(mtx_owned(&sc->sc_mtx) ? + &sc->sc_mtx : NULL, USB_MS_TO_TICKS(ms)); +} + +static device_method_t run_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, run_match), + DEVMETHOD(device_attach, run_attach), + DEVMETHOD(device_detach, run_detach), + DEVMETHOD_END +}; + +static driver_t run_driver = { + .name = "run", + .methods = run_methods, + .size = sizeof(struct run_softc) +}; + +static devclass_t run_devclass; + +DRIVER_MODULE(run, uhub, run_driver, run_devclass, run_driver_loaded, NULL); +MODULE_DEPEND(run, wlan, 1, 1, 1); +MODULE_DEPEND(run, usb, 1, 1, 1); +MODULE_DEPEND(run, firmware, 1, 1, 1); +MODULE_VERSION(run, 1); +USB_PNP_HOST_INFO(run_devs); diff --git a/freebsd/sys/dev/usb/wlan/if_runreg.h b/freebsd/sys/dev/usb/wlan/if_runreg.h new file mode 100644 index 00000000..c09aac8f --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_runreg.h @@ -0,0 +1,1669 @@ +/* $OpenBSD: rt2860reg.h,v 1.19 2009/05/18 19:25:07 damien Exp $ */ + +/*- + * Copyright (c) 2007 + * Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ + +#ifndef _IF_RUNREG_H_ +#define _IF_RUNREG_H_ + +#define RT2860_CONFIG_NO 1 +#define RT2860_IFACE_INDEX 0 + +#define RT3070_OPT_14 0x0114 + +/* SCH/DMA registers */ +#define RT2860_INT_STATUS 0x0200 +#define RT2860_INT_MASK 0x0204 +#define RT2860_WPDMA_GLO_CFG 0x0208 +#define RT2860_WPDMA_RST_IDX 0x020c +#define RT2860_DELAY_INT_CFG 0x0210 +#define RT2860_WMM_AIFSN_CFG 0x0214 +#define RT2860_WMM_CWMIN_CFG 0x0218 +#define RT2860_WMM_CWMAX_CFG 0x021c +#define RT2860_WMM_TXOP0_CFG 0x0220 +#define RT2860_WMM_TXOP1_CFG 0x0224 +#define RT2860_GPIO_CTRL 0x0228 +#define RT2860_MCU_CMD_REG 0x022c +#define RT2860_TX_BASE_PTR(qid) (0x0230 + (qid) * 16) +#define RT2860_TX_MAX_CNT(qid) (0x0234 + (qid) * 16) +#define RT2860_TX_CTX_IDX(qid) (0x0238 + (qid) * 16) +#define RT2860_TX_DTX_IDX(qid) (0x023c + (qid) * 16) +#define RT2860_RX_BASE_PTR 0x0290 +#define RT2860_RX_MAX_CNT 0x0294 +#define RT2860_RX_CALC_IDX 0x0298 +#define RT2860_FS_DRX_IDX 0x029c +#define RT2860_USB_DMA_CFG 0x02a0 /* RT2870 only */ +#define RT2860_US_CYC_CNT 0x02a4 + +/* PBF registers */ +#define RT2860_SYS_CTRL 0x0400 +#define RT2860_HOST_CMD 0x0404 +#define RT2860_PBF_CFG 0x0408 +#define RT2860_MAX_PCNT 0x040c +#define RT2860_BUF_CTRL 0x0410 +#define RT2860_MCU_INT_STA 0x0414 +#define RT2860_MCU_INT_ENA 0x0418 +#define RT2860_TXQ_IO(qid) (0x041c + (qid) * 4) +#define RT2860_RX0Q_IO 0x0424 +#define RT2860_BCN_OFFSET0 0x042c +#define RT2860_BCN_OFFSET1 0x0430 +#define RT2860_TXRXQ_STA 0x0434 +#define RT2860_TXRXQ_PCNT 0x0438 +#define RT2860_PBF_DBG 0x043c +#define RT2860_CAP_CTRL 0x0440 + +/* RT3070 registers */ +#define RT3070_RF_CSR_CFG 0x0500 +#define RT3070_EFUSE_CTRL 0x0580 +#define RT3070_EFUSE_DATA0 0x0590 +#define RT3070_EFUSE_DATA1 0x0594 +#define RT3070_EFUSE_DATA2 0x0598 +#define RT3070_EFUSE_DATA3 0x059c +#define RT3070_LDO_CFG0 0x05d4 +#define RT3070_GPIO_SWITCH 0x05dc + +/* RT5592 registers */ +#define RT5592_DEBUG_INDEX 0x05e8 + +/* MAC registers */ +#define RT2860_ASIC_VER_ID 0x1000 +#define RT2860_MAC_SYS_CTRL 0x1004 +#define RT2860_MAC_ADDR_DW0 0x1008 +#define RT2860_MAC_ADDR_DW1 0x100c +#define RT2860_MAC_BSSID_DW0 0x1010 +#define RT2860_MAC_BSSID_DW1 0x1014 +#define RT2860_MAX_LEN_CFG 0x1018 +#define RT2860_BBP_CSR_CFG 0x101c +#define RT2860_RF_CSR_CFG0 0x1020 +#define RT2860_RF_CSR_CFG1 0x1024 +#define RT2860_RF_CSR_CFG2 0x1028 +#define RT2860_LED_CFG 0x102c + +/* undocumented registers */ +#define RT2860_DEBUG 0x10f4 + +/* MAC Timing control registers */ +#define RT2860_XIFS_TIME_CFG 0x1100 +#define RT2860_BKOFF_SLOT_CFG 0x1104 +#define RT2860_NAV_TIME_CFG 0x1108 +#define RT2860_CH_TIME_CFG 0x110c +#define RT2860_PBF_LIFE_TIMER 0x1110 +#define RT2860_BCN_TIME_CFG 0x1114 +#define RT2860_TBTT_SYNC_CFG 0x1118 +#define RT2860_TSF_TIMER_DW0 0x111c +#define RT2860_TSF_TIMER_DW1 0x1120 +#define RT2860_TBTT_TIMER 0x1124 +#define RT2860_INT_TIMER_CFG 0x1128 +#define RT2860_INT_TIMER_EN 0x112c +#define RT2860_CH_IDLE_TIME 0x1130 + +/* MAC Power Save configuration registers */ +#define RT2860_MAC_STATUS_REG 0x1200 +#define RT2860_PWR_PIN_CFG 0x1204 +#define RT2860_AUTO_WAKEUP_CFG 0x1208 + +/* MAC TX configuration registers */ +#define RT2860_EDCA_AC_CFG(aci) (0x1300 + (aci) * 4) +#define RT2860_EDCA_TID_AC_MAP 0x1310 +#define RT2860_TX_PWR_CFG(ridx) (0x1314 + (ridx) * 4) +#define RT2860_TX_PIN_CFG 0x1328 +#define RT2860_TX_BAND_CFG 0x132c +#define RT2860_TX_SW_CFG0 0x1330 +#define RT2860_TX_SW_CFG1 0x1334 +#define RT2860_TX_SW_CFG2 0x1338 +#define RT2860_TXOP_THRES_CFG 0x133c +#define RT2860_TXOP_CTRL_CFG 0x1340 +#define RT2860_TX_RTS_CFG 0x1344 +#define RT2860_TX_TIMEOUT_CFG 0x1348 +#define RT2860_TX_RTY_CFG 0x134c +#define RT2860_TX_LINK_CFG 0x1350 +#define RT2860_HT_FBK_CFG0 0x1354 +#define RT2860_HT_FBK_CFG1 0x1358 +#define RT2860_LG_FBK_CFG0 0x135c +#define RT2860_LG_FBK_CFG1 0x1360 +#define RT2860_CCK_PROT_CFG 0x1364 +#define RT2860_OFDM_PROT_CFG 0x1368 +#define RT2860_MM20_PROT_CFG 0x136c +#define RT2860_MM40_PROT_CFG 0x1370 +#define RT2860_GF20_PROT_CFG 0x1374 +#define RT2860_GF40_PROT_CFG 0x1378 +#define RT2860_EXP_CTS_TIME 0x137c +#define RT2860_EXP_ACK_TIME 0x1380 + +/* MAC RX configuration registers */ +#define RT2860_RX_FILTR_CFG 0x1400 +#define RT2860_AUTO_RSP_CFG 0x1404 +#define RT2860_LEGACY_BASIC_RATE 0x1408 +#define RT2860_HT_BASIC_RATE 0x140c +#define RT2860_HT_CTRL_CFG 0x1410 +#define RT2860_SIFS_COST_CFG 0x1414 +#define RT2860_RX_PARSER_CFG 0x1418 + +/* MAC Security configuration registers */ +#define RT2860_TX_SEC_CNT0 0x1500 +#define RT2860_RX_SEC_CNT0 0x1504 +#define RT2860_CCMP_FC_MUTE 0x1508 + +/* MAC HCCA/PSMP configuration registers */ +#define RT2860_TXOP_HLDR_ADDR0 0x1600 +#define RT2860_TXOP_HLDR_ADDR1 0x1604 +#define RT2860_TXOP_HLDR_ET 0x1608 +#define RT2860_QOS_CFPOLL_RA_DW0 0x160c +#define RT2860_QOS_CFPOLL_A1_DW1 0x1610 +#define RT2860_QOS_CFPOLL_QC 0x1614 + +/* MAC Statistics Counters */ +#define RT2860_RX_STA_CNT0 0x1700 +#define RT2860_RX_STA_CNT1 0x1704 +#define RT2860_RX_STA_CNT2 0x1708 +#define RT2860_TX_STA_CNT0 0x170c +#define RT2860_TX_STA_CNT1 0x1710 +#define RT2860_TX_STA_CNT2 0x1714 +#define RT2860_TX_STAT_FIFO 0x1718 + +/* RX WCID search table */ +#define RT2860_WCID_ENTRY(wcid) (0x1800 + (wcid) * 8) + +#define RT2860_FW_BASE 0x2000 +#define RT2870_FW_BASE 0x3000 + +/* Pair-wise key table */ +#define RT2860_PKEY(wcid) (0x4000 + (wcid) * 32) + +/* IV/EIV table */ +#define RT2860_IVEIV(wcid) (0x6000 + (wcid) * 8) + +/* WCID attribute table */ +#define RT2860_WCID_ATTR(wcid) (0x6800 + (wcid) * 4) + +/* Shared Key Table */ +#define RT2860_SKEY(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32) + +/* Shared Key Mode */ +#define RT2860_SKEY_MODE_0_7 0x7000 +#define RT2860_SKEY_MODE_8_15 0x7004 +#define RT2860_SKEY_MODE_16_23 0x7008 +#define RT2860_SKEY_MODE_24_31 0x700c + +/* Shared Memory between MCU and host */ +#define RT2860_H2M_MAILBOX 0x7010 +#define RT2860_H2M_MAILBOX_CID 0x7014 +#define RT2860_H2M_MAILBOX_STATUS 0x701c +#define RT2860_H2M_INTSRC 0x7024 +#define RT2860_H2M_BBPAGENT 0x7028 +#define RT2860_BCN_BASE(vap) (0x7800 + (vap) * 512) + + +/* possible flags for register RT2860_PCI_EECTRL */ +#define RT2860_C (1 << 0) +#define RT2860_S (1 << 1) +#define RT2860_D (1 << 2) +#define RT2860_SHIFT_D 2 +#define RT2860_Q (1 << 3) +#define RT2860_SHIFT_Q 3 + +/* possible flags for registers INT_STATUS/INT_MASK */ +#define RT2860_TX_COHERENT (1 << 17) +#define RT2860_RX_COHERENT (1 << 16) +#define RT2860_MAC_INT_4 (1 << 15) +#define RT2860_MAC_INT_3 (1 << 14) +#define RT2860_MAC_INT_2 (1 << 13) +#define RT2860_MAC_INT_1 (1 << 12) +#define RT2860_MAC_INT_0 (1 << 11) +#define RT2860_TX_RX_COHERENT (1 << 10) +#define RT2860_MCU_CMD_INT (1 << 9) +#define RT2860_TX_DONE_INT5 (1 << 8) +#define RT2860_TX_DONE_INT4 (1 << 7) +#define RT2860_TX_DONE_INT3 (1 << 6) +#define RT2860_TX_DONE_INT2 (1 << 5) +#define RT2860_TX_DONE_INT1 (1 << 4) +#define RT2860_TX_DONE_INT0 (1 << 3) +#define RT2860_RX_DONE_INT (1 << 2) +#define RT2860_TX_DLY_INT (1 << 1) +#define RT2860_RX_DLY_INT (1 << 0) + +/* possible flags for register WPDMA_GLO_CFG */ +#define RT2860_HDR_SEG_LEN_SHIFT 8 +#define RT2860_BIG_ENDIAN (1 << 7) +#define RT2860_TX_WB_DDONE (1 << 6) +#define RT2860_WPDMA_BT_SIZE_SHIFT 4 +#define RT2860_WPDMA_BT_SIZE16 0 +#define RT2860_WPDMA_BT_SIZE32 1 +#define RT2860_WPDMA_BT_SIZE64 2 +#define RT2860_WPDMA_BT_SIZE128 3 +#define RT2860_RX_DMA_BUSY (1 << 3) +#define RT2860_RX_DMA_EN (1 << 2) +#define RT2860_TX_DMA_BUSY (1 << 1) +#define RT2860_TX_DMA_EN (1 << 0) + +/* possible flags for register DELAY_INT_CFG */ +#define RT2860_TXDLY_INT_EN (1U << 31) +#define RT2860_TXMAX_PINT_SHIFT 24 +#define RT2860_TXMAX_PTIME_SHIFT 16 +#define RT2860_RXDLY_INT_EN (1 << 15) +#define RT2860_RXMAX_PINT_SHIFT 8 +#define RT2860_RXMAX_PTIME_SHIFT 0 + +/* possible flags for register GPIO_CTRL */ +#define RT2860_GPIO_D_SHIFT 8 +#define RT2860_GPIO_O_SHIFT 0 + +/* possible flags for register USB_DMA_CFG */ +#define RT2860_USB_TX_BUSY (1U << 31) +#define RT2860_USB_RX_BUSY (1 << 30) +#define RT2860_USB_EPOUT_VLD_SHIFT 24 +#define RT2860_USB_TX_EN (1 << 23) +#define RT2860_USB_RX_EN (1 << 22) +#define RT2860_USB_RX_AGG_EN (1 << 21) +#define RT2860_USB_TXOP_HALT (1 << 20) +#define RT2860_USB_TX_CLEAR (1 << 19) +#define RT2860_USB_PHY_WD_EN (1 << 16) +#define RT2860_USB_RX_AGG_LMT(x) ((x) << 8) /* in unit of 1KB */ +#define RT2860_USB_RX_AGG_TO(x) ((x) & 0xff) /* in unit of 33ns */ + +/* possible flags for register US_CYC_CNT */ +#define RT2860_TEST_EN (1 << 24) +#define RT2860_TEST_SEL_SHIFT 16 +#define RT2860_BT_MODE_EN (1 << 8) +#define RT2860_US_CYC_CNT_SHIFT 0 + +/* possible flags for register SYS_CTRL */ +#define RT2860_HST_PM_SEL (1 << 16) +#define RT2860_CAP_MODE (1 << 14) +#define RT2860_PME_OEN (1 << 13) +#define RT2860_CLKSELECT (1 << 12) +#define RT2860_PBF_CLK_EN (1 << 11) +#define RT2860_MAC_CLK_EN (1 << 10) +#define RT2860_DMA_CLK_EN (1 << 9) +#define RT2860_MCU_READY (1 << 7) +#define RT2860_ASY_RESET (1 << 4) +#define RT2860_PBF_RESET (1 << 3) +#define RT2860_MAC_RESET (1 << 2) +#define RT2860_DMA_RESET (1 << 1) +#define RT2860_MCU_RESET (1 << 0) + +/* possible values for register HOST_CMD */ +#define RT2860_MCU_CMD_SLEEP 0x30 +#define RT2860_MCU_CMD_WAKEUP 0x31 +#define RT2860_MCU_CMD_LEDS 0x50 +#define RT2860_MCU_CMD_LED_RSSI 0x51 +#define RT2860_MCU_CMD_LED1 0x52 +#define RT2860_MCU_CMD_LED2 0x53 +#define RT2860_MCU_CMD_LED3 0x54 +#define RT2860_MCU_CMD_RFRESET 0x72 +#define RT2860_MCU_CMD_ANTSEL 0x73 +#define RT2860_MCU_CMD_BBP 0x80 +#define RT2860_MCU_CMD_PSLEVEL 0x83 + +/* possible flags for register PBF_CFG */ +#define RT2860_TX1Q_NUM_SHIFT 21 +#define RT2860_TX2Q_NUM_SHIFT 16 +#define RT2860_NULL0_MODE (1 << 15) +#define RT2860_NULL1_MODE (1 << 14) +#define RT2860_RX_DROP_MODE (1 << 13) +#define RT2860_TX0Q_MANUAL (1 << 12) +#define RT2860_TX1Q_MANUAL (1 << 11) +#define RT2860_TX2Q_MANUAL (1 << 10) +#define RT2860_RX0Q_MANUAL (1 << 9) +#define RT2860_HCCA_EN (1 << 8) +#define RT2860_TX0Q_EN (1 << 4) +#define RT2860_TX1Q_EN (1 << 3) +#define RT2860_TX2Q_EN (1 << 2) +#define RT2860_RX0Q_EN (1 << 1) + +/* possible flags for register BUF_CTRL */ +#define RT2860_WRITE_TXQ(qid) (1 << (11 - (qid))) +#define RT2860_NULL0_KICK (1 << 7) +#define RT2860_NULL1_KICK (1 << 6) +#define RT2860_BUF_RESET (1 << 5) +#define RT2860_READ_TXQ(qid) (1 << (3 - (qid)) +#define RT2860_READ_RX0Q (1 << 0) + +/* possible flags for registers MCU_INT_STA/MCU_INT_ENA */ +#define RT2860_MCU_MAC_INT_8 (1 << 24) +#define RT2860_MCU_MAC_INT_7 (1 << 23) +#define RT2860_MCU_MAC_INT_6 (1 << 22) +#define RT2860_MCU_MAC_INT_4 (1 << 20) +#define RT2860_MCU_MAC_INT_3 (1 << 19) +#define RT2860_MCU_MAC_INT_2 (1 << 18) +#define RT2860_MCU_MAC_INT_1 (1 << 17) +#define RT2860_MCU_MAC_INT_0 (1 << 16) +#define RT2860_DTX0_INT (1 << 11) +#define RT2860_DTX1_INT (1 << 10) +#define RT2860_DTX2_INT (1 << 9) +#define RT2860_DRX0_INT (1 << 8) +#define RT2860_HCMD_INT (1 << 7) +#define RT2860_N0TX_INT (1 << 6) +#define RT2860_N1TX_INT (1 << 5) +#define RT2860_BCNTX_INT (1 << 4) +#define RT2860_MTX0_INT (1 << 3) +#define RT2860_MTX1_INT (1 << 2) +#define RT2860_MTX2_INT (1 << 1) +#define RT2860_MRX0_INT (1 << 0) + +/* possible flags for register TXRXQ_PCNT */ +#define RT2860_RX0Q_PCNT_MASK 0xff000000 +#define RT2860_TX2Q_PCNT_MASK 0x00ff0000 +#define RT2860_TX1Q_PCNT_MASK 0x0000ff00 +#define RT2860_TX0Q_PCNT_MASK 0x000000ff + +/* possible flags for register CAP_CTRL */ +#define RT2860_CAP_ADC_FEQ (1U << 31) +#define RT2860_CAP_START (1 << 30) +#define RT2860_MAN_TRIG (1 << 29) +#define RT2860_TRIG_OFFSET_SHIFT 16 +#define RT2860_START_ADDR_SHIFT 0 + +/* possible flags for register RF_CSR_CFG */ +#define RT3070_RF_KICK (1 << 17) +#define RT3070_RF_WRITE (1 << 16) + +/* possible flags for register EFUSE_CTRL */ +#define RT3070_SEL_EFUSE (1U << 31) +#define RT3070_EFSROM_KICK (1 << 30) +#define RT3070_EFSROM_AIN_MASK 0x03ff0000 +#define RT3070_EFSROM_AIN_SHIFT 16 +#define RT3070_EFSROM_MODE_MASK 0x000000c0 +#define RT3070_EFUSE_AOUT_MASK 0x0000003f + +/* possible flag for register DEBUG_INDEX */ +#define RT5592_SEL_XTAL (1U << 31) + +/* possible flags for register MAC_SYS_CTRL */ +#define RT2860_RX_TS_EN (1 << 7) +#define RT2860_WLAN_HALT_EN (1 << 6) +#define RT2860_PBF_LOOP_EN (1 << 5) +#define RT2860_CONT_TX_TEST (1 << 4) +#define RT2860_MAC_RX_EN (1 << 3) +#define RT2860_MAC_TX_EN (1 << 2) +#define RT2860_BBP_HRST (1 << 1) +#define RT2860_MAC_SRST (1 << 0) + +/* possible flags for register MAC_BSSID_DW1 */ +#define RT2860_MULTI_BCN_NUM_SHIFT 18 +#define RT2860_MULTI_BSSID_MODE_SHIFT 16 + +/* possible flags for register MAX_LEN_CFG */ +#define RT2860_MIN_MPDU_LEN_SHIFT 16 +#define RT2860_MAX_PSDU_LEN_SHIFT 12 +#define RT2860_MAX_PSDU_LEN8K 0 +#define RT2860_MAX_PSDU_LEN16K 1 +#define RT2860_MAX_PSDU_LEN32K 2 +#define RT2860_MAX_PSDU_LEN64K 3 +#define RT2860_MAX_MPDU_LEN_SHIFT 0 + +/* possible flags for registers BBP_CSR_CFG/H2M_BBPAGENT */ +#define RT2860_BBP_RW_PARALLEL (1 << 19) +#define RT2860_BBP_PAR_DUR_112_5 (1 << 18) +#define RT2860_BBP_CSR_KICK (1 << 17) +#define RT2860_BBP_CSR_READ (1 << 16) +#define RT2860_BBP_ADDR_SHIFT 8 +#define RT2860_BBP_DATA_SHIFT 0 + +/* possible flags for register RF_CSR_CFG0 */ +#define RT2860_RF_REG_CTRL (1U << 31) +#define RT2860_RF_LE_SEL1 (1 << 30) +#define RT2860_RF_LE_STBY (1 << 29) +#define RT2860_RF_REG_WIDTH_SHIFT 24 +#define RT2860_RF_REG_0_SHIFT 0 + +/* possible flags for register RF_CSR_CFG1 */ +#define RT2860_RF_DUR_5 (1 << 24) +#define RT2860_RF_REG_1_SHIFT 0 + +/* possible flags for register LED_CFG */ +#define RT2860_LED_POL (1 << 30) +#define RT2860_Y_LED_MODE_SHIFT 28 +#define RT2860_G_LED_MODE_SHIFT 26 +#define RT2860_R_LED_MODE_SHIFT 24 +#define RT2860_LED_MODE_OFF 0 +#define RT2860_LED_MODE_BLINK_TX 1 +#define RT2860_LED_MODE_SLOW_BLINK 2 +#define RT2860_LED_MODE_ON 3 +#define RT2860_SLOW_BLK_TIME_SHIFT 16 +#define RT2860_LED_OFF_TIME_SHIFT 8 +#define RT2860_LED_ON_TIME_SHIFT 0 + +/* possible flags for register XIFS_TIME_CFG */ +#define RT2860_BB_RXEND_EN (1 << 29) +#define RT2860_EIFS_TIME_SHIFT 20 +#define RT2860_OFDM_XIFS_TIME_SHIFT 16 +#define RT2860_OFDM_SIFS_TIME_SHIFT 8 +#define RT2860_CCK_SIFS_TIME_SHIFT 0 + +/* possible flags for register BKOFF_SLOT_CFG */ +#define RT2860_CC_DELAY_TIME_SHIFT 8 +#define RT2860_SLOT_TIME 0 + +/* possible flags for register NAV_TIME_CFG */ +#define RT2860_NAV_UPD (1U << 31) +#define RT2860_NAV_UPD_VAL_SHIFT 16 +#define RT2860_NAV_CLR_EN (1 << 15) +#define RT2860_NAV_TIMER_SHIFT 0 + +/* possible flags for register CH_TIME_CFG */ +#define RT2860_EIFS_AS_CH_BUSY (1 << 4) +#define RT2860_NAV_AS_CH_BUSY (1 << 3) +#define RT2860_RX_AS_CH_BUSY (1 << 2) +#define RT2860_TX_AS_CH_BUSY (1 << 1) +#define RT2860_CH_STA_TIMER_EN (1 << 0) + +/* possible values for register BCN_TIME_CFG */ +#define RT2860_TSF_INS_COMP_SHIFT 24 +#define RT2860_BCN_TX_EN (1 << 20) +#define RT2860_TBTT_TIMER_EN (1 << 19) +#define RT2860_TSF_SYNC_MODE_SHIFT 17 +#define RT2860_TSF_SYNC_MODE_DIS 0 +#define RT2860_TSF_SYNC_MODE_STA 1 +#define RT2860_TSF_SYNC_MODE_IBSS 2 +#define RT2860_TSF_SYNC_MODE_HOSTAP 3 +#define RT2860_TSF_TIMER_EN (1 << 16) +#define RT2860_BCN_INTVAL_SHIFT 0 + +/* possible flags for register TBTT_SYNC_CFG */ +#define RT2860_BCN_CWMIN_SHIFT 20 +#define RT2860_BCN_AIFSN_SHIFT 16 +#define RT2860_BCN_EXP_WIN_SHIFT 8 +#define RT2860_TBTT_ADJUST_SHIFT 0 + +/* possible flags for register INT_TIMER_CFG */ +#define RT2860_GP_TIMER_SHIFT 16 +#define RT2860_PRE_TBTT_TIMER_SHIFT 0 + +/* possible flags for register INT_TIMER_EN */ +#define RT2860_GP_TIMER_EN (1 << 1) +#define RT2860_PRE_TBTT_INT_EN (1 << 0) + +/* possible flags for register MAC_STATUS_REG */ +#define RT2860_RX_STATUS_BUSY (1 << 1) +#define RT2860_TX_STATUS_BUSY (1 << 0) + +/* possible flags for register PWR_PIN_CFG */ +#define RT2860_IO_ADDA_PD (1 << 3) +#define RT2860_IO_PLL_PD (1 << 2) +#define RT2860_IO_RA_PE (1 << 1) +#define RT2860_IO_RF_PE (1 << 0) + +/* possible flags for register AUTO_WAKEUP_CFG */ +#define RT2860_AUTO_WAKEUP_EN (1 << 15) +#define RT2860_SLEEP_TBTT_NUM_SHIFT 8 +#define RT2860_WAKEUP_LEAD_TIME_SHIFT 0 + +/* possible flags for register TX_PIN_CFG */ +#define RT2860_TRSW_POL (1 << 19) +#define RT2860_TRSW_EN (1 << 18) +#define RT2860_RFTR_POL (1 << 17) +#define RT2860_RFTR_EN (1 << 16) +#define RT2860_LNA_PE_G1_POL (1 << 15) +#define RT2860_LNA_PE_A1_POL (1 << 14) +#define RT2860_LNA_PE_G0_POL (1 << 13) +#define RT2860_LNA_PE_A0_POL (1 << 12) +#define RT2860_LNA_PE_G1_EN (1 << 11) +#define RT2860_LNA_PE_A1_EN (1 << 10) +#define RT2860_LNA_PE1_EN (RT2860_LNA_PE_A1_EN | RT2860_LNA_PE_G1_EN) +#define RT2860_LNA_PE_G0_EN (1 << 9) +#define RT2860_LNA_PE_A0_EN (1 << 8) +#define RT2860_LNA_PE0_EN (RT2860_LNA_PE_A0_EN | RT2860_LNA_PE_G0_EN) +#define RT2860_PA_PE_G1_POL (1 << 7) +#define RT2860_PA_PE_A1_POL (1 << 6) +#define RT2860_PA_PE_G0_POL (1 << 5) +#define RT2860_PA_PE_A0_POL (1 << 4) +#define RT2860_PA_PE_G1_EN (1 << 3) +#define RT2860_PA_PE_A1_EN (1 << 2) +#define RT2860_PA_PE_G0_EN (1 << 1) +#define RT2860_PA_PE_A0_EN (1 << 0) + +/* possible flags for register TX_BAND_CFG */ +#define RT2860_5G_BAND_SEL_N (1 << 2) +#define RT2860_5G_BAND_SEL_P (1 << 1) +#define RT2860_TX_BAND_SEL (1 << 0) + +/* possible flags for register TX_SW_CFG0 */ +#define RT2860_DLY_RFTR_EN_SHIFT 24 +#define RT2860_DLY_TRSW_EN_SHIFT 16 +#define RT2860_DLY_PAPE_EN_SHIFT 8 +#define RT2860_DLY_TXPE_EN_SHIFT 0 + +/* possible flags for register TX_SW_CFG1 */ +#define RT2860_DLY_RFTR_DIS_SHIFT 16 +#define RT2860_DLY_TRSW_DIS_SHIFT 8 +#define RT2860_DLY_PAPE_DIS SHIFT 0 + +/* possible flags for register TX_SW_CFG2 */ +#define RT2860_DLY_LNA_EN_SHIFT 24 +#define RT2860_DLY_LNA_DIS_SHIFT 16 +#define RT2860_DLY_DAC_EN_SHIFT 8 +#define RT2860_DLY_DAC_DIS_SHIFT 0 + +/* possible flags for register TXOP_THRES_CFG */ +#define RT2860_TXOP_REM_THRES_SHIFT 24 +#define RT2860_CF_END_THRES_SHIFT 16 +#define RT2860_RDG_IN_THRES 8 +#define RT2860_RDG_OUT_THRES 0 + +/* possible flags for register TXOP_CTRL_CFG */ +#define RT2860_EXT_CW_MIN_SHIFT 16 +#define RT2860_EXT_CCA_DLY_SHIFT 8 +#define RT2860_EXT_CCA_EN (1 << 7) +#define RT2860_LSIG_TXOP_EN (1 << 6) +#define RT2860_TXOP_TRUN_EN_MIMOPS (1 << 4) +#define RT2860_TXOP_TRUN_EN_TXOP (1 << 3) +#define RT2860_TXOP_TRUN_EN_RATE (1 << 2) +#define RT2860_TXOP_TRUN_EN_AC (1 << 1) +#define RT2860_TXOP_TRUN_EN_TIMEOUT (1 << 0) + +/* possible flags for register TX_RTS_CFG */ +#define RT2860_RTS_FBK_EN (1 << 24) +#define RT2860_RTS_THRES_SHIFT 8 +#define RT2860_RTS_RTY_LIMIT_SHIFT 0 + +/* possible flags for register TX_TIMEOUT_CFG */ +#define RT2860_TXOP_TIMEOUT_SHIFT 16 +#define RT2860_RX_ACK_TIMEOUT_SHIFT 8 +#define RT2860_MPDU_LIFE_TIME_SHIFT 4 + +/* possible flags for register TX_RTY_CFG */ +#define RT2860_TX_AUTOFB_EN (1 << 30) +#define RT2860_AGG_RTY_MODE_TIMER (1 << 29) +#define RT2860_NAG_RTY_MODE_TIMER (1 << 28) +#define RT2860_LONG_RTY_THRES_SHIFT 16 +#define RT2860_LONG_RTY_LIMIT_SHIFT 8 +#define RT2860_SHORT_RTY_LIMIT_SHIFT 0 + +/* possible flags for register TX_LINK_CFG */ +#define RT2860_REMOTE_MFS_SHIFT 24 +#define RT2860_REMOTE_MFB_SHIFT 16 +#define RT2860_TX_CFACK_EN (1 << 12) +#define RT2860_TX_RDG_EN (1 << 11) +#define RT2860_TX_MRQ_EN (1 << 10) +#define RT2860_REMOTE_UMFS_EN (1 << 9) +#define RT2860_TX_MFB_EN (1 << 8) +#define RT2860_REMOTE_MFB_LT_SHIFT 0 + +/* possible flags for registers *_PROT_CFG */ +#define RT2860_RTSTH_EN (1 << 26) +#define RT2860_TXOP_ALLOW_GF40 (1 << 25) +#define RT2860_TXOP_ALLOW_GF20 (1 << 24) +#define RT2860_TXOP_ALLOW_MM40 (1 << 23) +#define RT2860_TXOP_ALLOW_MM20 (1 << 22) +#define RT2860_TXOP_ALLOW_OFDM (1 << 21) +#define RT2860_TXOP_ALLOW_CCK (1 << 20) +#define RT2860_TXOP_ALLOW_ALL (0x3f << 20) +#define RT2860_PROT_NAV_SHORT (1 << 18) +#define RT2860_PROT_NAV_LONG (2 << 18) +#define RT2860_PROT_CTRL_RTS_CTS (1 << 16) +#define RT2860_PROT_CTRL_CTS (2 << 16) + +/* possible flags for registers EXP_{CTS,ACK}_TIME */ +#define RT2860_EXP_OFDM_TIME_SHIFT 16 +#define RT2860_EXP_CCK_TIME_SHIFT 0 + +/* possible flags for register RX_FILTR_CFG */ +#define RT2860_DROP_CTRL_RSV (1 << 16) +#define RT2860_DROP_BAR (1 << 15) +#define RT2860_DROP_BA (1 << 14) +#define RT2860_DROP_PSPOLL (1 << 13) +#define RT2860_DROP_RTS (1 << 12) +#define RT2860_DROP_CTS (1 << 11) +#define RT2860_DROP_ACK (1 << 10) +#define RT2860_DROP_CFEND (1 << 9) +#define RT2860_DROP_CFACK (1 << 8) +#define RT2860_DROP_DUPL (1 << 7) +#define RT2860_DROP_BC (1 << 6) +#define RT2860_DROP_MC (1 << 5) +#define RT2860_DROP_VER_ERR (1 << 4) +#define RT2860_DROP_NOT_MYBSS (1 << 3) +#define RT2860_DROP_UC_NOME (1 << 2) +#define RT2860_DROP_PHY_ERR (1 << 1) +#define RT2860_DROP_CRC_ERR (1 << 0) + +/* possible flags for register AUTO_RSP_CFG */ +#define RT2860_CTRL_PWR_BIT (1 << 7) +#define RT2860_BAC_ACK_POLICY (1 << 6) +#define RT2860_CCK_SHORT_EN (1 << 4) +#define RT2860_CTS_40M_REF_EN (1 << 3) +#define RT2860_CTS_40M_MODE_EN (1 << 2) +#define RT2860_BAC_ACKPOLICY_EN (1 << 1) +#define RT2860_AUTO_RSP_EN (1 << 0) + +/* possible flags for register SIFS_COST_CFG */ +#define RT2860_OFDM_SIFS_COST_SHIFT 8 +#define RT2860_CCK_SIFS_COST_SHIFT 0 + +/* possible flags for register TXOP_HLDR_ET */ +#define RT2860_TXOP_ETM1_EN (1 << 25) +#define RT2860_TXOP_ETM0_EN (1 << 24) +#define RT2860_TXOP_ETM_THRES_SHIFT 16 +#define RT2860_TXOP_ETO_EN (1 << 8) +#define RT2860_TXOP_ETO_THRES_SHIFT 1 +#define RT2860_PER_RX_RST_EN (1 << 0) + +/* possible flags for register TX_STAT_FIFO */ +#define RT2860_TXQ_MCS_SHIFT 16 +#define RT2860_TXQ_WCID_SHIFT 8 +#define RT2860_TXQ_ACKREQ (1 << 7) +#define RT2860_TXQ_AGG (1 << 6) +#define RT2860_TXQ_OK (1 << 5) +#define RT2860_TXQ_PID_SHIFT 1 +#define RT2860_TXQ_VLD (1 << 0) + +/* possible flags for register WCID_ATTR */ +#define RT2860_MODE_NOSEC 0 +#define RT2860_MODE_WEP40 1 +#define RT2860_MODE_WEP104 2 +#define RT2860_MODE_TKIP 3 +#define RT2860_MODE_AES_CCMP 4 +#define RT2860_MODE_CKIP40 5 +#define RT2860_MODE_CKIP104 6 +#define RT2860_MODE_CKIP128 7 +#define RT2860_RX_PKEY_EN (1 << 0) + +/* possible flags for register H2M_MAILBOX */ +#define RT2860_H2M_BUSY (1 << 24) +#define RT2860_TOKEN_NO_INTR 0xff + +/* possible flags for MCU command RT2860_MCU_CMD_LEDS */ +#define RT2860_LED_RADIO (1 << 13) +#define RT2860_LED_LINK_2GHZ (1 << 14) +#define RT2860_LED_LINK_5GHZ (1 << 15) + +/* possible flags for RT3020 RF register 1 */ +#define RT3070_RF_BLOCK (1 << 0) +#define RT3070_PLL_PD (1 << 1) +#define RT3070_RX0_PD (1 << 2) +#define RT3070_TX0_PD (1 << 3) +#define RT3070_RX1_PD (1 << 4) +#define RT3070_TX1_PD (1 << 5) +#define RT3070_RX2_PD (1 << 6) +#define RT3070_TX2_PD (1 << 7) + +/* possible flags for RT3020 RF register 15 */ +#define RT3070_TX_LO2 (1 << 3) + +/* possible flags for RT3020 RF register 17 */ +#define RT3070_TX_LO1 (1 << 3) + +/* possible flags for RT3020 RF register 20 */ +#define RT3070_RX_LO1 (1 << 3) + +/* possible flags for RT3020 RF register 21 */ +#define RT3070_RX_LO2 (1 << 3) + +/* possible flags for RT3053 RF register 18 */ +#define RT3593_AUTOTUNE_BYPASS (1 << 6) + +/* possible flags for RT3053 RF register 50 */ +#define RT3593_TX_LO2 (1 << 4) + +/* possible flags for RT3053 RF register 51 */ +#define RT3593_TX_LO1 (1 << 4) + +/* Possible flags for RT5390 RF register 2. */ +#define RT5390_RESCAL (1 << 7) + +/* Possible flags for RT5390 RF register 3. */ +#define RT5390_VCOCAL (1 << 7) + +/* Possible flags for RT5390 RF register 38. */ +#define RT5390_RX_LO1 (1 << 5) + +/* Possible flags for RT5390 RF register 39. */ +#define RT5390_RX_LO2 (1 << 7) + +/* Possible flags for RT5390 BBP register 4. */ +#define RT5390_MAC_IF_CTRL (1 << 6) + +/* Possible flags for RT5390 BBP register 105. */ +#define RT5390_MLD (1 << 2) +#define RT5390_EN_SIG_MODULATION (1 << 3) + +/* RT2860 TX descriptor */ +struct rt2860_txd { + uint32_t sdp0; /* Segment Data Pointer 0 */ + uint16_t sdl1; /* Segment Data Length 1 */ +#define RT2860_TX_BURST (1 << 15) +#define RT2860_TX_LS1 (1 << 14) /* SDP1 is the last segment */ + + uint16_t sdl0; /* Segment Data Length 0 */ +#define RT2860_TX_DDONE (1 << 15) +#define RT2860_TX_LS0 (1 << 14) /* SDP0 is the last segment */ + + uint32_t sdp1; /* Segment Data Pointer 1 */ + uint8_t reserved[3]; + uint8_t flags; +#define RT2860_TX_QSEL_SHIFT 1 +#define RT2860_TX_QSEL_MGMT (0 << 1) +#define RT2860_TX_QSEL_HCCA (1 << 1) +#define RT2860_TX_QSEL_EDCA (2 << 1) +#define RT2860_TX_WIV (1 << 0) +} __packed; + +/* RT2870 TX descriptor */ +struct rt2870_txd { + uint16_t len; + uint8_t pad; + uint8_t flags; +} __packed; + +/* TX Wireless Information */ +struct rt2860_txwi { + uint8_t flags; +#define RT2860_TX_MPDU_DSITY_SHIFT 5 +#define RT2860_TX_AMPDU (1 << 4) +#define RT2860_TX_TS (1 << 3) +#define RT2860_TX_CFACK (1 << 2) +#define RT2860_TX_MMPS (1 << 1) +#define RT2860_TX_FRAG (1 << 0) + + uint8_t txop; +#define RT2860_TX_TXOP_HT 0 +#define RT2860_TX_TXOP_PIFS 1 +#define RT2860_TX_TXOP_SIFS 2 +#define RT2860_TX_TXOP_BACKOFF 3 + + uint16_t phy; +#define RT2860_PHY_MODE 0xc000 +#define RT2860_PHY_CCK (0 << 14) +#define RT2860_PHY_OFDM (1 << 14) +#define RT2860_PHY_HT (2 << 14) +#define RT2860_PHY_HT_GF (3 << 14) +#define RT2860_PHY_SGI (1 << 8) +#define RT2860_PHY_BW40 (1 << 7) +#define RT2860_PHY_MCS 0x7f +#define RT2860_PHY_SHPRE (1 << 3) + + uint8_t xflags; +#define RT2860_TX_BAWINSIZE_SHIFT 2 +#define RT2860_TX_NSEQ (1 << 1) +#define RT2860_TX_ACK (1 << 0) + + uint8_t wcid; /* Wireless Client ID */ + uint16_t len; +#define RT2860_TX_PID_SHIFT 12 + + uint32_t iv; + uint32_t eiv; +} __packed; + +/* RT2860 RX descriptor */ +struct rt2860_rxd { + uint32_t sdp0; + uint16_t sdl1; /* unused */ + uint16_t sdl0; +#define RT2860_RX_DDONE (1 << 15) +#define RT2860_RX_LS0 (1 << 14) + + uint32_t sdp1; /* unused */ + uint32_t flags; +#define RT2860_RX_DEC (1 << 16) +#define RT2860_RX_AMPDU (1 << 15) +#define RT2860_RX_L2PAD (1 << 14) +#define RT2860_RX_RSSI (1 << 13) +#define RT2860_RX_HTC (1 << 12) +#define RT2860_RX_AMSDU (1 << 11) +#define RT2860_RX_MICERR (1 << 10) +#define RT2860_RX_ICVERR (1 << 9) +#define RT2860_RX_CRCERR (1 << 8) +#define RT2860_RX_MYBSS (1 << 7) +#define RT2860_RX_BC (1 << 6) +#define RT2860_RX_MC (1 << 5) +#define RT2860_RX_UC2ME (1 << 4) +#define RT2860_RX_FRAG (1 << 3) +#define RT2860_RX_NULL (1 << 2) +#define RT2860_RX_DATA (1 << 1) +#define RT2860_RX_BA (1 << 0) +} __packed; + +/* RT2870 RX descriptor */ +struct rt2870_rxd { + /* single 32-bit field */ + uint32_t flags; +} __packed; + +/* RX Wireless Information */ +struct rt2860_rxwi { + uint8_t wcid; + uint8_t keyidx; +#define RT2860_RX_UDF_SHIFT 5 +#define RT2860_RX_BSS_IDX_SHIFT 2 + + uint16_t len; +#define RT2860_RX_TID_SHIFT 12 + + uint16_t seq; + uint16_t phy; + uint8_t rssi[3]; + uint8_t reserved1; + uint8_t snr[2]; + uint16_t reserved2; +} __packed; + +#define RT2860_RF_2820 0x0001 /* 2T3R */ +#define RT2860_RF_2850 0x0002 /* dual-band 2T3R */ +#define RT2860_RF_2720 0x0003 /* 1T2R */ +#define RT2860_RF_2750 0x0004 /* dual-band 1T2R */ +#define RT3070_RF_3020 0x0005 /* 1T1R */ +#define RT3070_RF_2020 0x0006 /* b/g */ +#define RT3070_RF_3021 0x0007 /* 1T2R */ +#define RT3070_RF_3022 0x0008 /* 2T2R */ +#define RT3070_RF_3052 0x0009 /* dual-band 2T2R */ +#define RT3593_RF_3053 0x000d /* dual-band 3T3R */ +#define RT5592_RF_5592 0x000f /* dual-band 2T2R */ +#define RT5390_RF_5370 0x5370 /* 1T1R */ +#define RT5390_RF_5372 0x5372 /* 2T2R */ + +/* USB commands for RT2870 only */ +#define RT2870_RESET 1 +#define RT2870_WRITE_2 2 +#define RT2870_WRITE_REGION_1 6 +#define RT2870_READ_REGION_1 7 +#define RT2870_EEPROM_READ 9 + +#define RT2860_EEPROM_DELAY 1 /* minimum hold time (microsecond) */ + +#define RT2860_EEPROM_VERSION 0x01 +#define RT2860_EEPROM_MAC01 0x02 +#define RT2860_EEPROM_MAC23 0x03 +#define RT2860_EEPROM_MAC45 0x04 +#define RT2860_EEPROM_PCIE_PSLEVEL 0x11 +#define RT2860_EEPROM_REV 0x12 +#define RT2860_EEPROM_ANTENNA 0x1a +#define RT2860_EEPROM_CONFIG 0x1b +#define RT2860_EEPROM_COUNTRY 0x1c +#define RT2860_EEPROM_FREQ_LEDS 0x1d +#define RT2860_EEPROM_LED1 0x1e +#define RT2860_EEPROM_LED2 0x1f +#define RT2860_EEPROM_LED3 0x20 +#define RT2860_EEPROM_LNA 0x22 +#define RT2860_EEPROM_RSSI1_2GHZ 0x23 +#define RT2860_EEPROM_RSSI2_2GHZ 0x24 +#define RT2860_EEPROM_RSSI1_5GHZ 0x25 +#define RT2860_EEPROM_RSSI2_5GHZ 0x26 +#define RT2860_EEPROM_DELTAPWR 0x28 +#define RT2860_EEPROM_PWR2GHZ_BASE1 0x29 +#define RT2860_EEPROM_PWR2GHZ_BASE2 0x30 +#define RT2860_EEPROM_TSSI1_2GHZ 0x37 +#define RT2860_EEPROM_TSSI2_2GHZ 0x38 +#define RT2860_EEPROM_TSSI3_2GHZ 0x39 +#define RT2860_EEPROM_TSSI4_2GHZ 0x3a +#define RT2860_EEPROM_TSSI5_2GHZ 0x3b +#define RT2860_EEPROM_PWR5GHZ_BASE1 0x3c +#define RT2860_EEPROM_PWR5GHZ_BASE2 0x53 +#define RT2860_EEPROM_TSSI1_5GHZ 0x6a +#define RT2860_EEPROM_TSSI2_5GHZ 0x6b +#define RT2860_EEPROM_TSSI3_5GHZ 0x6c +#define RT2860_EEPROM_TSSI4_5GHZ 0x6d +#define RT2860_EEPROM_TSSI5_5GHZ 0x6e +#define RT2860_EEPROM_RPWR 0x6f +#define RT2860_EEPROM_BBP_BASE 0x78 +#define RT3071_EEPROM_RF_BASE 0x82 + +/* EEPROM registers for RT3593. */ +#define RT3593_EEPROM_FREQ_LEDS 0x21 +#define RT3593_EEPROM_FREQ 0x22 +#define RT3593_EEPROM_LED1 0x22 +#define RT3593_EEPROM_LED2 0x23 +#define RT3593_EEPROM_LED3 0x24 +#define RT3593_EEPROM_LNA 0x26 +#define RT3593_EEPROM_LNA_5GHZ 0x27 +#define RT3593_EEPROM_RSSI1_2GHZ 0x28 +#define RT3593_EEPROM_RSSI2_2GHZ 0x29 +#define RT3593_EEPROM_RSSI1_5GHZ 0x2a +#define RT3593_EEPROM_RSSI2_5GHZ 0x2b +#define RT3593_EEPROM_PWR2GHZ_BASE1 0x30 +#define RT3593_EEPROM_PWR2GHZ_BASE2 0x37 +#define RT3593_EEPROM_PWR2GHZ_BASE3 0x3e +#define RT3593_EEPROM_PWR5GHZ_BASE1 0x4b +#define RT3593_EEPROM_PWR5GHZ_BASE2 0x65 +#define RT3593_EEPROM_PWR5GHZ_BASE3 0x7f + +/* + * EEPROM IQ calibration. + */ +#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ 0x130 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ 0x131 +#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ 0x133 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ 0x134 +#define RT5390_EEPROM_RF_IQ_COMPENSATION_CTL 0x13c +#define RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL 0x13d +#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ 0x144 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ 0x145 +#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ 0x146 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ 0x147 +#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ 0x148 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ 0x149 +#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ 0x14a +#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ 0x14b +#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ 0x14c +#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ 0x14d +#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ 0x14e +#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ 0x14f + +#define RT2860_RIDX_CCK1 0 +#define RT2860_RIDX_CCK11 3 +#define RT2860_RIDX_OFDM6 4 +#define RT2860_RIDX_MAX 12 + +/* + * EEPROM access macro. + */ +#define RT2860_EEPROM_CTL(sc, val) do { \ + RAL_WRITE((sc), RT2860_PCI_EECTRL, (val)); \ + RAL_BARRIER_READ_WRITE((sc)); \ + DELAY(RT2860_EEPROM_DELAY); \ +} while (/* CONSTCOND */0) + +/* + * Default values for MAC registers; values taken from the reference driver. + */ +#define RT2870_DEF_MAC \ + { RT2860_BCN_OFFSET0, 0xf8f0e8e0 }, \ + { RT2860_BCN_OFFSET1, 0x6f77d0c8 }, \ + { RT2860_LEGACY_BASIC_RATE, 0x0000013f }, \ + { RT2860_HT_BASIC_RATE, 0x00008003 }, \ + { RT2860_MAC_SYS_CTRL, 0x00000000 }, \ + { RT2860_BKOFF_SLOT_CFG, 0x00000209 }, \ + { RT2860_TX_SW_CFG0, 0x00000000 }, \ + { RT2860_TX_SW_CFG1, 0x00080606 }, \ + { RT2860_TX_LINK_CFG, 0x00001020 }, \ + { RT2860_TX_TIMEOUT_CFG, 0x000a2090 }, \ + { RT2860_MAX_LEN_CFG, 0x00001f00 }, \ + { RT2860_LED_CFG, 0x7f031e46 }, \ + { RT2860_WMM_AIFSN_CFG, 0x00002273 }, \ + { RT2860_WMM_CWMIN_CFG, 0x00002344 }, \ + { RT2860_WMM_CWMAX_CFG, 0x000034aa }, \ + { RT2860_MAX_PCNT, 0x1f3fbf9f }, \ + { RT2860_TX_RTY_CFG, 0x47d01f0f }, \ + { RT2860_AUTO_RSP_CFG, 0x00000013 }, \ + { RT2860_CCK_PROT_CFG, 0x05740003 }, \ + { RT2860_OFDM_PROT_CFG, 0x05740003 }, \ + { RT2860_PBF_CFG, 0x00f40006 }, \ + { RT2860_WPDMA_GLO_CFG, 0x00000030 }, \ + { RT2860_GF20_PROT_CFG, 0x01744004 }, \ + { RT2860_GF40_PROT_CFG, 0x03f44084 }, \ + { RT2860_MM20_PROT_CFG, 0x01744004 }, \ + { RT2860_MM40_PROT_CFG, 0x03f44084 }, \ + { RT2860_TXOP_CTRL_CFG, 0x0000583f }, \ + { RT2860_TXOP_HLDR_ET, 0x00000002 }, \ + { RT2860_TX_RTS_CFG, 0x00092b20 }, \ + { RT2860_EXP_ACK_TIME, 0x002400ca }, \ + { RT2860_XIFS_TIME_CFG, 0x33a41010 }, \ + { RT2860_PWR_PIN_CFG, 0x00000003 } + +/* + * Default values for BBP registers; values taken from the reference driver. + */ +#define RT2860_DEF_BBP \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 68, 0x0b }, \ + { 69, 0x12 }, \ + { 70, 0x0a }, \ + { 73, 0x10 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x6a }, \ + { 84, 0x99 }, \ + { 86, 0x00 }, \ + { 91, 0x04 }, \ + { 92, 0x00 }, \ + { 103, 0x00 }, \ + { 105, 0x05 }, \ + { 106, 0x35 } + +#define RT5390_DEF_BBP \ + { 31, 0x08 }, \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 68, 0x0b }, \ + { 69, 0x0d }, \ + { 70, 0x06 }, \ + { 73, 0x13 }, \ + { 75, 0x46 }, \ + { 76, 0x28 }, \ + { 77, 0x59 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x7a }, \ + { 84, 0x9a }, \ + { 86, 0x38 }, \ + { 91, 0x04 }, \ + { 92, 0x02 }, \ + { 103, 0xc0 }, \ + { 104, 0x92 }, \ + { 105, 0x3c }, \ + { 106, 0x03 }, \ + { 128, 0x12 } + +#define RT5592_DEF_BBP \ + { 20, 0x06 }, \ + { 31, 0x08 }, \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 68, 0xdd }, \ + { 69, 0x1a }, \ + { 70, 0x05 }, \ + { 73, 0x13 }, \ + { 74, 0x0f }, \ + { 75, 0x4f }, \ + { 76, 0x28 }, \ + { 77, 0x59 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x6a }, \ + { 84, 0x9a }, \ + { 86, 0x38 }, \ + { 88, 0x90 }, \ + { 91, 0x04 }, \ + { 92, 0x02 }, \ + { 95, 0x9a }, \ + { 98, 0x12 }, \ + { 103, 0xc0 }, \ + { 104, 0x92 }, \ + { 105, 0x3c }, \ + { 106, 0x35 }, \ + { 128, 0x12 }, \ + { 134, 0xd0 }, \ + { 135, 0xf6 }, \ + { 137, 0x0f } + +/* + * Channel map for run(4) driver; taken from the table below. + */ +static const uint8_t run_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + +static const uint8_t run_chan_5ghz[] = + { 36, 38, 40, 44, 46, 48, 52, 54, 56, 60, 62, 64, 100, 102, 104, + 108, 110, 112, 116, 118, 120, 124, 126, 128, 132, 134, 136, 140, + 149, 151, 153, 157, 159, 161, 165, 167, 169, 171, 173, + 184, 188, 192, 196, 208, 212, 216 }; + +/* + * Default settings for RF registers; values derived from the reference driver. + */ +#define RT2860_RF2850 \ + { 1, 0x98402ecc, 0x984c0786, 0x9816b455, 0x9800510b }, \ + { 2, 0x98402ecc, 0x984c0786, 0x98168a55, 0x9800519f }, \ + { 3, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800518b }, \ + { 4, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800519f }, \ + { 5, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800518b }, \ + { 6, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800519f }, \ + { 7, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800518b }, \ + { 8, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800519f }, \ + { 9, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800518b }, \ + { 10, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800519f }, \ + { 11, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800518b }, \ + { 12, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800519f }, \ + { 13, 0x98402ecc, 0x984c079e, 0x98168a55, 0x9800518b }, \ + { 14, 0x98402ecc, 0x984c07a2, 0x98168a55, 0x98005193 }, \ + { 36, 0x98402ecc, 0x984c099a, 0x98158a55, 0x980ed1a3 }, \ + { 38, 0x98402ecc, 0x984c099e, 0x98158a55, 0x980ed193 }, \ + { 40, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed183 }, \ + { 44, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed1a3 }, \ + { 46, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed18b }, \ + { 48, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed19b }, \ + { 52, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed193 }, \ + { 54, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed1a3 }, \ + { 56, 0x98402ec8, 0x984c068e, 0x98158a55, 0x980ed18b }, \ + { 60, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed183 }, \ + { 62, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed193 }, \ + { 64, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed1a3 }, \ + { 100, 0x98402ec8, 0x984c06b2, 0x98178a55, 0x980ed783 }, \ + { 102, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed793 }, \ + { 104, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed1a3 }, \ + { 108, 0x98402ecc, 0x985c0a32, 0x98578a55, 0x980ed193 }, \ + { 110, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed183 }, \ + { 112, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed19b }, \ + { 116, 0x98402ecc, 0x984c0a3a, 0x98178a55, 0x980ed1a3 }, \ + { 118, 0x98402ecc, 0x984c0a3e, 0x98178a55, 0x980ed193 }, \ + { 120, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed183 }, \ + { 124, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed193 }, \ + { 126, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed15b }, \ + { 128, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed1a3 }, \ + { 132, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed18b }, \ + { 134, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed193 }, \ + { 136, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed19b }, \ + { 140, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed183 }, \ + { 149, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed1a7 }, \ + { 151, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed187 }, \ + { 153, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed18f }, \ + { 157, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed19f }, \ + { 159, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed1a7 }, \ + { 161, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed187 }, \ + { 165, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed197 }, \ + { 167, 0x98402ec4, 0x984c03d2, 0x98179855, 0x9815531f }, \ + { 169, 0x98402ec4, 0x984c03d2, 0x98179855, 0x98155327 }, \ + { 171, 0x98402ec4, 0x984c03d6, 0x98179855, 0x98155307 }, \ + { 173, 0x98402ec4, 0x984c03d6, 0x98179855, 0x9815530f }, \ + { 184, 0x95002ccc, 0x9500491e, 0x9509be55, 0x950c0a0b }, \ + { 188, 0x95002ccc, 0x95004922, 0x9509be55, 0x950c0a13 }, \ + { 192, 0x95002ccc, 0x95004926, 0x9509be55, 0x950c0a1b }, \ + { 196, 0x95002ccc, 0x9500492a, 0x9509be55, 0x950c0a23 }, \ + { 208, 0x95002ccc, 0x9500493a, 0x9509be55, 0x950c0a13 }, \ + { 212, 0x95002ccc, 0x9500493e, 0x9509be55, 0x950c0a1b }, \ + { 216, 0x95002ccc, 0x95004982, 0x9509be55, 0x950c0a23 } + +#define RT3070_RF3052 \ + { 0xf1, 2, 2 }, \ + { 0xf1, 2, 7 }, \ + { 0xf2, 2, 2 }, \ + { 0xf2, 2, 7 }, \ + { 0xf3, 2, 2 }, \ + { 0xf3, 2, 7 }, \ + { 0xf4, 2, 2 }, \ + { 0xf4, 2, 7 }, \ + { 0xf5, 2, 2 }, \ + { 0xf5, 2, 7 }, \ + { 0xf6, 2, 2 }, \ + { 0xf6, 2, 7 }, \ + { 0xf7, 2, 2 }, \ + { 0xf8, 2, 4 }, \ + { 0x56, 0, 4 }, \ + { 0x56, 0, 6 }, \ + { 0x56, 0, 8 }, \ + { 0x57, 0, 0 }, \ + { 0x57, 0, 2 }, \ + { 0x57, 0, 4 }, \ + { 0x57, 0, 8 }, \ + { 0x57, 0, 10 }, \ + { 0x58, 0, 0 }, \ + { 0x58, 0, 4 }, \ + { 0x58, 0, 6 }, \ + { 0x58, 0, 8 }, \ + { 0x5b, 0, 8 }, \ + { 0x5b, 0, 10 }, \ + { 0x5c, 0, 0 }, \ + { 0x5c, 0, 4 }, \ + { 0x5c, 0, 6 }, \ + { 0x5c, 0, 8 }, \ + { 0x5d, 0, 0 }, \ + { 0x5d, 0, 2 }, \ + { 0x5d, 0, 4 }, \ + { 0x5d, 0, 8 }, \ + { 0x5d, 0, 10 }, \ + { 0x5e, 0, 0 }, \ + { 0x5e, 0, 4 }, \ + { 0x5e, 0, 6 }, \ + { 0x5e, 0, 8 }, \ + { 0x5f, 0, 0 }, \ + { 0x5f, 0, 9 }, \ + { 0x5f, 0, 11 }, \ + { 0x60, 0, 1 }, \ + { 0x60, 0, 5 }, \ + { 0x60, 0, 7 }, \ + { 0x60, 0, 9 }, \ + { 0x61, 0, 1 }, \ + { 0x61, 0, 3 }, \ + { 0x61, 0, 5 }, \ + { 0x61, 0, 7 }, \ + { 0x61, 0, 9 } + +#define RT5592_RF5592_20MHZ \ + { 0x1e2, 4, 10, 3 }, \ + { 0x1e3, 4, 10, 3 }, \ + { 0x1e4, 4, 10, 3 }, \ + { 0x1e5, 4, 10, 3 }, \ + { 0x1e6, 4, 10, 3 }, \ + { 0x1e7, 4, 10, 3 }, \ + { 0x1e8, 4, 10, 3 }, \ + { 0x1e9, 4, 10, 3 }, \ + { 0x1ea, 4, 10, 3 }, \ + { 0x1eb, 4, 10, 3 }, \ + { 0x1ec, 4, 10, 3 }, \ + { 0x1ed, 4, 10, 3 }, \ + { 0x1ee, 4, 10, 3 }, \ + { 0x1f0, 8, 10, 3 }, \ + { 0xac, 8, 12, 1 }, \ + { 0xad, 0, 12, 1 }, \ + { 0xad, 4, 12, 1 }, \ + { 0xae, 0, 12, 1 }, \ + { 0xae, 4, 12, 1 }, \ + { 0xae, 8, 12, 1 }, \ + { 0xaf, 4, 12, 1 }, \ + { 0xaf, 8, 12, 1 }, \ + { 0xb0, 0, 12, 1 }, \ + { 0xb0, 8, 12, 1 }, \ + { 0xb1, 0, 12, 1 }, \ + { 0xb1, 4, 12, 1 }, \ + { 0xb7, 4, 12, 1 }, \ + { 0xb7, 8, 12, 1 }, \ + { 0xb8, 0, 12, 1 }, \ + { 0xb8, 8, 12, 1 }, \ + { 0xb9, 0, 12, 1 }, \ + { 0xb9, 4, 12, 1 }, \ + { 0xba, 0, 12, 1 }, \ + { 0xba, 4, 12, 1 }, \ + { 0xba, 8, 12, 1 }, \ + { 0xbb, 4, 12, 1 }, \ + { 0xbb, 8, 12, 1 }, \ + { 0xbc, 0, 12, 1 }, \ + { 0xbc, 8, 12, 1 }, \ + { 0xbd, 0, 12, 1 }, \ + { 0xbd, 4, 12, 1 }, \ + { 0xbe, 0, 12, 1 }, \ + { 0xbf, 6, 12, 1 }, \ + { 0xbf, 10, 12, 1 }, \ + { 0xc0, 2, 12, 1 }, \ + { 0xc0, 10, 12, 1 }, \ + { 0xc1, 2, 12, 1 }, \ + { 0xc1, 6, 12, 1 }, \ + { 0xc2, 2, 12, 1 }, \ + { 0xa4, 0, 12, 1 }, \ + { 0xa4, 4, 12, 1 }, \ + { 0xa5, 8, 12, 1 }, \ + { 0xa6, 0, 12, 1 } + +#define RT5592_RF5592_40MHZ \ + { 0xf1, 2, 10, 3 }, \ + { 0xf1, 7, 10, 3 }, \ + { 0xf2, 2, 10, 3 }, \ + { 0xf2, 7, 10, 3 }, \ + { 0xf3, 2, 10, 3 }, \ + { 0xf3, 7, 10, 3 }, \ + { 0xf4, 2, 10, 3 }, \ + { 0xf4, 7, 10, 3 }, \ + { 0xf5, 2, 10, 3 }, \ + { 0xf5, 7, 10, 3 }, \ + { 0xf6, 2, 10, 3 }, \ + { 0xf6, 7, 10, 3 }, \ + { 0xf7, 2, 10, 3 }, \ + { 0xf8, 4, 10, 3 }, \ + { 0x56, 4, 12, 1 }, \ + { 0x56, 6, 12, 1 }, \ + { 0x56, 8, 12, 1 }, \ + { 0x57, 0, 12, 1 }, \ + { 0x57, 2, 12, 1 }, \ + { 0x57, 4, 12, 1 }, \ + { 0x57, 8, 12, 1 }, \ + { 0x57, 10, 12, 1 }, \ + { 0x58, 0, 12, 1 }, \ + { 0x58, 4, 12, 1 }, \ + { 0x58, 6, 12, 1 }, \ + { 0x58, 8, 12, 1 }, \ + { 0x5b, 8, 12, 1 }, \ + { 0x5b, 10, 12, 1 }, \ + { 0x5c, 0, 12, 1 }, \ + { 0x5c, 4, 12, 1 }, \ + { 0x5c, 6, 12, 1 }, \ + { 0x5c, 8, 12, 1 }, \ + { 0x5d, 0, 12, 1 }, \ + { 0x5d, 2, 12, 1 }, \ + { 0x5d, 4, 12, 1 }, \ + { 0x5d, 8, 12, 1 }, \ + { 0x5d, 10, 12, 1 }, \ + { 0x5e, 0, 12, 1 }, \ + { 0x5e, 4, 12, 1 }, \ + { 0x5e, 6, 12, 1 }, \ + { 0x5e, 8, 12, 1 }, \ + { 0x5f, 0, 12, 1 }, \ + { 0x5f, 9, 12, 1 }, \ + { 0x5f, 11, 12, 1 }, \ + { 0x60, 1, 12, 1 }, \ + { 0x60, 5, 12, 1 }, \ + { 0x60, 7, 12, 1 }, \ + { 0x60, 9, 12, 1 }, \ + { 0x61, 1, 12, 1 }, \ + { 0x52, 0, 12, 1 }, \ + { 0x52, 4, 12, 1 }, \ + { 0x52, 8, 12, 1 }, \ + { 0x53, 0, 12, 1 } + +#define RT3070_DEF_RF \ + { 4, 0x40 }, \ + { 5, 0x03 }, \ + { 6, 0x02 }, \ + { 7, 0x60 }, \ + { 9, 0x0f }, \ + { 10, 0x41 }, \ + { 11, 0x21 }, \ + { 12, 0x7b }, \ + { 14, 0x90 }, \ + { 15, 0x58 }, \ + { 16, 0xb3 }, \ + { 17, 0x92 }, \ + { 18, 0x2c }, \ + { 19, 0x02 }, \ + { 20, 0xba }, \ + { 21, 0xdb }, \ + { 24, 0x16 }, \ + { 25, 0x03 }, \ + { 29, 0x1f } + +#define RT3572_DEF_RF \ + { 0, 0x70 }, \ + { 1, 0x81 }, \ + { 2, 0xf1 }, \ + { 3, 0x02 }, \ + { 4, 0x4c }, \ + { 5, 0x05 }, \ + { 6, 0x4a }, \ + { 7, 0xd8 }, \ + { 9, 0xc3 }, \ + { 10, 0xf1 }, \ + { 11, 0xb9 }, \ + { 12, 0x70 }, \ + { 13, 0x65 }, \ + { 14, 0xa0 }, \ + { 15, 0x53 }, \ + { 16, 0x4c }, \ + { 17, 0x23 }, \ + { 18, 0xac }, \ + { 19, 0x93 }, \ + { 20, 0xb3 }, \ + { 21, 0xd0 }, \ + { 22, 0x00 }, \ + { 23, 0x3c }, \ + { 24, 0x16 }, \ + { 25, 0x15 }, \ + { 26, 0x85 }, \ + { 27, 0x00 }, \ + { 28, 0x00 }, \ + { 29, 0x9b }, \ + { 30, 0x09 }, \ + { 31, 0x10 } + +#define RT3593_DEF_RF \ + { 1, 0x03 }, \ + { 3, 0x80 }, \ + { 5, 0x00 }, \ + { 6, 0x40 }, \ + { 8, 0xf1 }, \ + { 9, 0x02 }, \ + { 10, 0xd3 }, \ + { 11, 0x40 }, \ + { 12, 0x4e }, \ + { 13, 0x12 }, \ + { 18, 0x40 }, \ + { 22, 0x20 }, \ + { 30, 0x10 }, \ + { 31, 0x80 }, \ + { 32, 0x78 }, \ + { 33, 0x3b }, \ + { 34, 0x3c }, \ + { 35, 0xe0 }, \ + { 38, 0x86 }, \ + { 39, 0x23 }, \ + { 44, 0xd3 }, \ + { 45, 0xbb }, \ + { 46, 0x60 }, \ + { 49, 0x81 }, \ + { 50, 0x86 }, \ + { 51, 0x75 }, \ + { 52, 0x45 }, \ + { 53, 0x18 }, \ + { 54, 0x18 }, \ + { 55, 0x18 }, \ + { 56, 0xdb }, \ + { 57, 0x6e } + +#define RT5390_DEF_RF \ + { 1, 0x0f }, \ + { 2, 0x80 }, \ + { 3, 0x88 }, \ + { 5, 0x10 }, \ + { 6, 0xa0 }, \ + { 7, 0x00 }, \ + { 10, 0x53 }, \ + { 11, 0x4a }, \ + { 12, 0x46 }, \ + { 13, 0x9f }, \ + { 14, 0x00 }, \ + { 15, 0x00 }, \ + { 16, 0x00 }, \ + { 18, 0x03 }, \ + { 19, 0x00 }, \ + { 20, 0x00 }, \ + { 21, 0x00 }, \ + { 22, 0x20 }, \ + { 23, 0x00 }, \ + { 24, 0x00 }, \ + { 25, 0xc0 }, \ + { 26, 0x00 }, \ + { 27, 0x09 }, \ + { 28, 0x00 }, \ + { 29, 0x10 }, \ + { 30, 0x10 }, \ + { 31, 0x80 }, \ + { 32, 0x80 }, \ + { 33, 0x00 }, \ + { 34, 0x07 }, \ + { 35, 0x12 }, \ + { 36, 0x00 }, \ + { 37, 0x08 }, \ + { 38, 0x85 }, \ + { 39, 0x1b }, \ + { 40, 0x0b }, \ + { 41, 0xbb }, \ + { 42, 0xd2 }, \ + { 43, 0x9a }, \ + { 44, 0x0e }, \ + { 45, 0xa2 }, \ + { 46, 0x7b }, \ + { 47, 0x00 }, \ + { 48, 0x10 }, \ + { 49, 0x94 }, \ + { 52, 0x38 }, \ + { 53, 0x84 }, \ + { 54, 0x78 }, \ + { 55, 0x44 }, \ + { 56, 0x22 }, \ + { 57, 0x80 }, \ + { 58, 0x7f }, \ + { 59, 0x8f }, \ + { 60, 0x45 }, \ + { 61, 0xdd }, \ + { 62, 0x00 }, \ + { 63, 0x00 } + +#define RT5392_DEF_RF \ + { 1, 0x17 }, \ + { 3, 0x88 }, \ + { 5, 0x10 }, \ + { 6, 0xe0 }, \ + { 7, 0x00 }, \ + { 10, 0x53 }, \ + { 11, 0x4a }, \ + { 12, 0x46 }, \ + { 13, 0x9f }, \ + { 14, 0x00 }, \ + { 15, 0x00 }, \ + { 16, 0x00 }, \ + { 18, 0x03 }, \ + { 19, 0x4d }, \ + { 20, 0x00 }, \ + { 21, 0x8d }, \ + { 22, 0x20 }, \ + { 23, 0x0b }, \ + { 24, 0x44 }, \ + { 25, 0x80 }, \ + { 26, 0x82 }, \ + { 27, 0x09 }, \ + { 28, 0x00 }, \ + { 29, 0x10 }, \ + { 30, 0x10 }, \ + { 31, 0x80 }, \ + { 32, 0x20 }, \ + { 33, 0xc0 }, \ + { 34, 0x07 }, \ + { 35, 0x12 }, \ + { 36, 0x00 }, \ + { 37, 0x08 }, \ + { 38, 0x89 }, \ + { 39, 0x1b }, \ + { 40, 0x0f }, \ + { 41, 0xbb }, \ + { 42, 0xd5 }, \ + { 43, 0x9b }, \ + { 44, 0x0e }, \ + { 45, 0xa2 }, \ + { 46, 0x73 }, \ + { 47, 0x0c }, \ + { 48, 0x10 }, \ + { 49, 0x94 }, \ + { 50, 0x94 }, \ + { 51, 0x3a }, \ + { 52, 0x48 }, \ + { 53, 0x44 }, \ + { 54, 0x38 }, \ + { 55, 0x43 }, \ + { 56, 0xa1 }, \ + { 57, 0x00 }, \ + { 58, 0x39 }, \ + { 59, 0x07 }, \ + { 60, 0x45 }, \ + { 61, 0x91 }, \ + { 62, 0x39 }, \ + { 63, 0x07 } + +#define RT5592_DEF_RF \ + { 1, 0x3f }, \ + { 3, 0x08 }, \ + { 5, 0x10 }, \ + { 6, 0xe4 }, \ + { 7, 0x00 }, \ + { 14, 0x00 }, \ + { 15, 0x00 }, \ + { 16, 0x00 }, \ + { 18, 0x03 }, \ + { 19, 0x4d }, \ + { 20, 0x10 }, \ + { 21, 0x8d }, \ + { 26, 0x82 }, \ + { 28, 0x00 }, \ + { 29, 0x10 }, \ + { 33, 0xc0 }, \ + { 34, 0x07 }, \ + { 35, 0x12 }, \ + { 47, 0x0c }, \ + { 53, 0x22 }, \ + { 63, 0x07 } + +#define RT5592_2GHZ_DEF_RF \ + { 10, 0x90 }, \ + { 11, 0x4a }, \ + { 12, 0x52 }, \ + { 13, 0x42 }, \ + { 22, 0x40 }, \ + { 24, 0x4a }, \ + { 25, 0x80 }, \ + { 27, 0x42 }, \ + { 36, 0x80 }, \ + { 37, 0x08 }, \ + { 38, 0x89 }, \ + { 39, 0x1b }, \ + { 40, 0x0d }, \ + { 41, 0x9b }, \ + { 42, 0xd5 }, \ + { 43, 0x72 }, \ + { 44, 0x0e }, \ + { 45, 0xa2 }, \ + { 46, 0x6b }, \ + { 48, 0x10 }, \ + { 51, 0x3e }, \ + { 52, 0x48 }, \ + { 54, 0x38 }, \ + { 56, 0xa1 }, \ + { 57, 0x00 }, \ + { 58, 0x39 }, \ + { 60, 0x45 }, \ + { 61, 0x91 }, \ + { 62, 0x39 } + +#define RT5592_5GHZ_DEF_RF \ + { 10, 0x97 }, \ + { 11, 0x40 }, \ + { 25, 0xbf }, \ + { 27, 0x42 }, \ + { 36, 0x00 }, \ + { 37, 0x04 }, \ + { 38, 0x85 }, \ + { 40, 0x42 }, \ + { 41, 0xbb }, \ + { 42, 0xd7 }, \ + { 45, 0x41 }, \ + { 48, 0x00 }, \ + { 57, 0x77 }, \ + { 60, 0x05 }, \ + { 61, 0x01 } + +#define RT5592_CHAN_5GHZ \ + { 36, 64, 12, 0x2e }, \ + { 100, 165, 12, 0x0e }, \ + { 36, 64, 13, 0x22 }, \ + { 100, 165, 13, 0x42 }, \ + { 36, 64, 22, 0x60 }, \ + { 100, 165, 22, 0x40 }, \ + { 36, 64, 23, 0x7f }, \ + { 100, 153, 23, 0x3c }, \ + { 155, 165, 23, 0x38 }, \ + { 36, 50, 24, 0x09 }, \ + { 52, 64, 24, 0x07 }, \ + { 100, 153, 24, 0x06 }, \ + { 155, 165, 24, 0x05 }, \ + { 36, 64, 39, 0x1c }, \ + { 100, 138, 39, 0x1a }, \ + { 140, 165, 39, 0x18 }, \ + { 36, 64, 43, 0x5b }, \ + { 100, 138, 43, 0x3b }, \ + { 140, 165, 43, 0x1b }, \ + { 36, 64, 44, 0x40 }, \ + { 100, 138, 44, 0x20 }, \ + { 140, 165, 44, 0x10 }, \ + { 36, 64, 46, 0x00 }, \ + { 100, 138, 46, 0x18 }, \ + { 140, 165, 46, 0x08 }, \ + { 36, 64, 51, 0xfe }, \ + { 100, 124, 51, 0xfc }, \ + { 126, 165, 51, 0xec }, \ + { 36, 64, 52, 0x0c }, \ + { 100, 138, 52, 0x06 }, \ + { 140, 165, 52, 0x06 }, \ + { 36, 64, 54, 0xf8 }, \ + { 100, 165, 54, 0xeb }, \ + { 36, 50, 55, 0x06 }, \ + { 52, 64, 55, 0x04 }, \ + { 100, 138, 55, 0x01 }, \ + { 140, 165, 55, 0x00 }, \ + { 36, 50, 56, 0xd3 }, \ + { 52, 128, 56, 0xbb }, \ + { 130, 165, 56, 0xab }, \ + { 36, 64, 58, 0x15 }, \ + { 100, 116, 58, 0x1d }, \ + { 118, 165, 58, 0x15 }, \ + { 36, 64, 59, 0x7f }, \ + { 100, 138, 59, 0x3f }, \ + { 140, 165, 59, 0x7c }, \ + { 36, 64, 62, 0x15 }, \ + { 100, 116, 62, 0x1d }, \ + { 118, 165, 62, 0x15 } + +union run_stats { + uint32_t raw; + struct { + uint16_t fail; + uint16_t pad; + } error; + struct { + uint16_t success; + uint16_t retry; + } tx; +} __aligned(4); + +#endif /* _IF_RUNREG_H_ */ diff --git a/freebsd/sys/dev/usb/wlan/if_runvar.h b/freebsd/sys/dev/usb/wlan/if_runvar.h new file mode 100644 index 00000000..7209bfc7 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_runvar.h @@ -0,0 +1,269 @@ +/* $OpenBSD: if_runvar.h,v 1.3 2009/03/26 20:17:27 damien Exp $ */ + +/*- + * Copyright (c) 2008,2009 Damien Bergamini <damien.bergamini@free.fr> + * ported to FreeBSD by Akinori Furukoshi <moonlightakkiy@yahoo.ca> + * USB Consulting, Hans Petter Selasky <hselasky@freebsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ + +#ifndef _IF_RUNVAR_H_ +#define _IF_RUNVAR_H_ + +#define RUN_MAX_RXSZ \ + MIN(4096, MJUMPAGESIZE) + +/* NB: "11" is the maximum number of padding bytes needed for Tx */ +#define RUN_MAX_TXSZ \ + (sizeof (struct rt2870_txd) + \ + sizeof (struct rt2860_txwi) + \ + MCLBYTES + 11) + +#define RUN_TX_TIMEOUT 5000 /* ms */ + +/* Tx ring count was 8/endpoint, now 32 for all 4 (or 6) endpoints. */ +#define RUN_TX_RING_COUNT 32 +#define RUN_RX_RING_COUNT 1 + +#define RT2870_WCID_MAX 64 +#define RUN_AID2WCID(aid) ((aid) & 0xff) + +#define RUN_VAP_MAX 8 + +struct run_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint64_t wr_tsf; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_dbm_antsignal; + uint8_t wr_antenna; + uint8_t wr_antsignal; +} __packed __aligned(8); + +#define RUN_RX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_TSFT | \ + 1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL | \ + 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL | \ + 1 << IEEE80211_RADIOTAP_ANTENNA | \ + 1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) + +struct run_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; + uint8_t wt_hwqueue; +} __packed __aligned(8); + +#define IEEE80211_RADIOTAP_HWQUEUE 15 + +#define RUN_TX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL | \ + 1 << IEEE80211_RADIOTAP_HWQUEUE) + +struct run_softc; + +struct run_tx_data { + STAILQ_ENTRY(run_tx_data) next; + struct run_softc *sc; + struct mbuf *m; + struct ieee80211_node *ni; + uint32_t align[0]; /* dummy field */ + uint8_t desc[sizeof(struct rt2870_txd) + + sizeof(struct rt2860_txwi)]; + uint8_t ridx; +}; +STAILQ_HEAD(run_tx_data_head, run_tx_data); + +struct run_node { + struct ieee80211_node ni; + uint8_t ridx[IEEE80211_RATE_MAXSIZE]; + uint8_t ctl_ridx[IEEE80211_RATE_MAXSIZE]; + uint8_t amrr_ridx; + uint8_t mgt_ridx; + uint8_t fix_ridx; +}; +#define RUN_NODE(ni) ((struct run_node *)(ni)) + +struct run_cmdq { + void *arg0; + void *arg1; + void (*func)(void *); + struct ieee80211_key *k; + struct ieee80211_key key; + uint8_t mac[IEEE80211_ADDR_LEN]; + uint8_t wcid; +}; + +struct run_vap { + struct ieee80211vap vap; + struct mbuf *beacon_mbuf; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); + void (*recv_mgmt)(struct ieee80211_node *, + struct mbuf *, int, + const struct ieee80211_rx_stats *, + int, int); + + uint8_t rvp_id; +}; +#define RUN_VAP(vap) ((struct run_vap *)(vap)) + +/* + * There are 7 bulk endpoints: 1 for RX + * and 6 for TX (4 EDCAs + HCCA + Prio). + * Update 03-14-2009: some devices like the Planex GW-US300MiniS + * seem to have only 4 TX bulk endpoints (Fukaumi Naoki). + */ +enum { + RUN_BULK_TX_BE, /* = WME_AC_BE */ + RUN_BULK_TX_BK, /* = WME_AC_BK */ + RUN_BULK_TX_VI, /* = WME_AC_VI */ + RUN_BULK_TX_VO, /* = WME_AC_VO */ + RUN_BULK_TX_HCCA, + RUN_BULK_TX_PRIO, + RUN_BULK_RX, + RUN_N_XFER, +}; + +#define RUN_EP_QUEUES RUN_BULK_RX + +struct run_endpoint_queue { + struct run_tx_data tx_data[RUN_TX_RING_COUNT]; + struct run_tx_data_head tx_qh; + struct run_tx_data_head tx_fh; + uint32_t tx_nfree; +}; + +struct run_softc { + struct mtx sc_mtx; + struct ieee80211com sc_ic; + struct ieee80211_ratectl_tx_stats sc_txs; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + int sc_need_fwload; + + int sc_flags; +#define RUN_FLAG_FWLOAD_NEEDED 0x01 +#define RUN_RUNNING 0x02 + + uint16_t wcid_stats[RT2870_WCID_MAX + 1][3]; +#define RUN_TXCNT 0 +#define RUN_SUCCESS 1 +#define RUN_RETRY 2 + + int (*sc_srom_read)(struct run_softc *, + uint16_t, uint16_t *); + + uint16_t mac_ver; + uint16_t mac_rev; + uint16_t rf_rev; + uint8_t freq; + uint8_t ntxchains; + uint8_t nrxchains; + + uint8_t bbp25; + uint8_t bbp26; + uint8_t rf24_20mhz; + uint8_t rf24_40mhz; + uint8_t patch_dac; + uint8_t rfswitch; + uint8_t ext_2ghz_lna; + uint8_t ext_5ghz_lna; + uint8_t calib_2ghz; + uint8_t calib_5ghz; + uint8_t txmixgain_2ghz; + uint8_t txmixgain_5ghz; + int8_t txpow1[54]; + int8_t txpow2[54]; + int8_t txpow3[54]; + int8_t rssi_2ghz[3]; + int8_t rssi_5ghz[3]; + uint8_t lna[4]; + + struct { + uint8_t reg; + uint8_t val; + } bbp[10], rf[10]; + uint8_t leds; + uint16_t led[3]; + uint32_t txpow20mhz[5]; + uint32_t txpow40mhz_2ghz[5]; + uint32_t txpow40mhz_5ghz[5]; + + struct run_endpoint_queue sc_epq[RUN_EP_QUEUES]; + + struct task ratectl_task; + struct usb_callout ratectl_ch; + uint8_t ratectl_run; +#define RUN_RATECTL_OFF 0 + +/* need to be power of 2, otherwise RUN_CMDQ_GET fails */ +#define RUN_CMDQ_MAX 16 +#define RUN_CMDQ_MASQ (RUN_CMDQ_MAX - 1) + struct run_cmdq cmdq[RUN_CMDQ_MAX]; + struct task cmdq_task; + uint32_t cmdq_store; + uint8_t cmdq_exec; + uint8_t cmdq_run; + uint8_t cmdq_key_set; +#define RUN_CMDQ_ABORT 0 +#define RUN_CMDQ_GO 1 + + struct usb_xfer *sc_xfer[RUN_N_XFER]; + + struct mbuf *rx_m; + + uint8_t fifo_cnt; + + uint8_t running; + uint8_t runbmap; + uint8_t ap_running; + uint8_t adhoc_running; + uint8_t sta_running; + uint8_t rvp_cnt; + uint8_t rvp_bmap; + uint8_t sc_detached; + + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + + union { + struct run_rx_radiotap_header th; + uint8_t pad[64]; + } sc_rxtapu; +#define sc_rxtap sc_rxtapu.th + + union { + struct run_tx_radiotap_header th; + uint8_t pad[64]; + } sc_txtapu; +#define sc_txtap sc_txtapu.th +}; + +#define RUN_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RUN_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RUN_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) + +#endif /* _IF_RUNVAR_H_ */ diff --git a/freebsd/sys/dev/usb/wlan/if_uath.c b/freebsd/sys/dev/usb/wlan/if_uath.c new file mode 100644 index 00000000..c0c339e0 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_uath.c @@ -0,0 +1,2800 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2006 Sam Leffler, Errno Consulting + * Copyright (c) 2008-2009 Weongyo Jeong <weongyo@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * This driver is distantly derived from a driver of the same name + * by Damien Bergamini. The original copyright is included below: + * + * Copyright (c) 2006 + * Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/*- + * Driver for Atheros AR5523 USB parts. + * + * The driver requires firmware to be loaded into the device. This + * is done on device discovery from a user application (uathload) + * that is launched by devd when a device with suitable product ID + * is recognized. Once firmware has been loaded the device will + * reset the USB port and re-attach with the original product ID+1 + * and this driver will be attached. The firmware is licensed for + * general use (royalty free) and may be incorporated in products. + * Note that the firmware normally packaged with the NDIS drivers + * for these devices does not work in this way and so does not work + * with this driver. + */ +#include <rtems/bsd/sys/param.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/kdb.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_input.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_radiotap.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <rtems/bsd/local/usbdevs.h> + +#include <dev/usb/wlan/if_uathreg.h> +#include <dev/usb/wlan/if_uathvar.h> + +static SYSCTL_NODE(_hw_usb, OID_AUTO, uath, CTLFLAG_RW, 0, "USB Atheros"); + +static int uath_countrycode = CTRY_DEFAULT; /* country code */ +SYSCTL_INT(_hw_usb_uath, OID_AUTO, countrycode, CTLFLAG_RWTUN, &uath_countrycode, + 0, "country code"); +static int uath_regdomain = 0; /* regulatory domain */ +SYSCTL_INT(_hw_usb_uath, OID_AUTO, regdomain, CTLFLAG_RD, &uath_regdomain, + 0, "regulatory domain"); + +#ifdef UATH_DEBUG +int uath_debug = 0; +SYSCTL_INT(_hw_usb_uath, OID_AUTO, debug, CTLFLAG_RWTUN, &uath_debug, 0, + "uath debug level"); +enum { + UATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + UATH_DEBUG_XMIT_DUMP = 0x00000002, /* xmit dump */ + UATH_DEBUG_RECV = 0x00000004, /* basic recv operation */ + UATH_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */ + UATH_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */ + UATH_DEBUG_RECV_ALL = 0x00000020, /* trace all frames (beacons) */ + UATH_DEBUG_INIT = 0x00000040, /* initialization of dev */ + UATH_DEBUG_DEVCAP = 0x00000080, /* dev caps */ + UATH_DEBUG_CMDS = 0x00000100, /* commands */ + UATH_DEBUG_CMDS_DUMP = 0x00000200, /* command buffer dump */ + UATH_DEBUG_RESET = 0x00000400, /* reset processing */ + UATH_DEBUG_STATE = 0x00000800, /* 802.11 state transitions */ + UATH_DEBUG_MULTICAST = 0x00001000, /* multicast */ + UATH_DEBUG_WME = 0x00002000, /* WME */ + UATH_DEBUG_CHANNEL = 0x00004000, /* channel */ + UATH_DEBUG_RATES = 0x00008000, /* rates */ + UATH_DEBUG_CRYPTO = 0x00010000, /* crypto */ + UATH_DEBUG_LED = 0x00020000, /* LED */ + UATH_DEBUG_ANY = 0xffffffff +}; +#define DPRINTF(sc, m, fmt, ...) do { \ + if (sc->sc_debug & (m)) \ + printf(fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(sc, m, fmt, ...) do { \ + (void) sc; \ +} while (0) +#endif + +/* recognized device vendors/products */ +static const STRUCT_USB_HOST_ID uath_devs[] = { +#define UATH_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + UATH_DEV(ACCTON, SMCWUSBTG2), + UATH_DEV(ATHEROS, AR5523), + UATH_DEV(ATHEROS2, AR5523_1), + UATH_DEV(ATHEROS2, AR5523_2), + UATH_DEV(ATHEROS2, AR5523_3), + UATH_DEV(CONCEPTRONIC, AR5523_1), + UATH_DEV(CONCEPTRONIC, AR5523_2), + UATH_DEV(DLINK, DWLAG122), + UATH_DEV(DLINK, DWLAG132), + UATH_DEV(DLINK, DWLG132), + UATH_DEV(DLINK2, DWA120), + UATH_DEV(GIGASET, AR5523), + UATH_DEV(GIGASET, SMCWUSBTG), + UATH_DEV(GLOBALSUN, AR5523_1), + UATH_DEV(GLOBALSUN, AR5523_2), + UATH_DEV(NETGEAR, WG111U), + UATH_DEV(NETGEAR3, WG111T), + UATH_DEV(NETGEAR3, WPN111), + UATH_DEV(NETGEAR3, WPN111_2), + UATH_DEV(UMEDIA, TEW444UBEU), + UATH_DEV(UMEDIA, AR5523_2), + UATH_DEV(WISTRONNEWEB, AR5523_1), + UATH_DEV(WISTRONNEWEB, AR5523_2), + UATH_DEV(ZCOM, AR5523) +#undef UATH_DEV +}; + +static usb_callback_t uath_intr_rx_callback; +static usb_callback_t uath_intr_tx_callback; +static usb_callback_t uath_bulk_rx_callback; +static usb_callback_t uath_bulk_tx_callback; + +static const struct usb_config uath_usbconfig[UATH_N_XFERS] = { + [UATH_INTR_RX] = { + .type = UE_BULK, + .endpoint = 0x1, + .direction = UE_DIR_IN, + .bufsize = UATH_MAX_CMDSZ, + .flags = { + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = uath_intr_rx_callback + }, + [UATH_INTR_TX] = { + .type = UE_BULK, + .endpoint = 0x1, + .direction = UE_DIR_OUT, + .bufsize = UATH_MAX_CMDSZ * UATH_CMD_LIST_COUNT, + .flags = { + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = uath_intr_tx_callback, + .timeout = UATH_CMD_TIMEOUT + }, + [UATH_BULK_RX] = { + .type = UE_BULK, + .endpoint = 0x2, + .direction = UE_DIR_IN, + .bufsize = MCLBYTES, + .flags = { + .ext_buffer = 1, + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = uath_bulk_rx_callback + }, + [UATH_BULK_TX] = { + .type = UE_BULK, + .endpoint = 0x2, + .direction = UE_DIR_OUT, + .bufsize = UATH_MAX_TXBUFSZ * UATH_TX_DATA_LIST_COUNT, + .flags = { + .force_short_xfer = 1, + .pipe_bof = 1 + }, + .callback = uath_bulk_tx_callback, + .timeout = UATH_DATA_TIMEOUT + } +}; + +static struct ieee80211vap *uath_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, int, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void uath_vap_delete(struct ieee80211vap *); +static int uath_alloc_cmd_list(struct uath_softc *, struct uath_cmd []); +static void uath_free_cmd_list(struct uath_softc *, struct uath_cmd []); +static int uath_host_available(struct uath_softc *); +static int uath_get_capability(struct uath_softc *, uint32_t, uint32_t *); +static int uath_get_devcap(struct uath_softc *); +static struct uath_cmd * + uath_get_cmdbuf(struct uath_softc *); +static int uath_cmd_read(struct uath_softc *, uint32_t, const void *, + int, void *, int, int); +static int uath_cmd_write(struct uath_softc *, uint32_t, const void *, + int, int); +static void uath_stat(void *); +#ifdef UATH_DEBUG +static void uath_dump_cmd(const uint8_t *, int, char); +static const char * + uath_codename(int); +#endif +static int uath_get_devstatus(struct uath_softc *, + uint8_t macaddr[IEEE80211_ADDR_LEN]); +static int uath_get_status(struct uath_softc *, uint32_t, void *, int); +static int uath_alloc_rx_data_list(struct uath_softc *); +static int uath_alloc_tx_data_list(struct uath_softc *); +static void uath_free_rx_data_list(struct uath_softc *); +static void uath_free_tx_data_list(struct uath_softc *); +static int uath_init(struct uath_softc *); +static void uath_stop(struct uath_softc *); +static void uath_parent(struct ieee80211com *); +static int uath_transmit(struct ieee80211com *, struct mbuf *); +static void uath_start(struct uath_softc *); +static int uath_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void uath_scan_start(struct ieee80211com *); +static void uath_scan_end(struct ieee80211com *); +static void uath_set_channel(struct ieee80211com *); +static void uath_update_mcast(struct ieee80211com *); +static void uath_update_promisc(struct ieee80211com *); +static int uath_config(struct uath_softc *, uint32_t, uint32_t); +static int uath_config_multi(struct uath_softc *, uint32_t, const void *, + int); +static int uath_switch_channel(struct uath_softc *, + struct ieee80211_channel *); +static int uath_set_rxfilter(struct uath_softc *, uint32_t, uint32_t); +static void uath_watchdog(void *); +static void uath_abort_xfers(struct uath_softc *); +static int uath_dataflush(struct uath_softc *); +static int uath_cmdflush(struct uath_softc *); +static int uath_flush(struct uath_softc *); +static int uath_set_ledstate(struct uath_softc *, int); +static int uath_set_chan(struct uath_softc *, struct ieee80211_channel *); +static int uath_reset_tx_queues(struct uath_softc *); +static int uath_wme_init(struct uath_softc *); +static struct uath_data * + uath_getbuf(struct uath_softc *); +static int uath_newstate(struct ieee80211vap *, enum ieee80211_state, + int); +static int uath_set_key(struct uath_softc *, + const struct ieee80211_key *, int); +static int uath_set_keys(struct uath_softc *, struct ieee80211vap *); +static void uath_sysctl_node(struct uath_softc *); + +static int +uath_match(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != UATH_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != UATH_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(uath_devs, sizeof(uath_devs), uaa)); +} + +static int +uath_attach(device_t dev) +{ + struct uath_softc *sc = device_get_softc(dev); + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct ieee80211com *ic = &sc->sc_ic; + uint8_t bands[IEEE80211_MODE_BYTES]; + uint8_t iface_index = UATH_IFACE_INDEX; /* XXX */ + usb_error_t error; + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; +#ifdef UATH_DEBUG + sc->sc_debug = uath_debug; +#endif + device_set_usb_desc(dev); + + /* + * Only post-firmware devices here. + */ + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init(&sc->stat_ch, 0); + callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + uath_usbconfig, UATH_N_XFERS, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto fail; + } + + sc->sc_cmd_dma_buf = + usbd_xfer_get_frame_buffer(sc->sc_xfer[UATH_INTR_TX], 0); + sc->sc_tx_dma_buf = + usbd_xfer_get_frame_buffer(sc->sc_xfer[UATH_BULK_TX], 0); + + /* + * Setup buffers for firmware commands. + */ + error = uath_alloc_cmd_list(sc, sc->sc_cmd); + if (error != 0) { + device_printf(sc->sc_dev, + "could not allocate Tx command list\n"); + goto fail1; + } + + /* + * We're now ready to send+receive firmware commands. + */ + UATH_LOCK(sc); + error = uath_host_available(sc); + if (error != 0) { + device_printf(sc->sc_dev, "could not initialize adapter\n"); + goto fail2; + } + error = uath_get_devcap(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "could not get device capabilities\n"); + goto fail2; + } + UATH_UNLOCK(sc); + + /* Create device sysctl node. */ + uath_sysctl_node(sc); + + UATH_LOCK(sc); + error = uath_get_devstatus(sc, ic->ic_macaddr); + if (error != 0) { + device_printf(sc->sc_dev, "could not get device status\n"); + goto fail2; + } + + /* + * Allocate xfers for Rx/Tx data pipes. + */ + error = uath_alloc_rx_data_list(sc); + if (error != 0) { + device_printf(sc->sc_dev, "could not allocate Rx data list\n"); + goto fail2; + } + error = uath_alloc_tx_data_list(sc); + if (error != 0) { + device_printf(sc->sc_dev, "could not allocate Tx data list\n"); + goto fail2; + } + UATH_UNLOCK(sc); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(dev); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA | /* station mode */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_TXPMGT | /* tx power management */ + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_WPA | /* 802.11i */ + IEEE80211_C_BGSCAN | /* capable of bg scanning */ + IEEE80211_C_TXFRAG; /* handle tx frags */ + + /* put a regulatory domain to reveal informations. */ + uath_regdomain = sc->sc_devcap.regDomain; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + if ((sc->sc_devcap.analog5GhzRevision & 0xf0) == 0x30) + setbit(bands, IEEE80211_MODE_11A); + /* XXX turbo */ + ieee80211_init_channels(ic, NULL, bands); + + ieee80211_ifattach(ic); + ic->ic_raw_xmit = uath_raw_xmit; + ic->ic_scan_start = uath_scan_start; + ic->ic_scan_end = uath_scan_end; + ic->ic_set_channel = uath_set_channel; + ic->ic_vap_create = uath_vap_create; + ic->ic_vap_delete = uath_vap_delete; + ic->ic_update_mcast = uath_update_mcast; + ic->ic_update_promisc = uath_update_promisc; + ic->ic_transmit = uath_transmit; + ic->ic_parent = uath_parent; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + UATH_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + UATH_RX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +fail2: UATH_UNLOCK(sc); + uath_free_cmd_list(sc, sc->sc_cmd); +fail1: usbd_transfer_unsetup(sc->sc_xfer, UATH_N_XFERS); +fail: + return (error); +} + +static int +uath_detach(device_t dev) +{ + struct uath_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + unsigned int x; + + /* + * Prevent further allocations from RX/TX/CMD + * data lists and ioctls + */ + UATH_LOCK(sc); + sc->sc_flags |= UATH_FLAG_INVALID; + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + + STAILQ_INIT(&sc->sc_tx_active); + STAILQ_INIT(&sc->sc_tx_inactive); + STAILQ_INIT(&sc->sc_tx_pending); + + STAILQ_INIT(&sc->sc_cmd_active); + STAILQ_INIT(&sc->sc_cmd_pending); + STAILQ_INIT(&sc->sc_cmd_waiting); + STAILQ_INIT(&sc->sc_cmd_inactive); + + uath_stop(sc); + UATH_UNLOCK(sc); + + callout_drain(&sc->stat_ch); + callout_drain(&sc->watchdog_ch); + + /* drain USB transfers */ + for (x = 0; x != UATH_N_XFERS; x++) + usbd_transfer_drain(sc->sc_xfer[x]); + + /* free data buffers */ + UATH_LOCK(sc); + uath_free_rx_data_list(sc); + uath_free_tx_data_list(sc); + uath_free_cmd_list(sc, sc->sc_cmd); + UATH_UNLOCK(sc); + + /* free USB transfers and some data buffers */ + usbd_transfer_unsetup(sc->sc_xfer, UATH_N_XFERS); + + ieee80211_ifdetach(ic); + mbufq_drain(&sc->sc_snd); + mtx_destroy(&sc->sc_mtx); + return (0); +} + +static void +uath_free_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[]) +{ + int i; + + for (i = 0; i != UATH_CMD_LIST_COUNT; i++) + cmds[i].buf = NULL; +} + +static int +uath_alloc_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[]) +{ + int i; + + STAILQ_INIT(&sc->sc_cmd_active); + STAILQ_INIT(&sc->sc_cmd_pending); + STAILQ_INIT(&sc->sc_cmd_waiting); + STAILQ_INIT(&sc->sc_cmd_inactive); + + for (i = 0; i != UATH_CMD_LIST_COUNT; i++) { + struct uath_cmd *cmd = &cmds[i]; + + cmd->sc = sc; /* backpointer for callbacks */ + cmd->msgid = i; + cmd->buf = ((uint8_t *)sc->sc_cmd_dma_buf) + + (i * UATH_MAX_CMDSZ); + STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next); + UATH_STAT_INC(sc, st_cmd_inactive); + } + return (0); +} + +static int +uath_host_available(struct uath_softc *sc) +{ + struct uath_cmd_host_available setup; + + UATH_ASSERT_LOCKED(sc); + + /* inform target the host is available */ + setup.sw_ver_major = htobe32(ATH_SW_VER_MAJOR); + setup.sw_ver_minor = htobe32(ATH_SW_VER_MINOR); + setup.sw_ver_patch = htobe32(ATH_SW_VER_PATCH); + setup.sw_ver_build = htobe32(ATH_SW_VER_BUILD); + return uath_cmd_read(sc, WDCMSG_HOST_AVAILABLE, + &setup, sizeof setup, NULL, 0, 0); +} + +#ifdef UATH_DEBUG +static void +uath_dump_cmd(const uint8_t *buf, int len, char prefix) +{ + const char *sep = ""; + int i; + + for (i = 0; i < len; i++) { + if ((i % 16) == 0) { + printf("%s%c ", sep, prefix); + sep = "\n"; + } + else if ((i % 4) == 0) + printf(" "); + printf("%02x", buf[i]); + } + printf("\n"); +} + +static const char * +uath_codename(int code) +{ + static const char *names[] = { + "0x00", + "HOST_AVAILABLE", + "BIND", + "TARGET_RESET", + "TARGET_GET_CAPABILITY", + "TARGET_SET_CONFIG", + "TARGET_GET_STATUS", + "TARGET_GET_STATS", + "TARGET_START", + "TARGET_STOP", + "TARGET_ENABLE", + "TARGET_DISABLE", + "CREATE_CONNECTION", + "UPDATE_CONNECT_ATTR", + "DELETE_CONNECT", + "SEND", + "FLUSH", + "STATS_UPDATE", + "BMISS", + "DEVICE_AVAIL", + "SEND_COMPLETE", + "DATA_AVAIL", + "SET_PWR_MODE", + "BMISS_ACK", + "SET_LED_STEADY", + "SET_LED_BLINK", + "SETUP_BEACON_DESC", + "BEACON_INIT", + "RESET_KEY_CACHE", + "RESET_KEY_CACHE_ENTRY", + "SET_KEY_CACHE_ENTRY", + "SET_DECOMP_MASK", + "SET_REGULATORY_DOMAIN", + "SET_LED_STATE", + "WRITE_ASSOCID", + "SET_STA_BEACON_TIMERS", + "GET_TSF", + "RESET_TSF", + "SET_ADHOC_MODE", + "SET_BASIC_RATE", + "MIB_CONTROL", + "GET_CHANNEL_DATA", + "GET_CUR_RSSI", + "SET_ANTENNA_SWITCH", + "0x2c", "0x2d", "0x2e", + "USE_SHORT_SLOT_TIME", + "SET_POWER_MODE", + "SETUP_PSPOLL_DESC", + "SET_RX_MULTICAST_FILTER", + "RX_FILTER", + "PER_CALIBRATION", + "RESET", + "DISABLE", + "PHY_DISABLE", + "SET_TX_POWER_LIMIT", + "SET_TX_QUEUE_PARAMS", + "SETUP_TX_QUEUE", + "RELEASE_TX_QUEUE", + }; + static char buf[8]; + + if (code < nitems(names)) + return names[code]; + if (code == WDCMSG_SET_DEFAULT_KEY) + return "SET_DEFAULT_KEY"; + snprintf(buf, sizeof(buf), "0x%02x", code); + return buf; +} +#endif + +/* + * Low-level function to send read or write commands to the firmware. + */ +static int +uath_cmdsend(struct uath_softc *sc, uint32_t code, const void *idata, int ilen, + void *odata, int olen, int flags) +{ + struct uath_cmd_hdr *hdr; + struct uath_cmd *cmd; + int error; + + UATH_ASSERT_LOCKED(sc); + + /* grab a xfer */ + cmd = uath_get_cmdbuf(sc); + if (cmd == NULL) { + device_printf(sc->sc_dev, "%s: empty inactive queue\n", + __func__); + return (ENOBUFS); + } + cmd->flags = flags; + /* always bulk-out a multiple of 4 bytes */ + cmd->buflen = roundup2(sizeof(struct uath_cmd_hdr) + ilen, 4); + + hdr = (struct uath_cmd_hdr *)cmd->buf; + memset(hdr, 0, sizeof(struct uath_cmd_hdr)); + hdr->len = htobe32(cmd->buflen); + hdr->code = htobe32(code); + hdr->msgid = cmd->msgid; /* don't care about endianness */ + hdr->magic = htobe32((cmd->flags & UATH_CMD_FLAG_MAGIC) ? 1 << 24 : 0); + memcpy((uint8_t *)(hdr + 1), idata, ilen); + +#ifdef UATH_DEBUG + if (sc->sc_debug & UATH_DEBUG_CMDS) { + printf("%s: send %s [flags 0x%x] olen %d\n", + __func__, uath_codename(code), cmd->flags, olen); + if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) + uath_dump_cmd(cmd->buf, cmd->buflen, '+'); + } +#endif + cmd->odata = odata; + KASSERT(odata == NULL || + olen < UATH_MAX_CMDSZ - sizeof(*hdr) + sizeof(uint32_t), + ("odata %p olen %u", odata, olen)); + cmd->olen = olen; + + STAILQ_INSERT_TAIL(&sc->sc_cmd_pending, cmd, next); + UATH_STAT_INC(sc, st_cmd_pending); + usbd_transfer_start(sc->sc_xfer[UATH_INTR_TX]); + + if (cmd->flags & UATH_CMD_FLAG_READ) { + usbd_transfer_start(sc->sc_xfer[UATH_INTR_RX]); + + /* wait at most two seconds for command reply */ + error = mtx_sleep(cmd, &sc->sc_mtx, 0, "uathcmd", 2 * hz); + cmd->odata = NULL; /* in case reply comes too late */ + if (error != 0) { + device_printf(sc->sc_dev, "timeout waiting for reply " + "to cmd 0x%x (%u)\n", code, code); + } else if (cmd->olen != olen) { + device_printf(sc->sc_dev, "unexpected reply data count " + "to cmd 0x%x (%u), got %u, expected %u\n", + code, code, cmd->olen, olen); + error = EINVAL; + } + return (error); + } + return (0); +} + +static int +uath_cmd_read(struct uath_softc *sc, uint32_t code, const void *idata, + int ilen, void *odata, int olen, int flags) +{ + + flags |= UATH_CMD_FLAG_READ; + return uath_cmdsend(sc, code, idata, ilen, odata, olen, flags); +} + +static int +uath_cmd_write(struct uath_softc *sc, uint32_t code, const void *data, int len, + int flags) +{ + + flags &= ~UATH_CMD_FLAG_READ; + return uath_cmdsend(sc, code, data, len, NULL, 0, flags); +} + +static struct uath_cmd * +uath_get_cmdbuf(struct uath_softc *sc) +{ + struct uath_cmd *uc; + + UATH_ASSERT_LOCKED(sc); + + uc = STAILQ_FIRST(&sc->sc_cmd_inactive); + if (uc != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_cmd_inactive, next); + UATH_STAT_DEC(sc, st_cmd_inactive); + } else + uc = NULL; + if (uc == NULL) + DPRINTF(sc, UATH_DEBUG_XMIT, "%s: %s\n", __func__, + "out of command xmit buffers"); + return (uc); +} + +/* + * This function is called periodically (every second) when associated to + * query device statistics. + */ +static void +uath_stat(void *arg) +{ + struct uath_softc *sc = arg; + int error; + + UATH_LOCK(sc); + /* + * Send request for statistics asynchronously. The timer will be + * restarted when we'll get the stats notification. + */ + error = uath_cmd_write(sc, WDCMSG_TARGET_GET_STATS, NULL, 0, + UATH_CMD_FLAG_ASYNC); + if (error != 0) { + device_printf(sc->sc_dev, + "could not query stats, error %d\n", error); + } + UATH_UNLOCK(sc); +} + +static int +uath_get_capability(struct uath_softc *sc, uint32_t cap, uint32_t *val) +{ + int error; + + cap = htobe32(cap); + error = uath_cmd_read(sc, WDCMSG_TARGET_GET_CAPABILITY, + &cap, sizeof cap, val, sizeof(uint32_t), UATH_CMD_FLAG_MAGIC); + if (error != 0) { + device_printf(sc->sc_dev, "could not read capability %u\n", + be32toh(cap)); + return (error); + } + *val = be32toh(*val); + return (error); +} + +static int +uath_get_devcap(struct uath_softc *sc) +{ +#define GETCAP(x, v) do { \ + error = uath_get_capability(sc, x, &v); \ + if (error != 0) \ + return (error); \ + DPRINTF(sc, UATH_DEBUG_DEVCAP, \ + "%s: %s=0x%08x\n", __func__, #x, v); \ +} while (0) + struct uath_devcap *cap = &sc->sc_devcap; + int error; + + /* collect device capabilities */ + GETCAP(CAP_TARGET_VERSION, cap->targetVersion); + GETCAP(CAP_TARGET_REVISION, cap->targetRevision); + GETCAP(CAP_MAC_VERSION, cap->macVersion); + GETCAP(CAP_MAC_REVISION, cap->macRevision); + GETCAP(CAP_PHY_REVISION, cap->phyRevision); + GETCAP(CAP_ANALOG_5GHz_REVISION, cap->analog5GhzRevision); + GETCAP(CAP_ANALOG_2GHz_REVISION, cap->analog2GhzRevision); + + GETCAP(CAP_REG_DOMAIN, cap->regDomain); + GETCAP(CAP_REG_CAP_BITS, cap->regCapBits); +#if 0 + /* NB: not supported in rev 1.5 */ + GETCAP(CAP_COUNTRY_CODE, cap->countryCode); +#endif + GETCAP(CAP_WIRELESS_MODES, cap->wirelessModes); + GETCAP(CAP_CHAN_SPREAD_SUPPORT, cap->chanSpreadSupport); + GETCAP(CAP_COMPRESS_SUPPORT, cap->compressSupport); + GETCAP(CAP_BURST_SUPPORT, cap->burstSupport); + GETCAP(CAP_FAST_FRAMES_SUPPORT, cap->fastFramesSupport); + GETCAP(CAP_CHAP_TUNING_SUPPORT, cap->chapTuningSupport); + GETCAP(CAP_TURBOG_SUPPORT, cap->turboGSupport); + GETCAP(CAP_TURBO_PRIME_SUPPORT, cap->turboPrimeSupport); + GETCAP(CAP_DEVICE_TYPE, cap->deviceType); + GETCAP(CAP_WME_SUPPORT, cap->wmeSupport); + GETCAP(CAP_TOTAL_QUEUES, cap->numTxQueues); + GETCAP(CAP_CONNECTION_ID_MAX, cap->connectionIdMax); + + GETCAP(CAP_LOW_5GHZ_CHAN, cap->low5GhzChan); + GETCAP(CAP_HIGH_5GHZ_CHAN, cap->high5GhzChan); + GETCAP(CAP_LOW_2GHZ_CHAN, cap->low2GhzChan); + GETCAP(CAP_HIGH_2GHZ_CHAN, cap->high2GhzChan); + GETCAP(CAP_TWICE_ANTENNAGAIN_5G, cap->twiceAntennaGain5G); + GETCAP(CAP_TWICE_ANTENNAGAIN_2G, cap->twiceAntennaGain2G); + + GETCAP(CAP_CIPHER_AES_CCM, cap->supportCipherAES_CCM); + GETCAP(CAP_CIPHER_TKIP, cap->supportCipherTKIP); + GETCAP(CAP_MIC_TKIP, cap->supportMicTKIP); + + cap->supportCipherWEP = 1; /* NB: always available */ + + return (0); +} + +static int +uath_get_devstatus(struct uath_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + int error; + + /* retrieve MAC address */ + error = uath_get_status(sc, ST_MAC_ADDR, macaddr, IEEE80211_ADDR_LEN); + if (error != 0) { + device_printf(sc->sc_dev, "could not read MAC address\n"); + return (error); + } + + error = uath_get_status(sc, ST_SERIAL_NUMBER, + &sc->sc_serial[0], sizeof(sc->sc_serial)); + if (error != 0) { + device_printf(sc->sc_dev, + "could not read device serial number\n"); + return (error); + } + return (0); +} + +static int +uath_get_status(struct uath_softc *sc, uint32_t which, void *odata, int olen) +{ + int error; + + which = htobe32(which); + error = uath_cmd_read(sc, WDCMSG_TARGET_GET_STATUS, + &which, sizeof(which), odata, olen, UATH_CMD_FLAG_MAGIC); + if (error != 0) + device_printf(sc->sc_dev, + "could not read EEPROM offset 0x%02x\n", be32toh(which)); + return (error); +} + +static void +uath_free_data_list(struct uath_softc *sc, struct uath_data data[], int ndata, + int fillmbuf) +{ + int i; + + for (i = 0; i < ndata; i++) { + struct uath_data *dp = &data[i]; + + if (fillmbuf == 1) { + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + dp->buf = NULL; + } + } else { + dp->buf = NULL; + } + if (dp->ni != NULL) { + ieee80211_free_node(dp->ni); + dp->ni = NULL; + } + } +} + +static int +uath_alloc_data_list(struct uath_softc *sc, struct uath_data data[], + int ndata, int maxsz, void *dma_buf) +{ + int i, error; + + for (i = 0; i < ndata; i++) { + struct uath_data *dp = &data[i]; + + dp->sc = sc; + if (dma_buf == NULL) { + /* XXX check maxsz */ + dp->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (dp->m == NULL) { + device_printf(sc->sc_dev, + "could not allocate rx mbuf\n"); + error = ENOMEM; + goto fail; + } + dp->buf = mtod(dp->m, uint8_t *); + } else { + dp->m = NULL; + dp->buf = ((uint8_t *)dma_buf) + (i * maxsz); + } + dp->ni = NULL; + } + + return (0); + +fail: uath_free_data_list(sc, data, ndata, 1 /* free mbufs */); + return (error); +} + +static int +uath_alloc_rx_data_list(struct uath_softc *sc) +{ + int error, i; + + /* XXX is it enough to store the RX packet with MCLBYTES bytes? */ + error = uath_alloc_data_list(sc, + sc->sc_rx, UATH_RX_DATA_LIST_COUNT, MCLBYTES, + NULL /* setup mbufs */); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + + for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) { + STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], + next); + UATH_STAT_INC(sc, st_rx_inactive); + } + + return (0); +} + +static int +uath_alloc_tx_data_list(struct uath_softc *sc) +{ + int error, i; + + error = uath_alloc_data_list(sc, + sc->sc_tx, UATH_TX_DATA_LIST_COUNT, UATH_MAX_TXBUFSZ, + sc->sc_tx_dma_buf); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_tx_active); + STAILQ_INIT(&sc->sc_tx_inactive); + STAILQ_INIT(&sc->sc_tx_pending); + + for (i = 0; i < UATH_TX_DATA_LIST_COUNT; i++) { + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], + next); + UATH_STAT_INC(sc, st_tx_inactive); + } + + return (0); +} + +static void +uath_free_rx_data_list(struct uath_softc *sc) +{ + uath_free_data_list(sc, sc->sc_rx, UATH_RX_DATA_LIST_COUNT, + 1 /* free mbufs */); +} + +static void +uath_free_tx_data_list(struct uath_softc *sc) +{ + uath_free_data_list(sc, sc->sc_tx, UATH_TX_DATA_LIST_COUNT, + 0 /* no mbufs */); +} + +static struct ieee80211vap * +uath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct uath_vap *uvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return (NULL); + uvp = malloc(sizeof(struct uath_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &uvp->vap; + /* enable s/w bmiss handling for sta mode */ + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(uvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + vap->iv_newstate = uath_newstate; + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return (vap); +} + +static void +uath_vap_delete(struct ieee80211vap *vap) +{ + struct uath_vap *uvp = UATH_VAP(vap); + + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static int +uath_init(struct uath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t val; + int error; + + UATH_ASSERT_LOCKED(sc); + + if (sc->sc_flags & UATH_FLAG_INITDONE) + uath_stop(sc); + + /* reset variables */ + sc->sc_intrx_nextnum = sc->sc_msgid = 0; + + val = htobe32(0); + uath_cmd_write(sc, WDCMSG_BIND, &val, sizeof val, 0); + + /* set MAC address */ + uath_config_multi(sc, CFG_MAC_ADDR, + vap ? vap->iv_myaddr : ic->ic_macaddr, IEEE80211_ADDR_LEN); + + /* XXX honor net80211 state */ + uath_config(sc, CFG_RATE_CONTROL_ENABLE, 0x00000001); + uath_config(sc, CFG_DIVERSITY_CTL, 0x00000001); + uath_config(sc, CFG_ABOLT, 0x0000003f); + uath_config(sc, CFG_WME_ENABLED, 0x00000001); + + uath_config(sc, CFG_SERVICE_TYPE, 1); + uath_config(sc, CFG_TP_SCALE, 0x00000000); + uath_config(sc, CFG_TPC_HALF_DBM5, 0x0000003c); + uath_config(sc, CFG_TPC_HALF_DBM2, 0x0000003c); + uath_config(sc, CFG_OVERRD_TX_POWER, 0x00000000); + uath_config(sc, CFG_GMODE_PROTECTION, 0x00000000); + uath_config(sc, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003); + uath_config(sc, CFG_PROTECTION_TYPE, 0x00000000); + uath_config(sc, CFG_MODE_CTS, 0x00000002); + + error = uath_cmd_read(sc, WDCMSG_TARGET_START, NULL, 0, + &val, sizeof(val), UATH_CMD_FLAG_MAGIC); + if (error) { + device_printf(sc->sc_dev, + "could not start target, error %d\n", error); + goto fail; + } + DPRINTF(sc, UATH_DEBUG_INIT, "%s returns handle: 0x%x\n", + uath_codename(WDCMSG_TARGET_START), be32toh(val)); + + /* set default channel */ + error = uath_switch_channel(sc, ic->ic_curchan); + if (error) { + device_printf(sc->sc_dev, + "could not switch channel, error %d\n", error); + goto fail; + } + + val = htobe32(TARGET_DEVICE_AWAKE); + uath_cmd_write(sc, WDCMSG_SET_PWR_MODE, &val, sizeof val, 0); + /* XXX? check */ + uath_cmd_write(sc, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0); + + usbd_transfer_start(sc->sc_xfer[UATH_BULK_RX]); + /* enable Rx */ + uath_set_rxfilter(sc, 0x0, UATH_FILTER_OP_INIT); + uath_set_rxfilter(sc, + UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST | + UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON, + UATH_FILTER_OP_SET); + + sc->sc_flags |= UATH_FLAG_INITDONE; + + callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc); + + return (0); + +fail: + uath_stop(sc); + return (error); +} + +static void +uath_stop(struct uath_softc *sc) +{ + + UATH_ASSERT_LOCKED(sc); + + sc->sc_flags &= ~UATH_FLAG_INITDONE; + + callout_stop(&sc->stat_ch); + callout_stop(&sc->watchdog_ch); + sc->sc_tx_timer = 0; + /* abort pending transmits */ + uath_abort_xfers(sc); + /* flush data & control requests into the target */ + (void)uath_flush(sc); + /* set a LED status to the disconnected. */ + uath_set_ledstate(sc, 0); + /* stop the target */ + uath_cmd_write(sc, WDCMSG_TARGET_STOP, NULL, 0, 0); +} + +static int +uath_config(struct uath_softc *sc, uint32_t reg, uint32_t val) +{ + struct uath_write_mac write; + int error; + + write.reg = htobe32(reg); + write.len = htobe32(0); /* 0 = single write */ + *(uint32_t *)write.data = htobe32(val); + + error = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write, + 3 * sizeof (uint32_t), 0); + if (error != 0) { + device_printf(sc->sc_dev, "could not write register 0x%02x\n", + reg); + } + return (error); +} + +static int +uath_config_multi(struct uath_softc *sc, uint32_t reg, const void *data, + int len) +{ + struct uath_write_mac write; + int error; + + write.reg = htobe32(reg); + write.len = htobe32(len); + bcopy(data, write.data, len); + + /* properly handle the case where len is zero (reset) */ + error = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write, + (len == 0) ? sizeof (uint32_t) : 2 * sizeof (uint32_t) + len, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "could not write %d bytes to register 0x%02x\n", len, reg); + } + return (error); +} + +static int +uath_switch_channel(struct uath_softc *sc, struct ieee80211_channel *c) +{ + int error; + + UATH_ASSERT_LOCKED(sc); + + /* set radio frequency */ + error = uath_set_chan(sc, c); + if (error) { + device_printf(sc->sc_dev, + "could not set channel, error %d\n", error); + goto failed; + } + /* reset Tx rings */ + error = uath_reset_tx_queues(sc); + if (error) { + device_printf(sc->sc_dev, + "could not reset Tx queues, error %d\n", error); + goto failed; + } + /* set Tx rings WME properties */ + error = uath_wme_init(sc); + if (error) { + device_printf(sc->sc_dev, + "could not init Tx queues, error %d\n", error); + goto failed; + } + error = uath_set_ledstate(sc, 0); + if (error) { + device_printf(sc->sc_dev, + "could not set led state, error %d\n", error); + goto failed; + } + error = uath_flush(sc); + if (error) { + device_printf(sc->sc_dev, + "could not flush pipes, error %d\n", error); + goto failed; + } +failed: + return (error); +} + +static int +uath_set_rxfilter(struct uath_softc *sc, uint32_t bits, uint32_t op) +{ + struct uath_cmd_rx_filter rxfilter; + + rxfilter.bits = htobe32(bits); + rxfilter.op = htobe32(op); + + DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, + "setting Rx filter=0x%x flags=0x%x\n", bits, op); + return uath_cmd_write(sc, WDCMSG_RX_FILTER, &rxfilter, + sizeof rxfilter, 0); +} + +static void +uath_watchdog(void *arg) +{ + struct uath_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + device_printf(sc->sc_dev, "device timeout\n"); + /*uath_init(sc); XXX needs a process context! */ + counter_u64_add(ic->ic_oerrors, 1); + return; + } + callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc); + } +} + +static void +uath_abort_xfers(struct uath_softc *sc) +{ + int i; + + UATH_ASSERT_LOCKED(sc); + /* abort any pending transfers */ + for (i = 0; i < UATH_N_XFERS; i++) + usbd_transfer_stop(sc->sc_xfer[i]); +} + +static int +uath_flush(struct uath_softc *sc) +{ + int error; + + error = uath_dataflush(sc); + if (error != 0) + goto failed; + + error = uath_cmdflush(sc); + if (error != 0) + goto failed; + +failed: + return (error); +} + +static int +uath_cmdflush(struct uath_softc *sc) +{ + + return uath_cmd_write(sc, WDCMSG_FLUSH, NULL, 0, 0); +} + +static int +uath_dataflush(struct uath_softc *sc) +{ + struct uath_data *data; + struct uath_chunk *chunk; + struct uath_tx_desc *desc; + + UATH_ASSERT_LOCKED(sc); + + data = uath_getbuf(sc); + if (data == NULL) + return (ENOBUFS); + data->buflen = sizeof(struct uath_chunk) + sizeof(struct uath_tx_desc); + data->m = NULL; + data->ni = NULL; + chunk = (struct uath_chunk *)data->buf; + desc = (struct uath_tx_desc *)(chunk + 1); + + /* one chunk only */ + chunk->seqnum = 0; + chunk->flags = UATH_CFLAGS_FINAL; + chunk->length = htobe16(sizeof (struct uath_tx_desc)); + + memset(desc, 0, sizeof(struct uath_tx_desc)); + desc->msglen = htobe32(sizeof(struct uath_tx_desc)); + desc->msgid = (sc->sc_msgid++) + 1; /* don't care about endianness */ + desc->type = htobe32(WDCMSG_FLUSH); + desc->txqid = htobe32(0); + desc->connid = htobe32(0); + desc->flags = htobe32(0); + +#ifdef UATH_DEBUG + if (sc->sc_debug & UATH_DEBUG_CMDS) { + DPRINTF(sc, UATH_DEBUG_RESET, "send flush ix %d\n", + desc->msgid); + if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) + uath_dump_cmd(data->buf, data->buflen, '+'); + } +#endif + + STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); + UATH_STAT_INC(sc, st_tx_pending); + sc->sc_tx_timer = 5; + usbd_transfer_start(sc->sc_xfer[UATH_BULK_TX]); + + return (0); +} + +static struct uath_data * +_uath_getbuf(struct uath_softc *sc) +{ + struct uath_data *bf; + + bf = STAILQ_FIRST(&sc->sc_tx_inactive); + if (bf != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); + UATH_STAT_DEC(sc, st_tx_inactive); + } else + bf = NULL; + if (bf == NULL) + DPRINTF(sc, UATH_DEBUG_XMIT, "%s: %s\n", __func__, + "out of xmit buffers"); + return (bf); +} + +static struct uath_data * +uath_getbuf(struct uath_softc *sc) +{ + struct uath_data *bf; + + UATH_ASSERT_LOCKED(sc); + + bf = _uath_getbuf(sc); + if (bf == NULL) + DPRINTF(sc, UATH_DEBUG_XMIT, "%s: stop queue\n", __func__); + return (bf); +} + +static int +uath_set_ledstate(struct uath_softc *sc, int connected) +{ + + DPRINTF(sc, UATH_DEBUG_LED, + "set led state %sconnected\n", connected ? "" : "!"); + connected = htobe32(connected); + return uath_cmd_write(sc, WDCMSG_SET_LED_STATE, + &connected, sizeof connected, 0); +} + +static int +uath_set_chan(struct uath_softc *sc, struct ieee80211_channel *c) +{ +#ifdef UATH_DEBUG + struct ieee80211com *ic = &sc->sc_ic; +#endif + struct uath_cmd_reset reset; + + memset(&reset, 0, sizeof(reset)); + if (IEEE80211_IS_CHAN_2GHZ(c)) + reset.flags |= htobe32(UATH_CHAN_2GHZ); + if (IEEE80211_IS_CHAN_5GHZ(c)) + reset.flags |= htobe32(UATH_CHAN_5GHZ); + /* NB: 11g =>'s 11b so don't specify both OFDM and CCK */ + if (IEEE80211_IS_CHAN_OFDM(c)) + reset.flags |= htobe32(UATH_CHAN_OFDM); + else if (IEEE80211_IS_CHAN_CCK(c)) + reset.flags |= htobe32(UATH_CHAN_CCK); + /* turbo can be used in either 2GHz or 5GHz */ + if (c->ic_flags & IEEE80211_CHAN_TURBO) + reset.flags |= htobe32(UATH_CHAN_TURBO); + reset.freq = htobe32(c->ic_freq); + reset.maxrdpower = htobe32(50); /* XXX */ + reset.channelchange = htobe32(1); + reset.keeprccontent = htobe32(0); + + DPRINTF(sc, UATH_DEBUG_CHANNEL, "set channel %d, flags 0x%x freq %u\n", + ieee80211_chan2ieee(ic, c), + be32toh(reset.flags), be32toh(reset.freq)); + return uath_cmd_write(sc, WDCMSG_RESET, &reset, sizeof reset, 0); +} + +static int +uath_reset_tx_queues(struct uath_softc *sc) +{ + int ac, error; + + DPRINTF(sc, UATH_DEBUG_RESET, "%s: reset Tx queues\n", __func__); + for (ac = 0; ac < 4; ac++) { + const uint32_t qid = htobe32(ac); + + error = uath_cmd_write(sc, WDCMSG_RELEASE_TX_QUEUE, &qid, + sizeof qid, 0); + if (error != 0) + break; + } + return (error); +} + +static int +uath_wme_init(struct uath_softc *sc) +{ + /* XXX get from net80211 */ + static const struct uath_wme_settings uath_wme_11g[4] = { + { 7, 4, 10, 0, 0 }, /* Background */ + { 3, 4, 10, 0, 0 }, /* Best-Effort */ + { 3, 3, 4, 26, 0 }, /* Video */ + { 2, 2, 3, 47, 0 } /* Voice */ + }; + struct uath_cmd_txq_setup qinfo; + int ac, error; + + DPRINTF(sc, UATH_DEBUG_WME, "%s: setup Tx queues\n", __func__); + for (ac = 0; ac < 4; ac++) { + qinfo.qid = htobe32(ac); + qinfo.len = htobe32(sizeof(qinfo.attr)); + qinfo.attr.priority = htobe32(ac); /* XXX */ + qinfo.attr.aifs = htobe32(uath_wme_11g[ac].aifsn); + qinfo.attr.logcwmin = htobe32(uath_wme_11g[ac].logcwmin); + qinfo.attr.logcwmax = htobe32(uath_wme_11g[ac].logcwmax); + qinfo.attr.bursttime = htobe32(IEEE80211_TXOP_TO_US( + uath_wme_11g[ac].txop)); + qinfo.attr.mode = htobe32(uath_wme_11g[ac].acm);/*XXX? */ + qinfo.attr.qflags = htobe32(1); /* XXX? */ + + error = uath_cmd_write(sc, WDCMSG_SETUP_TX_QUEUE, &qinfo, + sizeof qinfo, 0); + if (error != 0) + break; + } + return (error); +} + +static void +uath_parent(struct ieee80211com *ic) +{ + struct uath_softc *sc = ic->ic_softc; + int startall = 0; + + UATH_LOCK(sc); + if (sc->sc_flags & UATH_FLAG_INVALID) { + UATH_UNLOCK(sc); + return; + } + + if (ic->ic_nrunning > 0) { + if (!(sc->sc_flags & UATH_FLAG_INITDONE)) { + uath_init(sc); + startall = 1; + } + } else if (sc->sc_flags & UATH_FLAG_INITDONE) + uath_stop(sc); + UATH_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static int +uath_tx_start(struct uath_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, + struct uath_data *data) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct uath_chunk *chunk; + struct uath_tx_desc *desc; + const struct ieee80211_frame *wh; + struct ieee80211_key *k; + int framelen, msglen; + + UATH_ASSERT_LOCKED(sc); + + data->ni = ni; + data->m = m0; + chunk = (struct uath_chunk *)data->buf; + desc = (struct uath_tx_desc *)(chunk + 1); + + if (ieee80211_radiotap_active_vap(vap)) { + struct uath_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + if (m0->m_flags & M_FRAG) + tap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG; + + ieee80211_radiotap_tx(vap, m0); + } + + wh = mtod(m0, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return (ENOBUFS); + } + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(desc + 1)); + + framelen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; + msglen = framelen + sizeof (struct uath_tx_desc); + data->buflen = msglen + sizeof (struct uath_chunk); + + /* one chunk only for now */ + chunk->seqnum = sc->sc_seqnum++; + chunk->flags = (m0->m_flags & M_FRAG) ? 0 : UATH_CFLAGS_FINAL; + if (m0->m_flags & M_LASTFRAG) + chunk->flags |= UATH_CFLAGS_FINAL; + chunk->flags = UATH_CFLAGS_FINAL; + chunk->length = htobe16(msglen); + + /* fill Tx descriptor */ + desc->msglen = htobe32(msglen); + /* NB: to get UATH_TX_NOTIFY reply, `msgid' must be larger than 0 */ + desc->msgid = (sc->sc_msgid++) + 1; /* don't care about endianness */ + desc->type = htobe32(WDCMSG_SEND); + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_CTL: + case IEEE80211_FC0_TYPE_MGT: + /* NB: force all management frames to highest queue */ + if (ni->ni_flags & IEEE80211_NODE_QOS) { + /* NB: force all management frames to highest queue */ + desc->txqid = htobe32(WME_AC_VO | UATH_TXQID_MINRATE); + } else + desc->txqid = htobe32(WME_AC_BE | UATH_TXQID_MINRATE); + break; + case IEEE80211_FC0_TYPE_DATA: + /* XXX multicast frames should honor mcastrate */ + desc->txqid = htobe32(M_WME_GETAC(m0)); + break; + default: + device_printf(sc->sc_dev, "bogus frame type 0x%x (%s)\n", + wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); + m_freem(m0); + return (EIO); + } + if (vap->iv_state == IEEE80211_S_AUTH || + vap->iv_state == IEEE80211_S_ASSOC || + vap->iv_state == IEEE80211_S_RUN) + desc->connid = htobe32(UATH_ID_BSS); + else + desc->connid = htobe32(UATH_ID_INVALID); + desc->flags = htobe32(0 /* no UATH_TX_NOTIFY */); + desc->buflen = htobe32(m0->m_pkthdr.len); + +#ifdef UATH_DEBUG + DPRINTF(sc, UATH_DEBUG_XMIT, + "send frame ix %u framelen %d msglen %d connid 0x%x txqid 0x%x\n", + desc->msgid, framelen, msglen, be32toh(desc->connid), + be32toh(desc->txqid)); + if (sc->sc_debug & UATH_DEBUG_XMIT_DUMP) + uath_dump_cmd(data->buf, data->buflen, '+'); +#endif + + STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); + UATH_STAT_INC(sc, st_tx_pending); + usbd_transfer_start(sc->sc_xfer[UATH_BULK_TX]); + + return (0); +} + +/* + * Cleanup driver resources when we run out of buffers while processing + * fragments; return the tx buffers allocated and drop node references. + */ +static void +uath_txfrag_cleanup(struct uath_softc *sc, + uath_datahead *frags, struct ieee80211_node *ni) +{ + struct uath_data *bf, *next; + + UATH_ASSERT_LOCKED(sc); + + STAILQ_FOREACH_SAFE(bf, frags, next, next) { + /* NB: bf assumed clean */ + STAILQ_REMOVE_HEAD(frags, next); + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); + UATH_STAT_INC(sc, st_tx_inactive); + ieee80211_node_decref(ni); + } +} + +/* + * Setup xmit of a fragmented frame. Allocate a buffer for each frag and bump + * the node reference count to reflect the held reference to be setup by + * uath_tx_start. + */ +static int +uath_txfrag_setup(struct uath_softc *sc, uath_datahead *frags, + struct mbuf *m0, struct ieee80211_node *ni) +{ + struct mbuf *m; + struct uath_data *bf; + + UATH_ASSERT_LOCKED(sc); + for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { + bf = uath_getbuf(sc); + if (bf == NULL) { /* out of buffers, cleanup */ + uath_txfrag_cleanup(sc, frags, ni); + break; + } + ieee80211_node_incref(ni); + STAILQ_INSERT_TAIL(frags, bf, next); + } + + return !STAILQ_EMPTY(frags); +} + +static int +uath_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct uath_softc *sc = ic->ic_softc; + int error; + + UATH_LOCK(sc); + if ((sc->sc_flags & UATH_FLAG_INITDONE) == 0) { + UATH_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + UATH_UNLOCK(sc); + return (error); + } + uath_start(sc); + UATH_UNLOCK(sc); + + return (0); +} + +static void +uath_start(struct uath_softc *sc) +{ + struct uath_data *bf; + struct ieee80211_node *ni; + struct mbuf *m, *next; + uath_datahead frags; + + UATH_ASSERT_LOCKED(sc); + + if ((sc->sc_flags & UATH_FLAG_INITDONE) == 0 || + (sc->sc_flags & UATH_FLAG_INVALID)) + return; + + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + bf = uath_getbuf(sc); + if (bf == NULL) { + mbufq_prepend(&sc->sc_snd, m); + break; + } + + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + + /* + * Check for fragmentation. If this frame has been broken up + * verify we have enough buffers to send all the fragments + * so all go out or none... + */ + STAILQ_INIT(&frags); + if ((m->m_flags & M_FRAG) && + !uath_txfrag_setup(sc, &frags, m, ni)) { + DPRINTF(sc, UATH_DEBUG_XMIT, + "%s: out of txfrag buffers\n", __func__); + ieee80211_free_mbuf(m); + goto bad; + } + sc->sc_seqnum = 0; + nextfrag: + /* + * Pass the frame to the h/w for transmission. + * Fragmented frames have each frag chained together + * with m_nextpkt. We know there are sufficient uath_data's + * to send all the frags because of work done by + * uath_txfrag_setup. + */ + next = m->m_nextpkt; + if (uath_tx_start(sc, m, ni, bf) != 0) { + bad: + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + reclaim: + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); + UATH_STAT_INC(sc, st_tx_inactive); + uath_txfrag_cleanup(sc, &frags, ni); + ieee80211_free_node(ni); + continue; + } + + if (next != NULL) { + /* + * Beware of state changing between frags. + XXX check sta power-save state? + */ + if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { + DPRINTF(sc, UATH_DEBUG_XMIT, + "%s: flush fragmented packet, state %s\n", + __func__, + ieee80211_state_name[ni->ni_vap->iv_state]); + ieee80211_free_mbuf(next); + goto reclaim; + } + m = next; + bf = STAILQ_FIRST(&frags); + KASSERT(bf != NULL, ("no buf for txfrag")); + STAILQ_REMOVE_HEAD(&frags, next); + goto nextfrag; + } + + sc->sc_tx_timer = 5; + } +} + +static int +uath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct uath_data *bf; + struct uath_softc *sc = ic->ic_softc; + + UATH_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if ((sc->sc_flags & UATH_FLAG_INVALID) || + !(sc->sc_flags & UATH_FLAG_INITDONE)) { + m_freem(m); + UATH_UNLOCK(sc); + return (ENETDOWN); + } + + /* grab a TX buffer */ + bf = uath_getbuf(sc); + if (bf == NULL) { + m_freem(m); + UATH_UNLOCK(sc); + return (ENOBUFS); + } + + sc->sc_seqnum = 0; + if (uath_tx_start(sc, m, ni, bf) != 0) { + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); + UATH_STAT_INC(sc, st_tx_inactive); + UATH_UNLOCK(sc); + return (EIO); + } + UATH_UNLOCK(sc); + + sc->sc_tx_timer = 5; + return (0); +} + +static void +uath_scan_start(struct ieee80211com *ic) +{ + /* do nothing */ +} + +static void +uath_scan_end(struct ieee80211com *ic) +{ + /* do nothing */ +} + +static void +uath_set_channel(struct ieee80211com *ic) +{ + struct uath_softc *sc = ic->ic_softc; + + UATH_LOCK(sc); + if ((sc->sc_flags & UATH_FLAG_INVALID) || + (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { + UATH_UNLOCK(sc); + return; + } + (void)uath_switch_channel(sc, ic->ic_curchan); + UATH_UNLOCK(sc); +} + +static int +uath_set_rxmulti_filter(struct uath_softc *sc) +{ + /* XXX broken */ + return (0); +} +static void +uath_update_mcast(struct ieee80211com *ic) +{ + struct uath_softc *sc = ic->ic_softc; + + UATH_LOCK(sc); + if ((sc->sc_flags & UATH_FLAG_INVALID) || + (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { + UATH_UNLOCK(sc); + return; + } + /* + * this is for avoiding the race condition when we're try to + * connect to the AP with WPA. + */ + if (sc->sc_flags & UATH_FLAG_INITDONE) + (void)uath_set_rxmulti_filter(sc); + UATH_UNLOCK(sc); +} + +static void +uath_update_promisc(struct ieee80211com *ic) +{ + struct uath_softc *sc = ic->ic_softc; + + UATH_LOCK(sc); + if ((sc->sc_flags & UATH_FLAG_INVALID) || + (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { + UATH_UNLOCK(sc); + return; + } + if (sc->sc_flags & UATH_FLAG_INITDONE) { + uath_set_rxfilter(sc, + UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST | + UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON | + UATH_FILTER_RX_PROM, UATH_FILTER_OP_SET); + } + UATH_UNLOCK(sc); +} + +static int +uath_create_connection(struct uath_softc *sc, uint32_t connid) +{ + const struct ieee80211_rateset *rs; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni; + struct uath_cmd_create_connection create; + + ni = ieee80211_ref_node(vap->iv_bss); + memset(&create, 0, sizeof(create)); + create.connid = htobe32(connid); + create.bssid = htobe32(0); + /* XXX packed or not? */ + create.size = htobe32(sizeof(struct uath_cmd_rateset)); + + rs = &ni->ni_rates; + create.connattr.rateset.length = rs->rs_nrates; + bcopy(rs->rs_rates, &create.connattr.rateset.set[0], + rs->rs_nrates); + + /* XXX turbo */ + if (IEEE80211_IS_CHAN_A(ni->ni_chan)) + create.connattr.wlanmode = htobe32(WLAN_MODE_11a); + else if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) + create.connattr.wlanmode = htobe32(WLAN_MODE_11g); + else + create.connattr.wlanmode = htobe32(WLAN_MODE_11b); + ieee80211_free_node(ni); + + return uath_cmd_write(sc, WDCMSG_CREATE_CONNECTION, &create, + sizeof create, 0); +} + +static int +uath_set_rates(struct uath_softc *sc, const struct ieee80211_rateset *rs) +{ + struct uath_cmd_rates rates; + + memset(&rates, 0, sizeof(rates)); + rates.connid = htobe32(UATH_ID_BSS); /* XXX */ + rates.size = htobe32(sizeof(struct uath_cmd_rateset)); + /* XXX bounds check rs->rs_nrates */ + rates.rateset.length = rs->rs_nrates; + bcopy(rs->rs_rates, &rates.rateset.set[0], rs->rs_nrates); + + DPRINTF(sc, UATH_DEBUG_RATES, + "setting supported rates nrates=%d\n", rs->rs_nrates); + return uath_cmd_write(sc, WDCMSG_SET_BASIC_RATE, + &rates, sizeof rates, 0); +} + +static int +uath_write_associd(struct uath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni; + struct uath_cmd_set_associd associd; + + ni = ieee80211_ref_node(vap->iv_bss); + memset(&associd, 0, sizeof(associd)); + associd.defaultrateix = htobe32(1); /* XXX */ + associd.associd = htobe32(ni->ni_associd); + associd.timoffset = htobe32(0x3b); /* XXX */ + IEEE80211_ADDR_COPY(associd.bssid, ni->ni_bssid); + ieee80211_free_node(ni); + return uath_cmd_write(sc, WDCMSG_WRITE_ASSOCID, &associd, + sizeof associd, 0); +} + +static int +uath_set_ledsteady(struct uath_softc *sc, int lednum, int ledmode) +{ + struct uath_cmd_ledsteady led; + + led.lednum = htobe32(lednum); + led.ledmode = htobe32(ledmode); + + DPRINTF(sc, UATH_DEBUG_LED, "set %s led %s (steady)\n", + (lednum == UATH_LED_LINK) ? "link" : "activity", + ledmode ? "on" : "off"); + return uath_cmd_write(sc, WDCMSG_SET_LED_STEADY, &led, sizeof led, 0); +} + +static int +uath_set_ledblink(struct uath_softc *sc, int lednum, int ledmode, + int blinkrate, int slowmode) +{ + struct uath_cmd_ledblink led; + + led.lednum = htobe32(lednum); + led.ledmode = htobe32(ledmode); + led.blinkrate = htobe32(blinkrate); + led.slowmode = htobe32(slowmode); + + DPRINTF(sc, UATH_DEBUG_LED, "set %s led %s (blink)\n", + (lednum == UATH_LED_LINK) ? "link" : "activity", + ledmode ? "on" : "off"); + return uath_cmd_write(sc, WDCMSG_SET_LED_BLINK, &led, sizeof led, 0); +} + +static int +uath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + enum ieee80211_state ostate = vap->iv_state; + int error; + struct ieee80211_node *ni; + struct ieee80211com *ic = vap->iv_ic; + struct uath_softc *sc = ic->ic_softc; + struct uath_vap *uvp = UATH_VAP(vap); + + DPRINTF(sc, UATH_DEBUG_STATE, + "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + UATH_LOCK(sc); + callout_stop(&sc->stat_ch); + callout_stop(&sc->watchdog_ch); + ni = ieee80211_ref_node(vap->iv_bss); + + switch (nstate) { + case IEEE80211_S_INIT: + if (ostate == IEEE80211_S_RUN) { + /* turn link and activity LEDs off */ + uath_set_ledstate(sc, 0); + } + break; + + case IEEE80211_S_SCAN: + break; + + case IEEE80211_S_AUTH: + /* XXX good place? set RTS threshold */ + uath_config(sc, CFG_USER_RTS_THRESHOLD, vap->iv_rtsthreshold); + /* XXX bad place */ + error = uath_set_keys(sc, vap); + if (error != 0) { + device_printf(sc->sc_dev, + "could not set crypto keys, error %d\n", error); + break; + } + if (uath_switch_channel(sc, ni->ni_chan) != 0) { + device_printf(sc->sc_dev, "could not switch channel\n"); + break; + } + if (uath_create_connection(sc, UATH_ID_BSS) != 0) { + device_printf(sc->sc_dev, + "could not create connection\n"); + break; + } + break; + + case IEEE80211_S_ASSOC: + if (uath_set_rates(sc, &ni->ni_rates) != 0) { + device_printf(sc->sc_dev, + "could not set negotiated rate set\n"); + break; + } + break; + + case IEEE80211_S_RUN: + /* XXX monitor mode doesn't be tested */ + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + uath_set_ledstate(sc, 1); + break; + } + + /* + * Tx rate is controlled by firmware, report the maximum + * negotiated rate in ifconfig output. + */ + ni->ni_txrate = ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates-1]; + + if (uath_write_associd(sc) != 0) { + device_printf(sc->sc_dev, + "could not write association id\n"); + break; + } + /* turn link LED on */ + uath_set_ledsteady(sc, UATH_LED_LINK, UATH_LED_ON); + /* make activity LED blink */ + uath_set_ledblink(sc, UATH_LED_ACTIVITY, UATH_LED_ON, 1, 2); + /* set state to associated */ + uath_set_ledstate(sc, 1); + + /* start statistics timer */ + callout_reset(&sc->stat_ch, hz, uath_stat, sc); + break; + default: + break; + } + ieee80211_free_node(ni); + UATH_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); +} + +static int +uath_set_key(struct uath_softc *sc, const struct ieee80211_key *wk, + int index) +{ +#if 0 + struct uath_cmd_crypto crypto; + int i; + + memset(&crypto, 0, sizeof(crypto)); + crypto.keyidx = htobe32(index); + crypto.magic1 = htobe32(1); + crypto.size = htobe32(368); + crypto.mask = htobe32(0xffff); + crypto.flags = htobe32(0x80000068); + if (index != UATH_DEFAULT_KEY) + crypto.flags |= htobe32(index << 16); + memset(crypto.magic2, 0xff, sizeof(crypto.magic2)); + + /* + * Each byte of the key must be XOR'ed with 10101010 before being + * transmitted to the firmware. + */ + for (i = 0; i < wk->wk_keylen; i++) + crypto.key[i] = wk->wk_key[i] ^ 0xaa; + + DPRINTF(sc, UATH_DEBUG_CRYPTO, + "setting crypto key index=%d len=%d\n", index, wk->wk_keylen); + return uath_cmd_write(sc, WDCMSG_SET_KEY_CACHE_ENTRY, &crypto, + sizeof crypto, 0); +#else + /* XXX support H/W cryto */ + return (0); +#endif +} + +static int +uath_set_keys(struct uath_softc *sc, struct ieee80211vap *vap) +{ + int i, error; + + error = 0; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + const struct ieee80211_key *wk = &vap->iv_nw_keys[i]; + + if (wk->wk_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV)) { + error = uath_set_key(sc, wk, i); + if (error) + return (error); + } + } + if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) { + error = uath_set_key(sc, &vap->iv_nw_keys[vap->iv_def_txkey], + UATH_DEFAULT_KEY); + } + return (error); +} + +#define UATH_SYSCTL_STAT_ADD32(c, h, n, p, d) \ + SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) + +static void +uath_sysctl_node(struct uath_softc *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *child; + struct sysctl_oid *tree; + struct uath_stat *stats; + + stats = &sc->sc_stat; + ctx = device_get_sysctl_ctx(sc->sc_dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); + + tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, + NULL, "UATH statistics"); + child = SYSCTL_CHILDREN(tree); + UATH_SYSCTL_STAT_ADD32(ctx, child, "badchunkseqnum", + &stats->st_badchunkseqnum, "Bad chunk sequence numbers"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "invalidlen", &stats->st_invalidlen, + "Invalid length"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "multichunk", &stats->st_multichunk, + "Multi chunks"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "toobigrxpkt", + &stats->st_toobigrxpkt, "Too big rx packets"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "stopinprogress", + &stats->st_stopinprogress, "Stop in progress"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "crcerrs", &stats->st_crcerr, + "CRC errors"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "phyerr", &stats->st_phyerr, + "PHY errors"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "decrypt_crcerr", + &stats->st_decrypt_crcerr, "Decryption CRC errors"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "decrypt_micerr", + &stats->st_decrypt_micerr, "Decryption Misc errors"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "decomperr", &stats->st_decomperr, + "Decomp errors"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "keyerr", &stats->st_keyerr, + "Key errors"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "err", &stats->st_err, + "Unknown errors"); + + UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_active", + &stats->st_cmd_active, "Active numbers in Command queue"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_inactive", + &stats->st_cmd_inactive, "Inactive numbers in Command queue"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_pending", + &stats->st_cmd_pending, "Pending numbers in Command queue"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_waiting", + &stats->st_cmd_waiting, "Waiting numbers in Command queue"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "rx_active", + &stats->st_rx_active, "Active numbers in RX queue"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "rx_inactive", + &stats->st_rx_inactive, "Inactive numbers in RX queue"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_active", + &stats->st_tx_active, "Active numbers in TX queue"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_inactive", + &stats->st_tx_inactive, "Inactive numbers in TX queue"); + UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_pending", + &stats->st_tx_pending, "Pending numbers in TX queue"); +} + +#undef UATH_SYSCTL_STAT_ADD32 + +static void +uath_cmdeof(struct uath_softc *sc, struct uath_cmd *cmd) +{ + struct uath_cmd_hdr *hdr; + int dlen; + + hdr = (struct uath_cmd_hdr *)cmd->buf; + /* NB: msgid is passed thru w/o byte swapping */ +#ifdef UATH_DEBUG + if (sc->sc_debug & UATH_DEBUG_CMDS) { + int len = be32toh(hdr->len); + printf("%s: %s [ix %u] len %u status %u\n", + __func__, uath_codename(be32toh(hdr->code)), + hdr->msgid, len, be32toh(hdr->magic)); + if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) + uath_dump_cmd(cmd->buf, + len > UATH_MAX_CMDSZ ? sizeof(*hdr) : len, '-'); + } +#endif + hdr->code = be32toh(hdr->code); + hdr->len = be32toh(hdr->len); + hdr->magic = be32toh(hdr->magic); /* target status on return */ + + switch (hdr->code & 0xff) { + /* reply to a read command */ + default: + dlen = hdr->len - sizeof(*hdr); + if (dlen < 0) { + device_printf(sc->sc_dev, + "Invalid header length %d\n", dlen); + return; + } + DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, + "%s: code %d data len %u\n", + __func__, hdr->code & 0xff, dlen); + /* + * The first response from the target after the + * HOST_AVAILABLE has an invalid msgid so we must + * treat it specially. + */ + if (hdr->msgid < UATH_CMD_LIST_COUNT) { + uint32_t *rp = (uint32_t *)(hdr+1); + u_int olen; + + if (!(sizeof(*hdr) <= hdr->len && + hdr->len < UATH_MAX_CMDSZ)) { + device_printf(sc->sc_dev, + "%s: invalid WDC msg length %u; " + "msg ignored\n", __func__, hdr->len); + return; + } + /* + * Calculate return/receive payload size; the + * first word, if present, always gives the + * number of bytes--unless it's 0 in which + * case a single 32-bit word should be present. + */ + if (dlen >= (int)sizeof(uint32_t)) { + olen = be32toh(rp[0]); + dlen -= sizeof(uint32_t); + if (olen == 0) { + /* convention is 0 =>'s one word */ + olen = sizeof(uint32_t); + /* XXX KASSERT(olen == dlen ) */ + } + } else + olen = 0; + if (cmd->odata != NULL) { + /* NB: cmd->olen validated in uath_cmd */ + if (olen > (u_int)cmd->olen) { + /* XXX complain? */ + device_printf(sc->sc_dev, + "%s: cmd 0x%x olen %u cmd olen %u\n", + __func__, hdr->code, olen, + cmd->olen); + olen = cmd->olen; + } + if (olen > (u_int)dlen) { + /* XXX complain, shouldn't happen */ + device_printf(sc->sc_dev, + "%s: cmd 0x%x olen %u dlen %u\n", + __func__, hdr->code, olen, dlen); + olen = dlen; + } + /* XXX have submitter do this */ + /* copy answer into caller's supplied buffer */ + bcopy(&rp[1], cmd->odata, olen); + cmd->olen = olen; + } + } + wakeup_one(cmd); /* wake up caller */ + break; + + case WDCMSG_TARGET_START: + if (hdr->msgid >= UATH_CMD_LIST_COUNT) { + /* XXX */ + return; + } + dlen = hdr->len - sizeof(*hdr); + if (dlen != (int)sizeof(uint32_t)) { + /* XXX something wrong */ + return; + } + /* XXX have submitter do this */ + /* copy answer into caller's supplied buffer */ + bcopy(hdr+1, cmd->odata, sizeof(uint32_t)); + cmd->olen = sizeof(uint32_t); + wakeup_one(cmd); /* wake up caller */ + break; + + case WDCMSG_SEND_COMPLETE: + /* this notification is sent when UATH_TX_NOTIFY is set */ + DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, + "%s: received Tx notification\n", __func__); + break; + + case WDCMSG_TARGET_GET_STATS: + DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, + "%s: received device statistics\n", __func__); + callout_reset(&sc->stat_ch, hz, uath_stat, sc); + break; + } +} + +static void +uath_intr_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct uath_softc *sc = usbd_xfer_softc(xfer); + struct uath_cmd *cmd; + struct usb_page_cache *pc; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + UATH_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + cmd = STAILQ_FIRST(&sc->sc_cmd_waiting); + if (cmd == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_cmd_waiting, next); + UATH_STAT_DEC(sc, st_cmd_waiting); + STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next); + UATH_STAT_INC(sc, st_cmd_inactive); + + KASSERT(actlen >= (int)sizeof(struct uath_cmd_hdr), + ("short xfer error")); + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, cmd->buf, actlen); + uath_cmdeof(sc, cmd); + case USB_ST_SETUP: +setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + default: + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto setup; + } + break; + } +} + +static void +uath_intr_tx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct uath_softc *sc = usbd_xfer_softc(xfer); + struct uath_cmd *cmd; + + UATH_ASSERT_LOCKED(sc); + + cmd = STAILQ_FIRST(&sc->sc_cmd_active); + if (cmd != NULL && USB_GET_STATE(xfer) != USB_ST_SETUP) { + STAILQ_REMOVE_HEAD(&sc->sc_cmd_active, next); + UATH_STAT_DEC(sc, st_cmd_active); + STAILQ_INSERT_TAIL((cmd->flags & UATH_CMD_FLAG_READ) ? + &sc->sc_cmd_waiting : &sc->sc_cmd_inactive, cmd, next); + if (cmd->flags & UATH_CMD_FLAG_READ) + UATH_STAT_INC(sc, st_cmd_waiting); + else + UATH_STAT_INC(sc, st_cmd_inactive); + } + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + case USB_ST_SETUP: +setup: + cmd = STAILQ_FIRST(&sc->sc_cmd_pending); + if (cmd == NULL) { + DPRINTF(sc, UATH_DEBUG_XMIT, "%s: empty pending queue\n", + __func__); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_cmd_pending, next); + UATH_STAT_DEC(sc, st_cmd_pending); + STAILQ_INSERT_TAIL((cmd->flags & UATH_CMD_FLAG_ASYNC) ? + &sc->sc_cmd_inactive : &sc->sc_cmd_active, cmd, next); + if (cmd->flags & UATH_CMD_FLAG_ASYNC) + UATH_STAT_INC(sc, st_cmd_inactive); + else + UATH_STAT_INC(sc, st_cmd_active); + + usbd_xfer_set_frame_data(xfer, 0, cmd->buf, cmd->buflen); + usbd_transfer_submit(xfer); + break; + default: + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto setup; + } + break; + } +} + +static void +uath_update_rxstat(struct uath_softc *sc, uint32_t status) +{ + + switch (status) { + case UATH_STATUS_STOP_IN_PROGRESS: + UATH_STAT_INC(sc, st_stopinprogress); + break; + case UATH_STATUS_CRC_ERR: + UATH_STAT_INC(sc, st_crcerr); + break; + case UATH_STATUS_PHY_ERR: + UATH_STAT_INC(sc, st_phyerr); + break; + case UATH_STATUS_DECRYPT_CRC_ERR: + UATH_STAT_INC(sc, st_decrypt_crcerr); + break; + case UATH_STATUS_DECRYPT_MIC_ERR: + UATH_STAT_INC(sc, st_decrypt_micerr); + break; + case UATH_STATUS_DECOMP_ERR: + UATH_STAT_INC(sc, st_decomperr); + break; + case UATH_STATUS_KEY_ERR: + UATH_STAT_INC(sc, st_keyerr); + break; + case UATH_STATUS_ERR: + UATH_STAT_INC(sc, st_err); + break; + default: + break; + } +} + +static struct mbuf * +uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data, + struct uath_rx_desc **pdesc) +{ + struct uath_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct uath_chunk *chunk; + struct uath_rx_desc *desc; + struct mbuf *m = data->m, *mnew, *mp; + uint16_t chunklen; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + if (actlen < (int)UATH_MIN_RXBUFSZ) { + DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, + "%s: wrong xfer size (len=%d)\n", __func__, actlen); + counter_u64_add(ic->ic_ierrors, 1); + return (NULL); + } + + chunk = (struct uath_chunk *)data->buf; + if (chunk->seqnum == 0 && chunk->flags == 0 && chunk->length == 0) { + device_printf(sc->sc_dev, "%s: strange response\n", __func__); + counter_u64_add(ic->ic_ierrors, 1); + UATH_RESET_INTRX(sc); + return (NULL); + } + + if (chunk->seqnum != sc->sc_intrx_nextnum) { + DPRINTF(sc, UATH_DEBUG_XMIT, "invalid seqnum %d, expected %d\n", + chunk->seqnum, sc->sc_intrx_nextnum); + UATH_STAT_INC(sc, st_badchunkseqnum); + if (sc->sc_intrx_head != NULL) + m_freem(sc->sc_intrx_head); + UATH_RESET_INTRX(sc); + return (NULL); + } + + /* check multi-chunk frames */ + if ((chunk->seqnum == 0 && !(chunk->flags & UATH_CFLAGS_FINAL)) || + (chunk->seqnum != 0 && (chunk->flags & UATH_CFLAGS_FINAL)) || + chunk->flags & UATH_CFLAGS_RXMSG) + UATH_STAT_INC(sc, st_multichunk); + + chunklen = be16toh(chunk->length); + if (chunk->flags & UATH_CFLAGS_FINAL) + chunklen -= sizeof(struct uath_rx_desc); + + if (chunklen > 0 && + (!(chunk->flags & UATH_CFLAGS_FINAL) || !(chunk->seqnum == 0))) { + /* we should use intermediate RX buffer */ + if (chunk->seqnum == 0) + UATH_RESET_INTRX(sc); + if ((sc->sc_intrx_len + sizeof(struct uath_rx_desc) + + chunklen) > UATH_MAX_INTRX_SIZE) { + UATH_STAT_INC(sc, st_invalidlen); + counter_u64_add(ic->ic_ierrors, 1); + if (sc->sc_intrx_head != NULL) + m_freem(sc->sc_intrx_head); + UATH_RESET_INTRX(sc); + return (NULL); + } + + m->m_len = chunklen; + m->m_data += sizeof(struct uath_chunk); + + if (sc->sc_intrx_head == NULL) { + sc->sc_intrx_head = m; + sc->sc_intrx_tail = m; + } else { + m->m_flags &= ~M_PKTHDR; + sc->sc_intrx_tail->m_next = m; + sc->sc_intrx_tail = m; + } + } + sc->sc_intrx_len += chunklen; + + mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (mnew == NULL) { + DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, + "%s: can't get new mbuf, drop frame\n", __func__); + counter_u64_add(ic->ic_ierrors, 1); + if (sc->sc_intrx_head != NULL) + m_freem(sc->sc_intrx_head); + UATH_RESET_INTRX(sc); + return (NULL); + } + + data->m = mnew; + data->buf = mtod(mnew, uint8_t *); + + /* if the frame is not final continue the transfer */ + if (!(chunk->flags & UATH_CFLAGS_FINAL)) { + sc->sc_intrx_nextnum++; + UATH_RESET_INTRX(sc); + return (NULL); + } + + /* + * if the frame is not set UATH_CFLAGS_RXMSG, then rx descriptor is + * located at the end, 32-bit aligned + */ + desc = (chunk->flags & UATH_CFLAGS_RXMSG) ? + (struct uath_rx_desc *)(chunk + 1) : + (struct uath_rx_desc *)(((uint8_t *)chunk) + + sizeof(struct uath_chunk) + be16toh(chunk->length) - + sizeof(struct uath_rx_desc)); + *pdesc = desc; + + DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, + "%s: frame len %u code %u status %u rate %u antenna %u " + "rssi %d channel %u phyerror %u connix %u decrypterror %u " + "keycachemiss %u\n", __func__, be32toh(desc->framelen) + , be32toh(desc->code), be32toh(desc->status), be32toh(desc->rate) + , be32toh(desc->antenna), be32toh(desc->rssi), be32toh(desc->channel) + , be32toh(desc->phyerror), be32toh(desc->connix) + , be32toh(desc->decrypterror), be32toh(desc->keycachemiss)); + + if (be32toh(desc->len) > MCLBYTES) { + DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, + "%s: bad descriptor (len=%d)\n", __func__, + be32toh(desc->len)); + counter_u64_add(ic->ic_ierrors, 1); + UATH_STAT_INC(sc, st_toobigrxpkt); + if (sc->sc_intrx_head != NULL) + m_freem(sc->sc_intrx_head); + UATH_RESET_INTRX(sc); + return (NULL); + } + + uath_update_rxstat(sc, be32toh(desc->status)); + + /* finalize mbuf */ + if (sc->sc_intrx_head == NULL) { + m->m_pkthdr.len = m->m_len = + be32toh(desc->framelen) - UATH_RX_DUMMYSIZE; + m->m_data += sizeof(struct uath_chunk); + } else { + mp = sc->sc_intrx_head; + mp->m_flags |= M_PKTHDR; + mp->m_pkthdr.len = sc->sc_intrx_len; + m = mp; + } + + /* there are a lot more fields in the RX descriptor */ + if ((sc->sc_flags & UATH_FLAG_INVALID) == 0 && + ieee80211_radiotap_active(ic)) { + struct uath_rx_radiotap_header *tap = &sc->sc_rxtap; + uint32_t tsf_hi = be32toh(desc->tstamp_high); + uint32_t tsf_lo = be32toh(desc->tstamp_low); + + /* XXX only get low order 24bits of tsf from h/w */ + tap->wr_tsf = htole64(((uint64_t)tsf_hi << 32) | tsf_lo); + tap->wr_flags = 0; + if (be32toh(desc->status) == UATH_STATUS_CRC_ERR) + tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; + /* XXX map other status to BADFCS? */ + /* XXX ath h/w rate code, need to map */ + tap->wr_rate = be32toh(desc->rate); + tap->wr_antenna = be32toh(desc->antenna); + tap->wr_antsignal = -95 + be32toh(desc->rssi); + tap->wr_antnoise = -95; + } + + UATH_RESET_INTRX(sc); + + return (m); +} + +static void +uath_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct uath_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m = NULL; + struct uath_data *data; + struct uath_rx_desc *desc = NULL; + int8_t nf; + + UATH_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + UATH_STAT_DEC(sc, st_rx_active); + m = uath_data_rxeof(xfer, data, &desc); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + UATH_STAT_INC(sc, st_rx_inactive); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + data = STAILQ_FIRST(&sc->sc_rx_inactive); + if (data == NULL) + return; + STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); + UATH_STAT_DEC(sc, st_rx_inactive); + STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); + UATH_STAT_INC(sc, st_rx_active); + usbd_xfer_set_frame_data(xfer, 0, data->buf, MCLBYTES); + usbd_transfer_submit(xfer); + + /* + * To avoid LOR we should unlock our private mutex here to call + * ieee80211_input() because here is at the end of a USB + * callback and safe to unlock. + */ + if (sc->sc_flags & UATH_FLAG_INVALID) { + if (m != NULL) + m_freem(m); + return; + } + UATH_UNLOCK(sc); + if (m != NULL && desc != NULL) { + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, + (struct ieee80211_frame_min *)wh); + nf = -95; /* XXX */ + if (ni != NULL) { + (void) ieee80211_input(ni, m, + (int)be32toh(desc->rssi), nf); + /* node is no longer needed */ + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, + (int)be32toh(desc->rssi), nf); + m = NULL; + desc = NULL; + } + UATH_LOCK(sc); + uath_start(sc); + break; + default: + /* needs it to the inactive queue due to a error. */ + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + UATH_STAT_DEC(sc, st_rx_active); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + UATH_STAT_INC(sc, st_rx_inactive); + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + counter_u64_add(ic->ic_ierrors, 1); + goto setup; + } + break; + } +} + +static void +uath_data_txeof(struct usb_xfer *xfer, struct uath_data *data) +{ + struct uath_softc *sc = usbd_xfer_softc(xfer); + + UATH_ASSERT_LOCKED(sc); + + if (data->m) { + /* XXX status? */ + ieee80211_tx_complete(data->ni, data->m, 0); + data->m = NULL; + data->ni = NULL; + } + sc->sc_tx_timer = 0; +} + +static void +uath_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct uath_softc *sc = usbd_xfer_softc(xfer); + struct uath_data *data; + + UATH_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_tx_active); + if (data == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); + UATH_STAT_DEC(sc, st_tx_active); + uath_data_txeof(xfer, data); + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); + UATH_STAT_INC(sc, st_tx_inactive); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + data = STAILQ_FIRST(&sc->sc_tx_pending); + if (data == NULL) { + DPRINTF(sc, UATH_DEBUG_XMIT, "%s: empty pending queue\n", + __func__); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); + UATH_STAT_DEC(sc, st_tx_pending); + STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); + UATH_STAT_INC(sc, st_tx_active); + + usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); + usbd_transfer_submit(xfer); + + uath_start(sc); + break; + default: + data = STAILQ_FIRST(&sc->sc_tx_active); + if (data == NULL) + goto setup; + if (data->ni != NULL) { + if_inc_counter(data->ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + if ((sc->sc_flags & UATH_FLAG_INVALID) == 0) + ieee80211_free_node(data->ni); + data->ni = NULL; + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto setup; + } + break; + } +} + +static device_method_t uath_methods[] = { + DEVMETHOD(device_probe, uath_match), + DEVMETHOD(device_attach, uath_attach), + DEVMETHOD(device_detach, uath_detach), + DEVMETHOD_END +}; +static driver_t uath_driver = { + .name = "uath", + .methods = uath_methods, + .size = sizeof(struct uath_softc) +}; +static devclass_t uath_devclass; + +DRIVER_MODULE(uath, uhub, uath_driver, uath_devclass, NULL, 0); +MODULE_DEPEND(uath, wlan, 1, 1, 1); +MODULE_DEPEND(uath, usb, 1, 1, 1); +MODULE_VERSION(uath, 1); +USB_PNP_HOST_INFO(uath_devs); diff --git a/freebsd/sys/dev/usb/wlan/if_uathreg.h b/freebsd/sys/dev/usb/wlan/if_uathreg.h new file mode 100644 index 00000000..1e7929bc --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_uathreg.h @@ -0,0 +1,601 @@ +/* $OpenBSD: if_uathreg.h,v 1.2 2006/09/18 16:34:23 damien Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2006 + * Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2006 Sam Leffler, Errno Consulting + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define UATH_CONFIG_INDEX 0 +#define UATH_IFACE_INDEX 0 + +/* all fields are big endian */ +struct uath_fwblock { + uint32_t flags; +#define UATH_WRITE_BLOCK (1 << 4) + + uint32_t len; +#define UATH_MAX_FWBLOCK_SIZE 2048 + + uint32_t total; + uint32_t remain; + uint32_t rxtotal; + uint32_t pad[123]; +} __packed; + +#define UATH_MAX_CMDSZ 512 + +/* + * Messages are passed in Target Endianness. All fixed-size + * fields of a WDS Control Message are treated as 32-bit + * values and Control Msgs are guaranteed to be 32-bit aligned. + * + * The format of a WDS Control Message is as follows: + * Message Length 32 bits + * Message Opcode 32 bits + * Message ID 32 bits + * parameter 1 + * parameter 2 + * ... + * + * A variable-length parameter, or a parmeter that is larger than + * 32 bits is passed as <length, data> pair, where length is a + * 32-bit quantity and data is padded to 32 bits. + */ +struct uath_cmd_hdr { + uint32_t len; /* msg length including header */ + uint32_t code; /* operation code */ +/* NB: these are defined for rev 1.5 firmware; rev 1.6 is different */ +/* messages from Host -> Target */ +#define WDCMSG_HOST_AVAILABLE 0x01 +#define WDCMSG_BIND 0x02 +#define WDCMSG_TARGET_RESET 0x03 +#define WDCMSG_TARGET_GET_CAPABILITY 0x04 +#define WDCMSG_TARGET_SET_CONFIG 0x05 +#define WDCMSG_TARGET_GET_STATUS 0x06 +#define WDCMSG_TARGET_GET_STATS 0x07 +#define WDCMSG_TARGET_START 0x08 +#define WDCMSG_TARGET_STOP 0x09 +#define WDCMSG_TARGET_ENABLE 0x0a +#define WDCMSG_TARGET_DISABLE 0x0b +#define WDCMSG_CREATE_CONNECTION 0x0c +#define WDCMSG_UPDATE_CONNECT_ATTR 0x0d +#define WDCMSG_DELETE_CONNECT 0x0e +#define WDCMSG_SEND 0x0f +#define WDCMSG_FLUSH 0x10 +/* messages from Target -> Host */ +#define WDCMSG_STATS_UPDATE 0x11 +#define WDCMSG_BMISS 0x12 +#define WDCMSG_DEVICE_AVAIL 0x13 +#define WDCMSG_SEND_COMPLETE 0x14 +#define WDCMSG_DATA_AVAIL 0x15 +#define WDCMSG_SET_PWR_MODE 0x16 +#define WDCMSG_BMISS_ACK 0x17 +#define WDCMSG_SET_LED_STEADY 0x18 +#define WDCMSG_SET_LED_BLINK 0x19 +/* more messages */ +#define WDCMSG_SETUP_BEACON_DESC 0x1a +#define WDCMSG_BEACON_INIT 0x1b +#define WDCMSG_RESET_KEY_CACHE 0x1c +#define WDCMSG_RESET_KEY_CACHE_ENTRY 0x1d +#define WDCMSG_SET_KEY_CACHE_ENTRY 0x1e +#define WDCMSG_SET_DECOMP_MASK 0x1f +#define WDCMSG_SET_REGULATORY_DOMAIN 0x20 +#define WDCMSG_SET_LED_STATE 0x21 +#define WDCMSG_WRITE_ASSOCID 0x22 +#define WDCMSG_SET_STA_BEACON_TIMERS 0x23 +#define WDCMSG_GET_TSF 0x24 +#define WDCMSG_RESET_TSF 0x25 +#define WDCMSG_SET_ADHOC_MODE 0x26 +#define WDCMSG_SET_BASIC_RATE 0x27 +#define WDCMSG_MIB_CONTROL 0x28 +#define WDCMSG_GET_CHANNEL_DATA 0x29 +#define WDCMSG_GET_CUR_RSSI 0x2a +#define WDCMSG_SET_ANTENNA_SWITCH 0x2b +#define WDCMSG_USE_SHORT_SLOT_TIME 0x2f +#define WDCMSG_SET_POWER_MODE 0x30 +#define WDCMSG_SETUP_PSPOLL_DESC 0x31 +#define WDCMSG_SET_RX_MULTICAST_FILTER 0x32 +#define WDCMSG_RX_FILTER 0x33 +#define WDCMSG_PER_CALIBRATION 0x34 +#define WDCMSG_RESET 0x35 +#define WDCMSG_DISABLE 0x36 +#define WDCMSG_PHY_DISABLE 0x37 +#define WDCMSG_SET_TX_POWER_LIMIT 0x38 +#define WDCMSG_SET_TX_QUEUE_PARAMS 0x39 +#define WDCMSG_SETUP_TX_QUEUE 0x3a +#define WDCMSG_RELEASE_TX_QUEUE 0x3b +#define WDCMSG_SET_DEFAULT_KEY 0x43 + uint32_t msgid; /* msg id (supplied by host) */ + uint32_t magic; /* response desired/target status */ + uint32_t debug[4]; /* debug data area */ + /* msg data follows */ +} __packed; + +struct uath_chunk { + uint8_t seqnum; /* sequence number for ordering */ + uint8_t flags; +#define UATH_CFLAGS_FINAL 0x01 /* final chunk of a msg */ +#define UATH_CFLAGS_RXMSG 0x02 /* chunk contains rx completion */ +#define UATH_CFLAGS_DEBUG 0x04 /* for debugging */ + uint16_t length; /* chunk size in bytes */ + /* chunk data follows */ +} __packed; + +#define UATH_RX_DUMMYSIZE 4 + +/* + * Message format for a WDCMSG_DATA_AVAIL message from Target to Host. + */ +struct uath_rx_desc { + uint32_t len; /* msg length including header */ + uint32_t code; /* WDCMSG_DATA_AVAIL */ + uint32_t gennum; /* generation number */ + uint32_t status; /* start of RECEIVE_INFO */ +#define UATH_STATUS_OK 0 +#define UATH_STATUS_STOP_IN_PROGRESS 1 +#define UATH_STATUS_CRC_ERR 2 +#define UATH_STATUS_PHY_ERR 3 +#define UATH_STATUS_DECRYPT_CRC_ERR 4 +#define UATH_STATUS_DECRYPT_MIC_ERR 5 +#define UATH_STATUS_DECOMP_ERR 6 +#define UATH_STATUS_KEY_ERR 7 +#define UATH_STATUS_ERR 8 + uint32_t tstamp_low; /* low-order 32-bits of rx timestamp */ + uint32_t tstamp_high; /* high-order 32-bits of rx timestamp */ + uint32_t framelen; /* frame length */ + uint32_t rate; /* rx rate code */ + uint32_t antenna; + int32_t rssi; + uint32_t channel; + uint32_t phyerror; + uint32_t connix; /* key table ix for bss traffic */ + uint32_t decrypterror; + uint32_t keycachemiss; + uint32_t pad; /* XXX? */ +} __packed; + +struct uath_tx_desc { + uint32_t msglen; + uint32_t msgid; /* msg id (supplied by host) */ + uint32_t type; /* opcode: WDMSG_SEND or WDCMSG_FLUSH */ + uint32_t txqid; /* tx queue id and flags */ +#define UATH_TXQID_MASK 0x0f +#define UATH_TXQID_MINRATE 0x10 /* use min tx rate */ +#define UATH_TXQID_FF 0x20 /* content is fast frame */ + uint32_t connid; /* tx connection id */ +#define UATH_ID_INVALID 0xffffffff /* for sending prior to connection */ + uint32_t flags; /* non-zero if response desired */ +#define UATH_TX_NOTIFY (1 << 24) /* f/w will send a UATH_NOTIF_TX */ + uint32_t buflen; /* payload length */ +} __packed; + +struct uath_cmd_host_available { + uint32_t sw_ver_major; + uint32_t sw_ver_minor; + uint32_t sw_ver_patch; + uint32_t sw_ver_build; +} __packed; +#define ATH_SW_VER_MAJOR 1 +#define ATH_SW_VER_MINOR 5 +#define ATH_SW_VER_PATCH 0 +#define ATH_SW_VER_BUILD 9999 + +struct uath_cmd_bind { + uint32_t targethandle; + uint32_t hostapiversion; +} __packed; + +/* structure for command WDCMSG_RESET */ +struct uath_cmd_reset { + uint32_t flags; /* channel flags */ +#define UATH_CHAN_TURBO 0x0100 +#define UATH_CHAN_CCK 0x0200 +#define UATH_CHAN_OFDM 0x0400 +#define UATH_CHAN_2GHZ 0x1000 +#define UATH_CHAN_5GHZ 0x2000 + uint32_t freq; /* channel frequency */ + uint32_t maxrdpower; + uint32_t cfgctl; + uint32_t twiceantennareduction; + uint32_t channelchange; + uint32_t keeprccontent; +} __packed; + +/* structure for commands UATH_CMD_READ_MAC and UATH_CMD_READ_EEPROM */ +struct uath_read_mac { + uint32_t len; + uint8_t data[32]; +} __packed; + +/* structure for command UATH_CMD_WRITE_MAC */ +struct uath_write_mac { + uint32_t reg; + uint32_t len; + uint8_t data[32]; +} __packed; + +/* structure for command UATH_CMD_STA_JOIN */ +struct uath_cmd_join_bss { + uint32_t bssid; /* NB: use zero */ + uint32_t bssmac[2]; /* bssid mac address */ + uint32_t bsstype; + uint32_t wlanmode; + uint32_t beaconinterval; + uint32_t dtiminterval; + uint32_t cfpinterval; + uint32_t atimwindow; + uint32_t defaultrateix; + uint32_t shortslottime11g; + uint32_t sleepduration; + uint32_t bmissthreshold; + uint32_t tcppowerlimit; + uint32_t quietduration; + uint32_t quietoffset; + uint32_t quietackctsallow; + uint32_t bssdefaultkey; /* XXX? */ +} __packed; + +struct uath_cmd_assoc_bss { + uint32_t bssid; + uint32_t associd; +} __packed; + +struct uath_cmd_start_bss { + uint32_t bssid; +} __packed; + +/* structure for command UATH_CMD_0C */ +struct uath_cmd_0c { + uint32_t magic1; + uint32_t magic2; + uint32_t magic3; +} __packed; + +struct uath_cmd_ledsteady { /* WDCMSG_SET_LED_STEADY */ + uint32_t lednum; +#define UATH_LED_LINK 0 +#define UATH_LED_ACTIVITY 1 + uint32_t ledmode; +#define UATH_LED_OFF 0 +#define UATH_LED_ON 1 +} __packed; + +struct uath_cmd_ledblink { /* WDCMSG_SET_LED_BLINK */ + uint32_t lednum; + uint32_t ledmode; + uint32_t blinkrate; + uint32_t slowmode; +} __packed; + +struct uath_cmd_ledstate { /* WDCMSG_SET_LED_STATE */ + uint32_t connected; +} __packed; + +struct uath_connkey_rec { + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint32_t keyiv; + uint32_t extkeyiv; + uint16_t keyflags; + uint16_t keylen; + uint16_t keytype; /* WEP, TKIP or AES */ + /* As far as I know, MIPS 4Kp is 32-bit processor */ + uint32_t priv; + uint8_t keyval[32]; + uint16_t aes_keylen; + uint8_t aes_keyval[16]; + uint8_t mic_txkeyval[8]; + uint8_t mic_rxkeyval[8]; + int64_t keyrsc[17]; + int32_t keytsc[17]; + int32_t keyexttsc[17]; +} __packed; + +/* structure for command UATH_CMD_CRYPTO */ +struct uath_cmd_crypto { + uint32_t keyidx; +#define UATH_DEFAULT_KEY 6 + uint32_t xorkey; + uint32_t size; + struct uath_connkey_rec rec; +} __packed; + +struct uath_cmd_rateset { + uint8_t length; +#define UATH_MAX_NRATES 32 + uint8_t set[UATH_MAX_NRATES]; +}; + +/* structure for command WDCMSG_SET_BASIC_RATE */ +struct uath_cmd_rates { + uint32_t connid; + uint32_t keeprccontent; + uint32_t size; + struct uath_cmd_rateset rateset; +} __packed; + +enum { + WLAN_MODE_NONE = 0, + WLAN_MODE_11b, + WLAN_MODE_11a, + WLAN_MODE_11g, + WLAN_MODE_11a_TURBO, + WLAN_MODE_11g_TURBO, + WLAN_MODE_11a_TURBO_PRIME, + WLAN_MODE_11g_TURBO_PRIME, + WLAN_MODE_11a_XR, + WLAN_MODE_11g_XR, +}; + +struct uath_cmd_connection_attr { + uint32_t longpreambleonly; + struct uath_cmd_rateset rateset; + uint32_t wlanmode; +} __packed; + +/* structure for command WDCMSG_CREATE_CONNECTION */ +struct uath_cmd_create_connection { + uint32_t connid; + uint32_t bssid; + uint32_t size; + struct uath_cmd_connection_attr connattr; +} __packed; + +struct uath_cmd_txq_setparams { /* WDCMSG_SET_TX_QUEUE_PARAMS */ + uint32_t qnum; + uint32_t aifs; + uint32_t logcwmin; + uint32_t logcwmax; + uint32_t bursttime; + uint32_t qflags; +} __packed; + +struct uath_cmd_txq_attr { + uint32_t priority; + uint32_t aifs; + uint32_t logcwmin; + uint32_t logcwmax; + uint32_t bursttime; + uint32_t mode; + uint32_t qflags; +} __packed; + +struct uath_cmd_txq_setup { /* WDCMSG_SETUP_TX_QUEUE */ + uint32_t qid; + uint32_t len; + struct uath_cmd_txq_attr attr; +} __packed; + +struct uath_cmd_stoptxdma { /* WDCMSG_STOP_TX_DMA */ + uint32_t qnum; + uint32_t msec; +} __packed; + +/* structure for command UATH_CMD_31 */ +struct uath_cmd_31 { + uint32_t magic1; + uint32_t magic2; +} __packed; + +struct uath_cmd_rx_filter { /* WDCMSG_RX_FILTER */ + uint32_t bits; +#define UATH_FILTER_RX_UCAST 0x00000001 +#define UATH_FILTER_RX_MCAST 0x00000002 +#define UATH_FILTER_RX_BCAST 0x00000004 +#define UATH_FILTER_RX_CONTROL 0x00000008 +#define UATH_FILTER_RX_BEACON 0x00000010 /* beacon frames */ +#define UATH_FILTER_RX_PROM 0x00000020 /* promiscuous mode */ +#define UATH_FILTER_RX_PHY_ERR 0x00000040 /* phy errors */ +#define UATH_FILTER_RX_PHY_RADAR 0x00000080 /* radar phy errors */ +#define UATH_FILTER_RX_XR_POOL 0x00000400 /* XR group polls */ +#define UATH_FILTER_RX_PROBE_REQ 0x00000800 + uint32_t op; +#define UATH_FILTER_OP_INIT 0x0 +#define UATH_FILTER_OP_SET 0x1 +#define UATH_FILTER_OP_CLEAR 0x2 +#define UATH_FILTER_OP_TEMP 0x3 +#define UATH_FILTER_OP_RESTORE 0x4 +} __packed; + +struct uath_cmd_rx_mcast_filter { /* WDCMSG_SET_RX_MCAST_FILTER */ + uint32_t filter0; + uint32_t filter1; +} __packed; + +struct uath_cmd_set_associd { /* WDCMSG_WRITE_ASSOCID */ + uint32_t defaultrateix; + uint32_t associd; + uint32_t timoffset; + uint32_t turboprime; + uint32_t bssid[2]; +} __packed; + +struct uath_cmd_set_stabeacon_timers { /* WDCMSG_SET_STA_BEACON_TIMERS */ + uint32_t nexttbtt; + uint32_t nextdtim; + uint32_t nextcfp; + uint32_t beaconperiod; + uint32_t dtimperiod; + uint32_t cfpperiod; + uint32_t cfpduration; + uint32_t sleepduration; + uint32_t bsmissthreshold; +} __packed; + +enum { + CFG_NONE, /* Sentinal to indicate "no config" */ + CFG_REG_DOMAIN, /* Regulatory Domain */ + CFG_RATE_CONTROL_ENABLE, + CFG_DEF_XMIT_DATA_RATE, /* NB: if rate control is not enabled */ + CFG_HW_TX_RETRIES, + CFG_SW_TX_RETRIES, + CFG_SLOW_CLOCK_ENABLE, + CFG_COMP_PROC, + CFG_USER_RTS_THRESHOLD, + CFG_XR2NORM_RATE_THRESHOLD, + CFG_XRMODE_SWITCH_COUNT, + CFG_PROTECTION_TYPE, + CFG_BURST_SEQ_THRESHOLD, + CFG_ABOLT, + CFG_IQ_LOG_COUNT_MAX, + CFG_MODE_CTS, + CFG_WME_ENABLED, + CFG_GPRS_CBR_PERIOD, + CFG_SERVICE_TYPE, + /* MAC Address to use. Overrides EEPROM */ + CFG_MAC_ADDR, + CFG_DEBUG_EAR, + CFG_INIT_REGS, + /* An ID for use in error & debug messages */ + CFG_DEBUG_ID, + CFG_COMP_WIN_SZ, + CFG_DIVERSITY_CTL, + CFG_TP_SCALE, + CFG_TPC_HALF_DBM5, + CFG_TPC_HALF_DBM2, + CFG_OVERRD_TX_POWER, + CFG_USE_32KHZ_CLOCK, + CFG_GMODE_PROTECTION, + CFG_GMODE_PROTECT_RATE_INDEX, + CFG_GMODE_NON_ERP_PREAMBLE, + CFG_WDC_TRANSPORT_CHUNK_SIZE, +}; + +enum { + /* Sentinal to indicate "no capability" */ + CAP_NONE, + CAP_ALL, /* ALL capabilities */ + CAP_TARGET_VERSION, + CAP_TARGET_REVISION, + CAP_MAC_VERSION, + CAP_MAC_REVISION, + CAP_PHY_REVISION, + CAP_ANALOG_5GHz_REVISION, + CAP_ANALOG_2GHz_REVISION, + /* Target supports WDC message debug features */ + CAP_DEBUG_WDCMSG_SUPPORT, + + CAP_REG_DOMAIN, + CAP_COUNTRY_CODE, + CAP_REG_CAP_BITS, + + CAP_WIRELESS_MODES, + CAP_CHAN_SPREAD_SUPPORT, + CAP_SLEEP_AFTER_BEACON_BROKEN, + CAP_COMPRESS_SUPPORT, + CAP_BURST_SUPPORT, + CAP_FAST_FRAMES_SUPPORT, + CAP_CHAP_TUNING_SUPPORT, + CAP_TURBOG_SUPPORT, + CAP_TURBO_PRIME_SUPPORT, + CAP_DEVICE_TYPE, + CAP_XR_SUPPORT, + CAP_WME_SUPPORT, + CAP_TOTAL_QUEUES, + CAP_CONNECTION_ID_MAX, /* Should absorb CAP_KEY_CACHE_SIZE */ + + CAP_LOW_5GHZ_CHAN, + CAP_HIGH_5GHZ_CHAN, + CAP_LOW_2GHZ_CHAN, + CAP_HIGH_2GHZ_CHAN, + + CAP_MIC_AES_CCM, + CAP_MIC_CKIP, + CAP_MIC_TKIP, + CAP_MIC_TKIP_WME, + CAP_CIPHER_AES_CCM, + CAP_CIPHER_CKIP, + CAP_CIPHER_TKIP, + + CAP_TWICE_ANTENNAGAIN_5G, + CAP_TWICE_ANTENNAGAIN_2G, +}; + +enum { + ST_NONE, /* Sentinal to indicate "no status" */ + ST_ALL, + ST_SERVICE_TYPE, + ST_WLAN_MODE, + ST_FREQ, + ST_BAND, + ST_LAST_RSSI, + ST_PS_FRAMES_DROPPED, + ST_CACHED_DEF_ANT, + ST_COUNT_OTHER_RX_ANT, + ST_USE_FAST_DIVERSITY, + ST_MAC_ADDR, + ST_RX_GENERATION_NUM, + ST_TX_QUEUE_DEPTH, + ST_SERIAL_NUMBER, + ST_WDC_TRANSPORT_CHUNK_SIZE, +}; + +enum { + BSS_ATTR_BEACON_INTERVAL, + BSS_ATTR_DTIM_INTERVAL, + BSS_ATTR_CFP_INTERVAL, + BSS_ATTR_CFP_MAX_DURATION, + BSS_ATTR_ATIM_WINDOW, + BSS_ATTR_DEFAULT_RATE_INDEX, + BSS_ATTR_SHORT_SLOT_TIME_11g, + BSS_ATTR_SLEEP_DURATION, + BSS_ATTR_BMISS_THRESHOLD, + BSS_ATTR_TPC_POWER_LIMIT, + BSS_ATTR_BSS_KEY_UPDATE, +}; + +struct uath_cmd_update_bss_attribute { + uint32_t bssid; + uint32_t attribute; /* BSS_ATTR_BEACON_INTERVAL, et al. */ + uint32_t cfgsize; /* should be zero 0 */ + uint32_t cfgdata; +}; + +struct uath_cmd_update_bss_attribute_key { + uint32_t bssid; + uint32_t attribute; /* BSS_ATTR_BSS_KEY_UPDATE */ + uint32_t cfgsize; /* size of remaining data */ + uint32_t bsskeyix; + uint32_t isdefaultkey; + uint32_t keyiv; /* IV generation control */ + uint32_t extkeyiv; /* extended IV for TKIP & CCM */ + uint32_t keyflags; + uint32_t keytype; + uint32_t initvalue; /* XXX */ + uint32_t keyval[4]; + uint32_t mictxkeyval[2]; + uint32_t micrxkeyval[2]; + uint32_t keyrsc[2]; +}; + +enum { + TARGET_DEVICE_AWAKE, + TARGET_DEVICE_SLEEP, + TARGET_DEVICE_PWRDN, + TARGET_DEVICE_PWRSAVE, + TARGET_DEVICE_SUSPEND, + TARGET_DEVICE_RESUME, +}; + +#define UATH_MAX_TXBUFSZ \ + (sizeof(struct uath_chunk) + sizeof(struct uath_tx_desc) + \ + IEEE80211_MAX_LEN) + +/* + * it's not easy to measure how the chunk is passed into the host if the target + * passed the multi-chunks so just we check a minimal size we can imagine. + */ +#define UATH_MIN_RXBUFSZ (sizeof(struct uath_chunk)) diff --git a/freebsd/sys/dev/usb/wlan/if_uathvar.h b/freebsd/sys/dev/usb/wlan/if_uathvar.h new file mode 100644 index 00000000..a38f54fc --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_uathvar.h @@ -0,0 +1,245 @@ +/* $OpenBSD: if_uathvar.h,v 1.3 2006/09/20 19:47:17 damien Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2006 + * Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2006 Sam Leffler, Errno Consulting + * Copyright (c) 2008-2009 Weongyo Jeong <weongyo@freebsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +enum { + UATH_INTR_RX, + UATH_INTR_TX, + UATH_BULK_RX, + UATH_BULK_TX, + UATH_N_XFERS = 4, +}; + +#define UATH_ID_BSS 2 /* Connection ID */ + +#define UATH_RX_DATA_LIST_COUNT 128 +#define UATH_TX_DATA_LIST_COUNT 16 +#define UATH_CMD_LIST_COUNT 60 + +#define UATH_DATA_TIMEOUT 10000 +#define UATH_CMD_TIMEOUT 1000 + +/* flags for sending firmware commands */ +#define UATH_CMD_FLAG_ASYNC (1 << 0) +#define UATH_CMD_FLAG_READ (1 << 1) +#define UATH_CMD_FLAG_MAGIC (1 << 2) + +struct uath_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + u_int64_t wr_tsf; + u_int8_t wr_flags; + u_int8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_antsignal; + int8_t wr_antnoise; + u_int8_t wr_antenna; +} __packed __aligned(8); + +#define UATH_RX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ + 0) + +struct uath_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed __aligned(8); + +#define UATH_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct uath_data { + struct uath_softc *sc; + uint8_t *buf; + uint16_t buflen; + struct mbuf *m; + struct ieee80211_node *ni; /* NB: tx only */ + STAILQ_ENTRY(uath_data) next; +}; +typedef STAILQ_HEAD(, uath_data) uath_datahead; + +struct uath_cmd { + struct uath_softc *sc; + uint32_t flags; + uint32_t msgid; + uint8_t *buf; + uint16_t buflen; + void *odata; /* NB: tx only */ + int olen; /* space in odata */ + STAILQ_ENTRY(uath_cmd) next; +}; +typedef STAILQ_HEAD(, uath_cmd) uath_cmdhead; + +struct uath_wme_settings { + uint8_t aifsn; + uint8_t logcwmin; + uint8_t logcwmax; + uint16_t txop; + uint8_t acm; +}; + +struct uath_devcap { + uint32_t targetVersion; + uint32_t targetRevision; + uint32_t macVersion; + uint32_t macRevision; + uint32_t phyRevision; + uint32_t analog5GhzRevision; + uint32_t analog2GhzRevision; + uint32_t regDomain; + uint32_t regCapBits; + uint32_t countryCode; + uint32_t keyCacheSize; + uint32_t numTxQueues; + uint32_t connectionIdMax; + uint32_t wirelessModes; +#define UATH_WIRELESS_MODE_11A 0x01 +#define UATH_WIRELESS_MODE_TURBO 0x02 +#define UATH_WIRELESS_MODE_11B 0x04 +#define UATH_WIRELESS_MODE_11G 0x08 +#define UATH_WIRELESS_MODE_108G 0x10 + uint32_t chanSpreadSupport; + uint32_t compressSupport; + uint32_t burstSupport; + uint32_t fastFramesSupport; + uint32_t chapTuningSupport; + uint32_t turboGSupport; + uint32_t turboPrimeSupport; + uint32_t deviceType; + uint32_t wmeSupport; + uint32_t low2GhzChan; + uint32_t high2GhzChan; + uint32_t low5GhzChan; + uint32_t high5GhzChan; + uint32_t supportCipherWEP; + uint32_t supportCipherAES_CCM; + uint32_t supportCipherTKIP; + uint32_t supportCipherMicAES_CCM; + uint32_t supportMicTKIP; + uint32_t twiceAntennaGain5G; + uint32_t twiceAntennaGain2G; +}; + +struct uath_stat { + uint32_t st_badchunkseqnum; + uint32_t st_invalidlen; + uint32_t st_multichunk; + uint32_t st_toobigrxpkt; + uint32_t st_stopinprogress; + uint32_t st_crcerr; + uint32_t st_phyerr; + uint32_t st_decrypt_crcerr; + uint32_t st_decrypt_micerr; + uint32_t st_decomperr; + uint32_t st_keyerr; + uint32_t st_err; + /* CMD/RX/TX queues */ + uint32_t st_cmd_active; + uint32_t st_cmd_inactive; + uint32_t st_cmd_pending; + uint32_t st_cmd_waiting; + uint32_t st_rx_active; + uint32_t st_rx_inactive; + uint32_t st_tx_active; + uint32_t st_tx_inactive; + uint32_t st_tx_pending; +}; +#define UATH_STAT_INC(sc, var) (sc)->sc_stat.var++ +#define UATH_STAT_DEC(sc, var) (sc)->sc_stat.var-- + +struct uath_vap { + struct ieee80211vap vap; + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define UATH_VAP(vap) ((struct uath_vap *)(vap)) + +struct uath_softc { + struct ieee80211com sc_ic; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + void *sc_cmd_dma_buf; + void *sc_tx_dma_buf; + struct mtx sc_mtx; + uint32_t sc_debug; + + struct uath_stat sc_stat; + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + + struct usb_xfer *sc_xfer[UATH_N_XFERS]; + struct uath_cmd sc_cmd[UATH_CMD_LIST_COUNT]; + uath_cmdhead sc_cmd_active; + uath_cmdhead sc_cmd_inactive; + uath_cmdhead sc_cmd_pending; + uath_cmdhead sc_cmd_waiting; + struct uath_data sc_rx[UATH_RX_DATA_LIST_COUNT]; + uath_datahead sc_rx_active; + uath_datahead sc_rx_inactive; + struct uath_data sc_tx[UATH_TX_DATA_LIST_COUNT]; + uath_datahead sc_tx_active; + uath_datahead sc_tx_inactive; + uath_datahead sc_tx_pending; + + uint32_t sc_msgid; + uint32_t sc_seqnum; + int sc_tx_timer; + struct callout watchdog_ch; + struct callout stat_ch; + /* multi-chunked support */ + struct mbuf *sc_intrx_head; + struct mbuf *sc_intrx_tail; + uint8_t sc_intrx_nextnum; + uint32_t sc_intrx_len; +#define UATH_MAX_INTRX_SIZE 3616 + + struct uath_devcap sc_devcap; + uint8_t sc_serial[16]; + + /* unsorted */ + uint32_t sc_flags; +#define UATH_FLAG_INVALID (1 << 1) +#define UATH_FLAG_INITDONE (1 << 2) + + struct uath_rx_radiotap_header sc_rxtap; + struct uath_tx_radiotap_header sc_txtap; +}; + +#define UATH_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define UATH_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define UATH_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) + +#define UATH_RESET_INTRX(sc) do { \ + (sc)->sc_intrx_head = NULL; \ + (sc)->sc_intrx_tail = NULL; \ + (sc)->sc_intrx_nextnum = 0; \ + (sc)->sc_intrx_len = 0; \ +} while (0) diff --git a/freebsd/sys/dev/usb/wlan/if_upgt.c b/freebsd/sys/dev/usb/wlan/if_upgt.c new file mode 100644 index 00000000..50acf468 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_upgt.c @@ -0,0 +1,2353 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $OpenBSD: if_upgt.c,v 1.35 2008/04/16 18:32:15 damien Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/endian.h> +#include <sys/firmware.h> +#include <sys/linker.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <sys/bus.h> +#include <machine/bus.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_phy.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> + +#include <net/bpf.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <rtems/bsd/local/usbdevs.h> + +#include <dev/usb/wlan/if_upgtvar.h> + +/* + * Driver for the USB PrismGT devices. + * + * For now just USB 2.0 devices with the GW3887 chipset are supported. + * The driver has been written based on the firmware version 2.13.1.0_LM87. + * + * TODO's: + * - MONITOR mode test. + * - Add HOSTAP mode. + * - Add IBSS mode. + * - Support the USB 1.0 devices (NET2280, ISL3880, ISL3886 chipsets). + * + * Parts of this driver has been influenced by reading the p54u driver + * written by Jean-Baptiste Note <jean-baptiste.note@m4x.org> and + * Sebastien Bourdeauducq <lekernel@prism54.org>. + */ + +static SYSCTL_NODE(_hw, OID_AUTO, upgt, CTLFLAG_RD, 0, + "USB PrismGT GW3887 driver parameters"); + +#ifdef UPGT_DEBUG +int upgt_debug = 0; +SYSCTL_INT(_hw_upgt, OID_AUTO, debug, CTLFLAG_RWTUN, &upgt_debug, + 0, "control debugging printfs"); +enum { + UPGT_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + UPGT_DEBUG_RECV = 0x00000002, /* basic recv operation */ + UPGT_DEBUG_RESET = 0x00000004, /* reset processing */ + UPGT_DEBUG_INTR = 0x00000008, /* INTR */ + UPGT_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */ + UPGT_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */ + UPGT_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */ + UPGT_DEBUG_STAT = 0x00000080, /* statistic */ + UPGT_DEBUG_FW = 0x00000100, /* firmware */ + UPGT_DEBUG_ANY = 0xffffffff +}; +#define DPRINTF(sc, m, fmt, ...) do { \ + if (sc->sc_debug & (m)) \ + printf(fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(sc, m, fmt, ...) do { \ + (void) sc; \ +} while (0) +#endif + +/* + * Prototypes. + */ +static device_probe_t upgt_match; +static device_attach_t upgt_attach; +static device_detach_t upgt_detach; +static int upgt_alloc_tx(struct upgt_softc *); +static int upgt_alloc_rx(struct upgt_softc *); +static int upgt_device_reset(struct upgt_softc *); +static void upgt_bulk_tx(struct upgt_softc *, struct upgt_data *); +static int upgt_fw_verify(struct upgt_softc *); +static int upgt_mem_init(struct upgt_softc *); +static int upgt_fw_load(struct upgt_softc *); +static int upgt_fw_copy(const uint8_t *, char *, int); +static uint32_t upgt_crc32_le(const void *, size_t); +static struct mbuf * + upgt_rxeof(struct usb_xfer *, struct upgt_data *, int *); +static struct mbuf * + upgt_rx(struct upgt_softc *, uint8_t *, int, int *); +static void upgt_txeof(struct usb_xfer *, struct upgt_data *); +static int upgt_eeprom_read(struct upgt_softc *); +static int upgt_eeprom_parse(struct upgt_softc *); +static void upgt_eeprom_parse_hwrx(struct upgt_softc *, uint8_t *); +static void upgt_eeprom_parse_freq3(struct upgt_softc *, uint8_t *, int); +static void upgt_eeprom_parse_freq4(struct upgt_softc *, uint8_t *, int); +static void upgt_eeprom_parse_freq6(struct upgt_softc *, uint8_t *, int); +static uint32_t upgt_chksum_le(const uint32_t *, size_t); +static void upgt_tx_done(struct upgt_softc *, uint8_t *); +static void upgt_init(struct upgt_softc *); +static void upgt_parent(struct ieee80211com *); +static int upgt_transmit(struct ieee80211com *, struct mbuf *); +static void upgt_start(struct upgt_softc *); +static int upgt_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void upgt_scan_start(struct ieee80211com *); +static void upgt_scan_end(struct ieee80211com *); +static void upgt_set_channel(struct ieee80211com *); +static struct ieee80211vap *upgt_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, int, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void upgt_vap_delete(struct ieee80211vap *); +static void upgt_update_mcast(struct ieee80211com *); +static uint8_t upgt_rx_rate(struct upgt_softc *, const int); +static void upgt_set_multi(void *); +static void upgt_stop(struct upgt_softc *); +static void upgt_setup_rates(struct ieee80211vap *, struct ieee80211com *); +static int upgt_set_macfilter(struct upgt_softc *, uint8_t); +static int upgt_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static void upgt_set_chan(struct upgt_softc *, struct ieee80211_channel *); +static void upgt_set_led(struct upgt_softc *, int); +static void upgt_set_led_blink(void *); +static void upgt_get_stats(struct upgt_softc *); +static void upgt_mem_free(struct upgt_softc *, uint32_t); +static uint32_t upgt_mem_alloc(struct upgt_softc *); +static void upgt_free_tx(struct upgt_softc *); +static void upgt_free_rx(struct upgt_softc *); +static void upgt_watchdog(void *); +static void upgt_abort_xfers(struct upgt_softc *); +static void upgt_abort_xfers_locked(struct upgt_softc *); +static void upgt_sysctl_node(struct upgt_softc *); +static struct upgt_data * + upgt_getbuf(struct upgt_softc *); +static struct upgt_data * + upgt_gettxbuf(struct upgt_softc *); +static int upgt_tx_start(struct upgt_softc *, struct mbuf *, + struct ieee80211_node *, struct upgt_data *); + +static const char *upgt_fwname = "upgt-gw3887"; + +static const STRUCT_USB_HOST_ID upgt_devs[] = { +#define UPGT_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + /* version 2 devices */ + UPGT_DEV(ACCTON, PRISM_GT), + UPGT_DEV(BELKIN, F5D7050), + UPGT_DEV(CISCOLINKSYS, WUSB54AG), + UPGT_DEV(CONCEPTRONIC, PRISM_GT), + UPGT_DEV(DELL, PRISM_GT_1), + UPGT_DEV(DELL, PRISM_GT_2), + UPGT_DEV(FSC, E5400), + UPGT_DEV(GLOBESPAN, PRISM_GT_1), + UPGT_DEV(GLOBESPAN, PRISM_GT_2), + UPGT_DEV(NETGEAR, WG111V1_2), + UPGT_DEV(INTERSIL, PRISM_GT), + UPGT_DEV(SMC, 2862WG), + UPGT_DEV(USR, USR5422), + UPGT_DEV(WISTRONNEWEB, UR045G), + UPGT_DEV(XYRATEX, PRISM_GT_1), + UPGT_DEV(XYRATEX, PRISM_GT_2), + UPGT_DEV(ZCOM, XG703A), + UPGT_DEV(ZCOM, XM142) +}; + +static usb_callback_t upgt_bulk_rx_callback; +static usb_callback_t upgt_bulk_tx_callback; + +static const struct usb_config upgt_config[UPGT_N_XFERS] = { + [UPGT_BULK_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MCLBYTES * UPGT_TX_MAXCOUNT, + .flags = { + .force_short_xfer = 1, + .pipe_bof = 1 + }, + .callback = upgt_bulk_tx_callback, + .timeout = UPGT_USB_TIMEOUT, /* ms */ + }, + [UPGT_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = MCLBYTES * UPGT_RX_MAXCOUNT, + .flags = { + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = upgt_bulk_rx_callback, + }, +}; + +static int +upgt_match(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != UPGT_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != UPGT_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(upgt_devs, sizeof(upgt_devs), uaa)); +} + +static int +upgt_attach(device_t dev) +{ + struct upgt_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + struct usb_attach_arg *uaa = device_get_ivars(dev); + uint8_t bands[IEEE80211_MODE_BYTES]; + uint8_t iface_index = UPGT_IFACE_INDEX; + int error; + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; +#ifdef UPGT_DEBUG + sc->sc_debug = upgt_debug; +#endif + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init(&sc->sc_led_ch, 0); + callout_init(&sc->sc_watchdog_ch, 0); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + upgt_config, UPGT_N_XFERS, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto fail1; + } + + sc->sc_rx_dma_buf = usbd_xfer_get_frame_buffer( + sc->sc_xfer[UPGT_BULK_RX], 0); + sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer( + sc->sc_xfer[UPGT_BULK_TX], 0); + + /* Setup TX and RX buffers */ + error = upgt_alloc_tx(sc); + if (error) + goto fail2; + error = upgt_alloc_rx(sc); + if (error) + goto fail3; + + /* Initialize the device. */ + error = upgt_device_reset(sc); + if (error) + goto fail4; + /* Verify the firmware. */ + error = upgt_fw_verify(sc); + if (error) + goto fail4; + /* Calculate device memory space. */ + if (sc->sc_memaddr_frame_start == 0 || sc->sc_memaddr_frame_end == 0) { + device_printf(dev, + "could not find memory space addresses on FW\n"); + error = EIO; + goto fail4; + } + sc->sc_memaddr_frame_end -= UPGT_MEMSIZE_RX + 1; + sc->sc_memaddr_rx_start = sc->sc_memaddr_frame_end + 1; + + DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame start=0x%08x\n", + sc->sc_memaddr_frame_start); + DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame end=0x%08x\n", + sc->sc_memaddr_frame_end); + DPRINTF(sc, UPGT_DEBUG_FW, "memory address rx start=0x%08x\n", + sc->sc_memaddr_rx_start); + + upgt_mem_init(sc); + + /* Load the firmware. */ + error = upgt_fw_load(sc); + if (error) + goto fail4; + + /* Read the whole EEPROM content and parse it. */ + error = upgt_eeprom_read(sc); + if (error) + goto fail4; + error = upgt_eeprom_parse(sc); + if (error) + goto fail4; + + /* all works related with the device have done here. */ + upgt_abort_xfers(sc); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(dev); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode */ + | IEEE80211_C_MONITOR /* monitor mode */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ + | IEEE80211_C_WPA /* 802.11i */ + ; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + ieee80211_init_channels(ic, NULL, bands); + + ieee80211_ifattach(ic); + ic->ic_raw_xmit = upgt_raw_xmit; + ic->ic_scan_start = upgt_scan_start; + ic->ic_scan_end = upgt_scan_end; + ic->ic_set_channel = upgt_set_channel; + ic->ic_vap_create = upgt_vap_create; + ic->ic_vap_delete = upgt_vap_delete; + ic->ic_update_mcast = upgt_update_mcast; + ic->ic_transmit = upgt_transmit; + ic->ic_parent = upgt_parent; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + UPGT_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + UPGT_RX_RADIOTAP_PRESENT); + + upgt_sysctl_node(sc); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +fail4: upgt_free_rx(sc); +fail3: upgt_free_tx(sc); +fail2: usbd_transfer_unsetup(sc->sc_xfer, UPGT_N_XFERS); +fail1: mtx_destroy(&sc->sc_mtx); + + return (error); +} + +static void +upgt_txeof(struct usb_xfer *xfer, struct upgt_data *data) +{ + + if (data->m) { + /* XXX status? */ + ieee80211_tx_complete(data->ni, data->m, 0); + data->m = NULL; + data->ni = NULL; + } +} + +static void +upgt_get_stats(struct upgt_softc *sc) +{ + struct upgt_data *data_cmd; + struct upgt_lmac_mem *mem; + struct upgt_lmac_stats *stats; + + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__); + return; + } + + /* + * Transmit the URB containing the CMD data. + */ + memset(data_cmd->buf, 0, MCLBYTES); + + mem = (struct upgt_lmac_mem *)data_cmd->buf; + mem->addr = htole32(sc->sc_memaddr_frame_start + + UPGT_MEMSIZE_FRAME_HEAD); + + stats = (struct upgt_lmac_stats *)(mem + 1); + + stats->header1.flags = 0; + stats->header1.type = UPGT_H1_TYPE_CTRL; + stats->header1.len = htole16( + sizeof(struct upgt_lmac_stats) - sizeof(struct upgt_lmac_header)); + + stats->header2.reqid = htole32(sc->sc_memaddr_frame_start); + stats->header2.type = htole16(UPGT_H2_TYPE_STATS); + stats->header2.flags = 0; + + data_cmd->buflen = sizeof(*mem) + sizeof(*stats); + + mem->chksum = upgt_chksum_le((uint32_t *)stats, + data_cmd->buflen - sizeof(*mem)); + + upgt_bulk_tx(sc, data_cmd); +} + +static void +upgt_parent(struct ieee80211com *ic) +{ + struct upgt_softc *sc = ic->ic_softc; + int startall = 0; + + UPGT_LOCK(sc); + if (sc->sc_flags & UPGT_FLAG_DETACHED) { + UPGT_UNLOCK(sc); + return; + } + if (ic->ic_nrunning > 0) { + if (sc->sc_flags & UPGT_FLAG_INITDONE) { + if (ic->ic_allmulti > 0 || ic->ic_promisc > 0) + upgt_set_multi(sc); + } else { + upgt_init(sc); + startall = 1; + } + } else if (sc->sc_flags & UPGT_FLAG_INITDONE) + upgt_stop(sc); + UPGT_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static void +upgt_stop(struct upgt_softc *sc) +{ + + UPGT_ASSERT_LOCKED(sc); + + if (sc->sc_flags & UPGT_FLAG_INITDONE) + upgt_set_macfilter(sc, IEEE80211_S_INIT); + upgt_abort_xfers_locked(sc); + /* device down */ + sc->sc_tx_timer = 0; + sc->sc_flags &= ~UPGT_FLAG_INITDONE; +} + +static void +upgt_set_led(struct upgt_softc *sc, int action) +{ + struct upgt_data *data_cmd; + struct upgt_lmac_mem *mem; + struct upgt_lmac_led *led; + + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__); + return; + } + + /* + * Transmit the URB containing the CMD data. + */ + memset(data_cmd->buf, 0, MCLBYTES); + + mem = (struct upgt_lmac_mem *)data_cmd->buf; + mem->addr = htole32(sc->sc_memaddr_frame_start + + UPGT_MEMSIZE_FRAME_HEAD); + + led = (struct upgt_lmac_led *)(mem + 1); + + led->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; + led->header1.type = UPGT_H1_TYPE_CTRL; + led->header1.len = htole16( + sizeof(struct upgt_lmac_led) - + sizeof(struct upgt_lmac_header)); + + led->header2.reqid = htole32(sc->sc_memaddr_frame_start); + led->header2.type = htole16(UPGT_H2_TYPE_LED); + led->header2.flags = 0; + + switch (action) { + case UPGT_LED_OFF: + led->mode = htole16(UPGT_LED_MODE_SET); + led->action_fix = 0; + led->action_tmp = htole16(UPGT_LED_ACTION_OFF); + led->action_tmp_dur = 0; + break; + case UPGT_LED_ON: + led->mode = htole16(UPGT_LED_MODE_SET); + led->action_fix = 0; + led->action_tmp = htole16(UPGT_LED_ACTION_ON); + led->action_tmp_dur = 0; + break; + case UPGT_LED_BLINK: + if (sc->sc_state != IEEE80211_S_RUN) { + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next); + return; + } + if (sc->sc_led_blink) { + /* previous blink was not finished */ + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next); + return; + } + led->mode = htole16(UPGT_LED_MODE_SET); + led->action_fix = htole16(UPGT_LED_ACTION_OFF); + led->action_tmp = htole16(UPGT_LED_ACTION_ON); + led->action_tmp_dur = htole16(UPGT_LED_ACTION_TMP_DUR); + /* lock blink */ + sc->sc_led_blink = 1; + callout_reset(&sc->sc_led_ch, hz, upgt_set_led_blink, sc); + break; + default: + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next); + return; + } + + data_cmd->buflen = sizeof(*mem) + sizeof(*led); + + mem->chksum = upgt_chksum_le((uint32_t *)led, + data_cmd->buflen - sizeof(*mem)); + + upgt_bulk_tx(sc, data_cmd); +} + +static void +upgt_set_led_blink(void *arg) +{ + struct upgt_softc *sc = arg; + + /* blink finished, we are ready for a next one */ + sc->sc_led_blink = 0; +} + +static void +upgt_init(struct upgt_softc *sc) +{ + + UPGT_ASSERT_LOCKED(sc); + + if (sc->sc_flags & UPGT_FLAG_INITDONE) + upgt_stop(sc); + + usbd_transfer_start(sc->sc_xfer[UPGT_BULK_RX]); + + (void)upgt_set_macfilter(sc, IEEE80211_S_SCAN); + + sc->sc_flags |= UPGT_FLAG_INITDONE; + + callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc); +} + +static int +upgt_set_macfilter(struct upgt_softc *sc, uint8_t state) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni; + struct upgt_data *data_cmd; + struct upgt_lmac_mem *mem; + struct upgt_lmac_filter *filter; + + UPGT_ASSERT_LOCKED(sc); + + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + device_printf(sc->sc_dev, "out of TX buffers.\n"); + return (ENOBUFS); + } + + /* + * Transmit the URB containing the CMD data. + */ + memset(data_cmd->buf, 0, MCLBYTES); + + mem = (struct upgt_lmac_mem *)data_cmd->buf; + mem->addr = htole32(sc->sc_memaddr_frame_start + + UPGT_MEMSIZE_FRAME_HEAD); + + filter = (struct upgt_lmac_filter *)(mem + 1); + + filter->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; + filter->header1.type = UPGT_H1_TYPE_CTRL; + filter->header1.len = htole16( + sizeof(struct upgt_lmac_filter) - + sizeof(struct upgt_lmac_header)); + + filter->header2.reqid = htole32(sc->sc_memaddr_frame_start); + filter->header2.type = htole16(UPGT_H2_TYPE_MACFILTER); + filter->header2.flags = 0; + + switch (state) { + case IEEE80211_S_INIT: + DPRINTF(sc, UPGT_DEBUG_STATE, "%s: set MAC filter to INIT\n", + __func__); + filter->type = htole16(UPGT_FILTER_TYPE_RESET); + break; + case IEEE80211_S_SCAN: + DPRINTF(sc, UPGT_DEBUG_STATE, + "set MAC filter to SCAN (bssid %s)\n", + ether_sprintf(ieee80211broadcastaddr)); + filter->type = htole16(UPGT_FILTER_TYPE_NONE); + IEEE80211_ADDR_COPY(filter->dst, + vap ? vap->iv_myaddr : ic->ic_macaddr); + IEEE80211_ADDR_COPY(filter->src, ieee80211broadcastaddr); + filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1); + filter->rxaddr = htole32(sc->sc_memaddr_rx_start); + filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2); + filter->rxhw = htole32(sc->sc_eeprom_hwrx); + filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3); + break; + case IEEE80211_S_RUN: + ni = ieee80211_ref_node(vap->iv_bss); + /* XXX monitor mode isn't tested yet. */ + if (vap->iv_opmode == IEEE80211_M_MONITOR) { + filter->type = htole16(UPGT_FILTER_TYPE_MONITOR); + IEEE80211_ADDR_COPY(filter->dst, + vap ? vap->iv_myaddr : ic->ic_macaddr); + IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid); + filter->unknown1 = htole16(UPGT_FILTER_MONITOR_UNKNOWN1); + filter->rxaddr = htole32(sc->sc_memaddr_rx_start); + filter->unknown2 = htole16(UPGT_FILTER_MONITOR_UNKNOWN2); + filter->rxhw = htole32(sc->sc_eeprom_hwrx); + filter->unknown3 = htole16(UPGT_FILTER_MONITOR_UNKNOWN3); + } else { + DPRINTF(sc, UPGT_DEBUG_STATE, + "set MAC filter to RUN (bssid %s)\n", + ether_sprintf(ni->ni_bssid)); + filter->type = htole16(UPGT_FILTER_TYPE_STA); + IEEE80211_ADDR_COPY(filter->dst, + vap ? vap->iv_myaddr : ic->ic_macaddr); + IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid); + filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1); + filter->rxaddr = htole32(sc->sc_memaddr_rx_start); + filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2); + filter->rxhw = htole32(sc->sc_eeprom_hwrx); + filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3); + } + ieee80211_free_node(ni); + break; + default: + device_printf(sc->sc_dev, + "MAC filter does not know that state\n"); + break; + } + + data_cmd->buflen = sizeof(*mem) + sizeof(*filter); + + mem->chksum = upgt_chksum_le((uint32_t *)filter, + data_cmd->buflen - sizeof(*mem)); + + upgt_bulk_tx(sc, data_cmd); + + return (0); +} + +static void +upgt_setup_rates(struct ieee80211vap *vap, struct ieee80211com *ic) +{ + struct upgt_softc *sc = ic->ic_softc; + const struct ieee80211_txparam *tp; + + /* + * 0x01 = OFMD6 0x10 = DS1 + * 0x04 = OFDM9 0x11 = DS2 + * 0x06 = OFDM12 0x12 = DS5 + * 0x07 = OFDM18 0x13 = DS11 + * 0x08 = OFDM24 + * 0x09 = OFDM36 + * 0x0a = OFDM48 + * 0x0b = OFDM54 + */ + const uint8_t rateset_auto_11b[] = + { 0x13, 0x13, 0x12, 0x11, 0x11, 0x10, 0x10, 0x10 }; + const uint8_t rateset_auto_11g[] = + { 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x04, 0x01 }; + const uint8_t rateset_fix_11bg[] = + { 0x10, 0x11, 0x12, 0x13, 0x01, 0x04, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b }; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + + /* XXX */ + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { + /* + * Automatic rate control is done by the device. + * We just pass the rateset from which the device + * will pickup a rate. + */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + memcpy(sc->sc_cur_rateset, rateset_auto_11b, + sizeof(sc->sc_cur_rateset)); + if (ic->ic_curmode == IEEE80211_MODE_11G || + ic->ic_curmode == IEEE80211_MODE_AUTO) + memcpy(sc->sc_cur_rateset, rateset_auto_11g, + sizeof(sc->sc_cur_rateset)); + } else { + /* set a fixed rate */ + memset(sc->sc_cur_rateset, rateset_fix_11bg[tp->ucastrate], + sizeof(sc->sc_cur_rateset)); + } +} + +static void +upgt_set_multi(void *arg) +{ + + /* XXX don't know how to set a device. Lack of docs. */ +} + +static int +upgt_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct upgt_softc *sc = ic->ic_softc; + int error; + + UPGT_LOCK(sc); + if ((sc->sc_flags & UPGT_FLAG_INITDONE) == 0) { + UPGT_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + UPGT_UNLOCK(sc); + return (error); + } + upgt_start(sc); + UPGT_UNLOCK(sc); + + return (0); +} + +static void +upgt_start(struct upgt_softc *sc) +{ + struct upgt_data *data_tx; + struct ieee80211_node *ni; + struct mbuf *m; + + UPGT_ASSERT_LOCKED(sc); + + if ((sc->sc_flags & UPGT_FLAG_INITDONE) == 0) + return; + + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + data_tx = upgt_gettxbuf(sc); + if (data_tx == NULL) { + mbufq_prepend(&sc->sc_snd, m); + break; + } + + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + + if (upgt_tx_start(sc, m, ni, data_tx) != 0) { + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, data_tx, next); + UPGT_STAT_INC(sc, st_tx_inactive); + ieee80211_free_node(ni); + continue; + } + sc->sc_tx_timer = 5; + } +} + +static int +upgt_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct upgt_softc *sc = ic->ic_softc; + struct upgt_data *data_tx = NULL; + + UPGT_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!(sc->sc_flags & UPGT_FLAG_INITDONE)) { + m_freem(m); + UPGT_UNLOCK(sc); + return ENETDOWN; + } + + data_tx = upgt_gettxbuf(sc); + if (data_tx == NULL) { + m_freem(m); + UPGT_UNLOCK(sc); + return (ENOBUFS); + } + + if (upgt_tx_start(sc, m, ni, data_tx) != 0) { + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, data_tx, next); + UPGT_STAT_INC(sc, st_tx_inactive); + UPGT_UNLOCK(sc); + return (EIO); + } + UPGT_UNLOCK(sc); + + sc->sc_tx_timer = 5; + return (0); +} + +static void +upgt_watchdog(void *arg) +{ + struct upgt_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + device_printf(sc->sc_dev, "watchdog timeout\n"); + /* upgt_init(sc); XXX needs a process context ? */ + counter_u64_add(ic->ic_oerrors, 1); + return; + } + callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc); + } +} + +static uint32_t +upgt_mem_alloc(struct upgt_softc *sc) +{ + int i; + + for (i = 0; i < sc->sc_memory.pages; i++) { + if (sc->sc_memory.page[i].used == 0) { + sc->sc_memory.page[i].used = 1; + return (sc->sc_memory.page[i].addr); + } + } + + return (0); +} + +static void +upgt_scan_start(struct ieee80211com *ic) +{ + /* do nothing. */ +} + +static void +upgt_scan_end(struct ieee80211com *ic) +{ + /* do nothing. */ +} + +static void +upgt_set_channel(struct ieee80211com *ic) +{ + struct upgt_softc *sc = ic->ic_softc; + + UPGT_LOCK(sc); + upgt_set_chan(sc, ic->ic_curchan); + UPGT_UNLOCK(sc); +} + +static void +upgt_set_chan(struct upgt_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct upgt_data *data_cmd; + struct upgt_lmac_mem *mem; + struct upgt_lmac_channel *chan; + int channel; + + UPGT_ASSERT_LOCKED(sc); + + channel = ieee80211_chan2ieee(ic, c); + if (channel == 0 || channel == IEEE80211_CHAN_ANY) { + /* XXX should NEVER happen */ + device_printf(sc->sc_dev, + "%s: invalid channel %x\n", __func__, channel); + return; + } + + DPRINTF(sc, UPGT_DEBUG_STATE, "%s: channel %d\n", __func__, channel); + + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__); + return; + } + /* + * Transmit the URB containing the CMD data. + */ + memset(data_cmd->buf, 0, MCLBYTES); + + mem = (struct upgt_lmac_mem *)data_cmd->buf; + mem->addr = htole32(sc->sc_memaddr_frame_start + + UPGT_MEMSIZE_FRAME_HEAD); + + chan = (struct upgt_lmac_channel *)(mem + 1); + + chan->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; + chan->header1.type = UPGT_H1_TYPE_CTRL; + chan->header1.len = htole16( + sizeof(struct upgt_lmac_channel) - sizeof(struct upgt_lmac_header)); + + chan->header2.reqid = htole32(sc->sc_memaddr_frame_start); + chan->header2.type = htole16(UPGT_H2_TYPE_CHANNEL); + chan->header2.flags = 0; + + chan->unknown1 = htole16(UPGT_CHANNEL_UNKNOWN1); + chan->unknown2 = htole16(UPGT_CHANNEL_UNKNOWN2); + chan->freq6 = sc->sc_eeprom_freq6[channel]; + chan->settings = sc->sc_eeprom_freq6_settings; + chan->unknown3 = UPGT_CHANNEL_UNKNOWN3; + + memcpy(chan->freq3_1, &sc->sc_eeprom_freq3[channel].data, + sizeof(chan->freq3_1)); + memcpy(chan->freq4, &sc->sc_eeprom_freq4[channel], + sizeof(sc->sc_eeprom_freq4[channel])); + memcpy(chan->freq3_2, &sc->sc_eeprom_freq3[channel].data, + sizeof(chan->freq3_2)); + + data_cmd->buflen = sizeof(*mem) + sizeof(*chan); + + mem->chksum = upgt_chksum_le((uint32_t *)chan, + data_cmd->buflen - sizeof(*mem)); + + upgt_bulk_tx(sc, data_cmd); +} + +static struct ieee80211vap * +upgt_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct upgt_vap *uvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + uvp = malloc(sizeof(struct upgt_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &uvp->vap; + /* enable s/w bmiss handling for sta mode */ + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(uvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + vap->iv_newstate = upgt_newstate; + + /* setup device rates */ + upgt_setup_rates(vap, ic); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return vap; +} + +static int +upgt_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct upgt_vap *uvp = UPGT_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct upgt_softc *sc = ic->ic_softc; + + /* do it in a process context */ + sc->sc_state = nstate; + + IEEE80211_UNLOCK(ic); + UPGT_LOCK(sc); + callout_stop(&sc->sc_led_ch); + callout_stop(&sc->sc_watchdog_ch); + + switch (nstate) { + case IEEE80211_S_INIT: + /* do not accept any frames if the device is down */ + (void)upgt_set_macfilter(sc, sc->sc_state); + upgt_set_led(sc, UPGT_LED_OFF); + break; + case IEEE80211_S_SCAN: + upgt_set_chan(sc, ic->ic_curchan); + break; + case IEEE80211_S_AUTH: + upgt_set_chan(sc, ic->ic_curchan); + break; + case IEEE80211_S_ASSOC: + break; + case IEEE80211_S_RUN: + upgt_set_macfilter(sc, sc->sc_state); + upgt_set_led(sc, UPGT_LED_ON); + break; + default: + break; + } + UPGT_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); +} + +static void +upgt_vap_delete(struct ieee80211vap *vap) +{ + struct upgt_vap *uvp = UPGT_VAP(vap); + + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static void +upgt_update_mcast(struct ieee80211com *ic) +{ + struct upgt_softc *sc = ic->ic_softc; + + upgt_set_multi(sc); +} + +static int +upgt_eeprom_parse(struct upgt_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct upgt_eeprom_header *eeprom_header; + struct upgt_eeprom_option *eeprom_option; + uint16_t option_len; + uint16_t option_type; + uint16_t preamble_len; + int option_end = 0; + + /* calculate eeprom options start offset */ + eeprom_header = (struct upgt_eeprom_header *)sc->sc_eeprom; + preamble_len = le16toh(eeprom_header->preamble_len); + eeprom_option = (struct upgt_eeprom_option *)(sc->sc_eeprom + + (sizeof(struct upgt_eeprom_header) + preamble_len)); + + while (!option_end) { + + /* sanity check */ + if (eeprom_option >= (struct upgt_eeprom_option *) + (sc->sc_eeprom + UPGT_EEPROM_SIZE)) { + return (EINVAL); + } + + /* the eeprom option length is stored in words */ + option_len = + (le16toh(eeprom_option->len) - 1) * sizeof(uint16_t); + option_type = + le16toh(eeprom_option->type); + + /* sanity check */ + if (option_len == 0 || option_len >= UPGT_EEPROM_SIZE) + return (EINVAL); + + switch (option_type) { + case UPGT_EEPROM_TYPE_NAME: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM name len=%d\n", option_len); + break; + case UPGT_EEPROM_TYPE_SERIAL: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM serial len=%d\n", option_len); + break; + case UPGT_EEPROM_TYPE_MAC: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM mac len=%d\n", option_len); + + IEEE80211_ADDR_COPY(ic->ic_macaddr, + eeprom_option->data); + break; + case UPGT_EEPROM_TYPE_HWRX: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM hwrx len=%d\n", option_len); + + upgt_eeprom_parse_hwrx(sc, eeprom_option->data); + break; + case UPGT_EEPROM_TYPE_CHIP: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM chip len=%d\n", option_len); + break; + case UPGT_EEPROM_TYPE_FREQ3: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM freq3 len=%d\n", option_len); + + upgt_eeprom_parse_freq3(sc, eeprom_option->data, + option_len); + break; + case UPGT_EEPROM_TYPE_FREQ4: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM freq4 len=%d\n", option_len); + + upgt_eeprom_parse_freq4(sc, eeprom_option->data, + option_len); + break; + case UPGT_EEPROM_TYPE_FREQ5: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM freq5 len=%d\n", option_len); + break; + case UPGT_EEPROM_TYPE_FREQ6: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM freq6 len=%d\n", option_len); + + upgt_eeprom_parse_freq6(sc, eeprom_option->data, + option_len); + break; + case UPGT_EEPROM_TYPE_END: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM end len=%d\n", option_len); + option_end = 1; + break; + case UPGT_EEPROM_TYPE_OFF: + DPRINTF(sc, UPGT_DEBUG_FW, + "%s: EEPROM off without end option\n", __func__); + return (EIO); + default: + DPRINTF(sc, UPGT_DEBUG_FW, + "EEPROM unknown type 0x%04x len=%d\n", + option_type, option_len); + break; + } + + /* jump to next EEPROM option */ + eeprom_option = (struct upgt_eeprom_option *) + (eeprom_option->data + option_len); + } + return (0); +} + +static void +upgt_eeprom_parse_freq3(struct upgt_softc *sc, uint8_t *data, int len) +{ + struct upgt_eeprom_freq3_header *freq3_header; + struct upgt_lmac_freq3 *freq3; + int i; + int elements; + int flags; + unsigned channel; + + freq3_header = (struct upgt_eeprom_freq3_header *)data; + freq3 = (struct upgt_lmac_freq3 *)(freq3_header + 1); + + flags = freq3_header->flags; + elements = freq3_header->elements; + + DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d\n", + flags, elements); + + if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq3[0]))) + return; + + for (i = 0; i < elements; i++) { + channel = ieee80211_mhz2ieee(le16toh(freq3[i].freq), 0); + if (channel >= IEEE80211_CHAN_MAX) + continue; + + sc->sc_eeprom_freq3[channel] = freq3[i]; + + DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", + le16toh(sc->sc_eeprom_freq3[channel].freq), channel); + } +} + +void +upgt_eeprom_parse_freq4(struct upgt_softc *sc, uint8_t *data, int len) +{ + struct upgt_eeprom_freq4_header *freq4_header; + struct upgt_eeprom_freq4_1 *freq4_1; + struct upgt_eeprom_freq4_2 *freq4_2; + int i; + int j; + int elements; + int settings; + int flags; + unsigned channel; + + freq4_header = (struct upgt_eeprom_freq4_header *)data; + freq4_1 = (struct upgt_eeprom_freq4_1 *)(freq4_header + 1); + flags = freq4_header->flags; + elements = freq4_header->elements; + settings = freq4_header->settings; + + /* we need this value later */ + sc->sc_eeprom_freq6_settings = freq4_header->settings; + + DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d settings=%d\n", + flags, elements, settings); + + if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq4_1[0]))) + return; + + for (i = 0; i < elements; i++) { + channel = ieee80211_mhz2ieee(le16toh(freq4_1[i].freq), 0); + if (channel >= IEEE80211_CHAN_MAX) + continue; + + freq4_2 = (struct upgt_eeprom_freq4_2 *)freq4_1[i].data; + for (j = 0; j < settings; j++) { + sc->sc_eeprom_freq4[channel][j].cmd = freq4_2[j]; + sc->sc_eeprom_freq4[channel][j].pad = 0; + } + + DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", + le16toh(freq4_1[i].freq), channel); + } +} + +void +upgt_eeprom_parse_freq6(struct upgt_softc *sc, uint8_t *data, int len) +{ + struct upgt_lmac_freq6 *freq6; + int i; + int elements; + unsigned channel; + + freq6 = (struct upgt_lmac_freq6 *)data; + elements = len / sizeof(struct upgt_lmac_freq6); + + DPRINTF(sc, UPGT_DEBUG_FW, "elements=%d\n", elements); + + if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq6[0]))) + return; + + for (i = 0; i < elements; i++) { + channel = ieee80211_mhz2ieee(le16toh(freq6[i].freq), 0); + if (channel >= IEEE80211_CHAN_MAX) + continue; + + sc->sc_eeprom_freq6[channel] = freq6[i]; + + DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", + le16toh(sc->sc_eeprom_freq6[channel].freq), channel); + } +} + +static void +upgt_eeprom_parse_hwrx(struct upgt_softc *sc, uint8_t *data) +{ + struct upgt_eeprom_option_hwrx *option_hwrx; + + option_hwrx = (struct upgt_eeprom_option_hwrx *)data; + + sc->sc_eeprom_hwrx = option_hwrx->rxfilter - UPGT_EEPROM_RX_CONST; + + DPRINTF(sc, UPGT_DEBUG_FW, "hwrx option value=0x%04x\n", + sc->sc_eeprom_hwrx); +} + +static int +upgt_eeprom_read(struct upgt_softc *sc) +{ + struct upgt_data *data_cmd; + struct upgt_lmac_mem *mem; + struct upgt_lmac_eeprom *eeprom; + int block, error, offset; + + UPGT_LOCK(sc); + usb_pause_mtx(&sc->sc_mtx, 100); + + offset = 0; + block = UPGT_EEPROM_BLOCK_SIZE; + while (offset < UPGT_EEPROM_SIZE) { + DPRINTF(sc, UPGT_DEBUG_FW, + "request EEPROM block (offset=%d, len=%d)\n", offset, block); + + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + UPGT_UNLOCK(sc); + return (ENOBUFS); + } + + /* + * Transmit the URB containing the CMD data. + */ + memset(data_cmd->buf, 0, MCLBYTES); + + mem = (struct upgt_lmac_mem *)data_cmd->buf; + mem->addr = htole32(sc->sc_memaddr_frame_start + + UPGT_MEMSIZE_FRAME_HEAD); + + eeprom = (struct upgt_lmac_eeprom *)(mem + 1); + eeprom->header1.flags = 0; + eeprom->header1.type = UPGT_H1_TYPE_CTRL; + eeprom->header1.len = htole16(( + sizeof(struct upgt_lmac_eeprom) - + sizeof(struct upgt_lmac_header)) + block); + + eeprom->header2.reqid = htole32(sc->sc_memaddr_frame_start); + eeprom->header2.type = htole16(UPGT_H2_TYPE_EEPROM); + eeprom->header2.flags = 0; + + eeprom->offset = htole16(offset); + eeprom->len = htole16(block); + + data_cmd->buflen = sizeof(*mem) + sizeof(*eeprom) + block; + + mem->chksum = upgt_chksum_le((uint32_t *)eeprom, + data_cmd->buflen - sizeof(*mem)); + upgt_bulk_tx(sc, data_cmd); + + error = mtx_sleep(sc, &sc->sc_mtx, 0, "eeprom_request", hz); + if (error != 0) { + device_printf(sc->sc_dev, + "timeout while waiting for EEPROM data\n"); + UPGT_UNLOCK(sc); + return (EIO); + } + + offset += block; + if (UPGT_EEPROM_SIZE - offset < block) + block = UPGT_EEPROM_SIZE - offset; + } + + UPGT_UNLOCK(sc); + return (0); +} + +/* + * When a rx data came in the function returns a mbuf and a rssi values. + */ +static struct mbuf * +upgt_rxeof(struct usb_xfer *xfer, struct upgt_data *data, int *rssi) +{ + struct mbuf *m = NULL; + struct upgt_softc *sc = usbd_xfer_softc(xfer); + struct upgt_lmac_header *header; + struct upgt_lmac_eeprom *eeprom; + uint8_t h1_type; + uint16_t h2_type; + int actlen, sumlen; + + usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + + UPGT_ASSERT_LOCKED(sc); + + if (actlen < 1) + return (NULL); + + /* Check only at the very beginning. */ + if (!(sc->sc_flags & UPGT_FLAG_FWLOADED) && + (memcmp(data->buf, "OK", 2) == 0)) { + sc->sc_flags |= UPGT_FLAG_FWLOADED; + wakeup_one(sc); + return (NULL); + } + + if (actlen < (int)UPGT_RX_MINSZ) + return (NULL); + + /* + * Check what type of frame came in. + */ + header = (struct upgt_lmac_header *)(data->buf + 4); + + h1_type = header->header1.type; + h2_type = le16toh(header->header2.type); + + if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_EEPROM) { + eeprom = (struct upgt_lmac_eeprom *)(data->buf + 4); + uint16_t eeprom_offset = le16toh(eeprom->offset); + uint16_t eeprom_len = le16toh(eeprom->len); + + DPRINTF(sc, UPGT_DEBUG_FW, + "received EEPROM block (offset=%d, len=%d)\n", + eeprom_offset, eeprom_len); + + memcpy(sc->sc_eeprom + eeprom_offset, + data->buf + sizeof(struct upgt_lmac_eeprom) + 4, + eeprom_len); + + /* EEPROM data has arrived in time, wakeup. */ + wakeup(sc); + } else if (h1_type == UPGT_H1_TYPE_CTRL && + h2_type == UPGT_H2_TYPE_TX_DONE) { + DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: received 802.11 TX done\n", + __func__); + upgt_tx_done(sc, data->buf + 4); + } else if (h1_type == UPGT_H1_TYPE_RX_DATA || + h1_type == UPGT_H1_TYPE_RX_DATA_MGMT) { + DPRINTF(sc, UPGT_DEBUG_RECV, "%s: received 802.11 RX data\n", + __func__); + m = upgt_rx(sc, data->buf + 4, le16toh(header->header1.len), + rssi); + } else if (h1_type == UPGT_H1_TYPE_CTRL && + h2_type == UPGT_H2_TYPE_STATS) { + DPRINTF(sc, UPGT_DEBUG_STAT, "%s: received statistic data\n", + __func__); + /* TODO: what could we do with the statistic data? */ + } else { + /* ignore unknown frame types */ + DPRINTF(sc, UPGT_DEBUG_INTR, + "received unknown frame type 0x%02x\n", + header->header1.type); + } + return (m); +} + +/* + * The firmware awaits a checksum for each frame we send to it. + * The algorithm used therefor is uncommon but somehow similar to CRC32. + */ +static uint32_t +upgt_chksum_le(const uint32_t *buf, size_t size) +{ + size_t i; + uint32_t crc = 0; + + for (i = 0; i < size; i += sizeof(uint32_t)) { + crc = htole32(crc ^ *buf++); + crc = htole32((crc >> 5) ^ (crc << 3)); + } + + return (crc); +} + +static struct mbuf * +upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen, int *rssi) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct upgt_lmac_rx_desc *rxdesc; + struct mbuf *m; + + /* + * don't pass packets to the ieee80211 framework if the driver isn't + * RUNNING. + */ + if (!(sc->sc_flags & UPGT_FLAG_INITDONE)) + return (NULL); + + /* access RX packet descriptor */ + rxdesc = (struct upgt_lmac_rx_desc *)data; + + /* create mbuf which is suitable for strict alignment archs */ + KASSERT((pkglen + ETHER_ALIGN) < MCLBYTES, + ("A current mbuf storage is small (%d)", pkglen + ETHER_ALIGN)); + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + device_printf(sc->sc_dev, "could not create RX mbuf\n"); + return (NULL); + } + m_adj(m, ETHER_ALIGN); + memcpy(mtod(m, char *), rxdesc->data, pkglen); + /* trim FCS */ + m->m_len = m->m_pkthdr.len = pkglen - IEEE80211_CRC_LEN; + + if (ieee80211_radiotap_active(ic)) { + struct upgt_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + tap->wr_rate = upgt_rx_rate(sc, rxdesc->rate); + tap->wr_antsignal = rxdesc->rssi; + } + + DPRINTF(sc, UPGT_DEBUG_RX_PROC, "%s: RX done\n", __func__); + *rssi = rxdesc->rssi; + return (m); +} + +static uint8_t +upgt_rx_rate(struct upgt_softc *sc, const int rate) +{ + struct ieee80211com *ic = &sc->sc_ic; + static const uint8_t cck_upgt2rate[4] = { 2, 4, 11, 22 }; + static const uint8_t ofdm_upgt2rate[12] = + { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; + + if (ic->ic_curmode == IEEE80211_MODE_11B && + !(rate < 0 || rate > 3)) + return cck_upgt2rate[rate & 0xf]; + + if (ic->ic_curmode == IEEE80211_MODE_11G && + !(rate < 0 || rate > 11)) + return ofdm_upgt2rate[rate & 0xf]; + + return (0); +} + +static void +upgt_tx_done(struct upgt_softc *sc, uint8_t *data) +{ + struct upgt_lmac_tx_done_desc *desc; + int i, freed = 0; + + UPGT_ASSERT_LOCKED(sc); + + desc = (struct upgt_lmac_tx_done_desc *)data; + + for (i = 0; i < UPGT_TX_MAXCOUNT; i++) { + struct upgt_data *data_tx = &sc->sc_tx_data[i]; + + if (data_tx->addr == le32toh(desc->header2.reqid)) { + upgt_mem_free(sc, data_tx->addr); + data_tx->ni = NULL; + data_tx->addr = 0; + data_tx->m = NULL; + + DPRINTF(sc, UPGT_DEBUG_TX_PROC, + "TX done: memaddr=0x%08x, status=0x%04x, rssi=%d, ", + le32toh(desc->header2.reqid), + le16toh(desc->status), le16toh(desc->rssi)); + DPRINTF(sc, UPGT_DEBUG_TX_PROC, "seq=%d\n", + le16toh(desc->seq)); + + freed++; + } + } + + if (freed != 0) { + UPGT_UNLOCK(sc); + sc->sc_tx_timer = 0; + upgt_start(sc); + UPGT_LOCK(sc); + } +} + +static void +upgt_mem_free(struct upgt_softc *sc, uint32_t addr) +{ + int i; + + for (i = 0; i < sc->sc_memory.pages; i++) { + if (sc->sc_memory.page[i].addr == addr) { + sc->sc_memory.page[i].used = 0; + return; + } + } + + device_printf(sc->sc_dev, + "could not free memory address 0x%08x\n", addr); +} + +static int +upgt_fw_load(struct upgt_softc *sc) +{ + const struct firmware *fw; + struct upgt_data *data_cmd; + struct upgt_fw_x2_header *x2; + char start_fwload_cmd[] = { 0x3c, 0x0d }; + int error = 0; + size_t offset; + int bsize; + int n; + uint32_t crc32; + + fw = firmware_get(upgt_fwname); + if (fw == NULL) { + device_printf(sc->sc_dev, "could not read microcode %s\n", + upgt_fwname); + return (EIO); + } + + UPGT_LOCK(sc); + + /* send firmware start load command */ + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + error = ENOBUFS; + goto fail; + } + data_cmd->buflen = sizeof(start_fwload_cmd); + memcpy(data_cmd->buf, start_fwload_cmd, data_cmd->buflen); + upgt_bulk_tx(sc, data_cmd); + + /* send X2 header */ + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + error = ENOBUFS; + goto fail; + } + data_cmd->buflen = sizeof(struct upgt_fw_x2_header); + x2 = (struct upgt_fw_x2_header *)data_cmd->buf; + memcpy(x2->signature, UPGT_X2_SIGNATURE, UPGT_X2_SIGNATURE_SIZE); + x2->startaddr = htole32(UPGT_MEMADDR_FIRMWARE_START); + x2->len = htole32(fw->datasize); + x2->crc = upgt_crc32_le((uint8_t *)data_cmd->buf + + UPGT_X2_SIGNATURE_SIZE, + sizeof(struct upgt_fw_x2_header) - UPGT_X2_SIGNATURE_SIZE - + sizeof(uint32_t)); + upgt_bulk_tx(sc, data_cmd); + + /* download firmware */ + for (offset = 0; offset < fw->datasize; offset += bsize) { + if (fw->datasize - offset > UPGT_FW_BLOCK_SIZE) + bsize = UPGT_FW_BLOCK_SIZE; + else + bsize = fw->datasize - offset; + + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + error = ENOBUFS; + goto fail; + } + n = upgt_fw_copy((const uint8_t *)fw->data + offset, + data_cmd->buf, bsize); + data_cmd->buflen = bsize; + upgt_bulk_tx(sc, data_cmd); + + DPRINTF(sc, UPGT_DEBUG_FW, "FW offset=%d, read=%d, sent=%d\n", + offset, n, bsize); + bsize = n; + } + DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware downloaded\n", __func__); + + /* load firmware */ + data_cmd = upgt_getbuf(sc); + if (data_cmd == NULL) { + error = ENOBUFS; + goto fail; + } + crc32 = upgt_crc32_le(fw->data, fw->datasize); + *((uint32_t *)(data_cmd->buf) ) = crc32; + *((uint8_t *)(data_cmd->buf) + 4) = 'g'; + *((uint8_t *)(data_cmd->buf) + 5) = '\r'; + data_cmd->buflen = 6; + upgt_bulk_tx(sc, data_cmd); + + /* waiting 'OK' response. */ + usbd_transfer_start(sc->sc_xfer[UPGT_BULK_RX]); + error = mtx_sleep(sc, &sc->sc_mtx, 0, "upgtfw", 2 * hz); + if (error != 0) { + device_printf(sc->sc_dev, "firmware load failed\n"); + error = EIO; + } + + DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware loaded\n", __func__); +fail: + UPGT_UNLOCK(sc); + firmware_put(fw, FIRMWARE_UNLOAD); + return (error); +} + +static uint32_t +upgt_crc32_le(const void *buf, size_t size) +{ + uint32_t crc; + + crc = ether_crc32_le(buf, size); + + /* apply final XOR value as common for CRC-32 */ + crc = htole32(crc ^ 0xffffffffU); + + return (crc); +} + +/* + * While copying the version 2 firmware, we need to replace two characters: + * + * 0x7e -> 0x7d 0x5e + * 0x7d -> 0x7d 0x5d + */ +static int +upgt_fw_copy(const uint8_t *src, char *dst, int size) +{ + int i, j; + + for (i = 0, j = 0; i < size && j < size; i++) { + switch (src[i]) { + case 0x7e: + dst[j] = 0x7d; + j++; + dst[j] = 0x5e; + j++; + break; + case 0x7d: + dst[j] = 0x7d; + j++; + dst[j] = 0x5d; + j++; + break; + default: + dst[j] = src[i]; + j++; + break; + } + } + + return (i); +} + +static int +upgt_mem_init(struct upgt_softc *sc) +{ + int i; + + for (i = 0; i < UPGT_MEMORY_MAX_PAGES; i++) { + sc->sc_memory.page[i].used = 0; + + if (i == 0) { + /* + * The first memory page is always reserved for + * command data. + */ + sc->sc_memory.page[i].addr = + sc->sc_memaddr_frame_start + MCLBYTES; + } else { + sc->sc_memory.page[i].addr = + sc->sc_memory.page[i - 1].addr + MCLBYTES; + } + + if (sc->sc_memory.page[i].addr + MCLBYTES >= + sc->sc_memaddr_frame_end) + break; + + DPRINTF(sc, UPGT_DEBUG_FW, "memory address page %d=0x%08x\n", + i, sc->sc_memory.page[i].addr); + } + + sc->sc_memory.pages = i; + + DPRINTF(sc, UPGT_DEBUG_FW, "memory pages=%d\n", sc->sc_memory.pages); + return (0); +} + +static int +upgt_fw_verify(struct upgt_softc *sc) +{ + const struct firmware *fw; + const struct upgt_fw_bra_option *bra_opt; + const struct upgt_fw_bra_descr *descr; + const uint8_t *p; + const uint32_t *uc; + uint32_t bra_option_type, bra_option_len; + size_t offset; + int bra_end = 0; + int error = 0; + + fw = firmware_get(upgt_fwname); + if (fw == NULL) { + device_printf(sc->sc_dev, "could not read microcode %s\n", + upgt_fwname); + return EIO; + } + + /* + * Seek to beginning of Boot Record Area (BRA). + */ + for (offset = 0; offset < fw->datasize; offset += sizeof(*uc)) { + uc = (const uint32_t *)((const uint8_t *)fw->data + offset); + if (*uc == 0) + break; + } + for (; offset < fw->datasize; offset += sizeof(*uc)) { + uc = (const uint32_t *)((const uint8_t *)fw->data + offset); + if (*uc != 0) + break; + } + if (offset == fw->datasize) { + device_printf(sc->sc_dev, + "firmware Boot Record Area not found\n"); + error = EIO; + goto fail; + } + + DPRINTF(sc, UPGT_DEBUG_FW, + "firmware Boot Record Area found at offset %d\n", offset); + + /* + * Parse Boot Record Area (BRA) options. + */ + while (offset < fw->datasize && bra_end == 0) { + /* get current BRA option */ + p = (const uint8_t *)fw->data + offset; + bra_opt = (const struct upgt_fw_bra_option *)p; + bra_option_type = le32toh(bra_opt->type); + bra_option_len = le32toh(bra_opt->len) * sizeof(*uc); + + switch (bra_option_type) { + case UPGT_BRA_TYPE_FW: + DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_FW len=%d\n", + bra_option_len); + + if (bra_option_len != UPGT_BRA_FWTYPE_SIZE) { + device_printf(sc->sc_dev, + "wrong UPGT_BRA_TYPE_FW len\n"); + error = EIO; + goto fail; + } + if (memcmp(UPGT_BRA_FWTYPE_LM86, bra_opt->data, + bra_option_len) == 0) { + sc->sc_fw_type = UPGT_FWTYPE_LM86; + break; + } + if (memcmp(UPGT_BRA_FWTYPE_LM87, bra_opt->data, + bra_option_len) == 0) { + sc->sc_fw_type = UPGT_FWTYPE_LM87; + break; + } + device_printf(sc->sc_dev, + "unsupported firmware type\n"); + error = EIO; + goto fail; + case UPGT_BRA_TYPE_VERSION: + DPRINTF(sc, UPGT_DEBUG_FW, + "UPGT_BRA_TYPE_VERSION len=%d\n", bra_option_len); + break; + case UPGT_BRA_TYPE_DEPIF: + DPRINTF(sc, UPGT_DEBUG_FW, + "UPGT_BRA_TYPE_DEPIF len=%d\n", bra_option_len); + break; + case UPGT_BRA_TYPE_EXPIF: + DPRINTF(sc, UPGT_DEBUG_FW, + "UPGT_BRA_TYPE_EXPIF len=%d\n", bra_option_len); + break; + case UPGT_BRA_TYPE_DESCR: + DPRINTF(sc, UPGT_DEBUG_FW, + "UPGT_BRA_TYPE_DESCR len=%d\n", bra_option_len); + + descr = (const struct upgt_fw_bra_descr *)bra_opt->data; + + sc->sc_memaddr_frame_start = + le32toh(descr->memaddr_space_start); + sc->sc_memaddr_frame_end = + le32toh(descr->memaddr_space_end); + + DPRINTF(sc, UPGT_DEBUG_FW, + "memory address space start=0x%08x\n", + sc->sc_memaddr_frame_start); + DPRINTF(sc, UPGT_DEBUG_FW, + "memory address space end=0x%08x\n", + sc->sc_memaddr_frame_end); + break; + case UPGT_BRA_TYPE_END: + DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_END len=%d\n", + bra_option_len); + bra_end = 1; + break; + default: + DPRINTF(sc, UPGT_DEBUG_FW, "unknown BRA option len=%d\n", + bra_option_len); + error = EIO; + goto fail; + } + + /* jump to next BRA option */ + offset += sizeof(struct upgt_fw_bra_option) + bra_option_len; + } + + DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware verified", __func__); +fail: + firmware_put(fw, FIRMWARE_UNLOAD); + return (error); +} + +static void +upgt_bulk_tx(struct upgt_softc *sc, struct upgt_data *data) +{ + + UPGT_ASSERT_LOCKED(sc); + + STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); + UPGT_STAT_INC(sc, st_tx_pending); + usbd_transfer_start(sc->sc_xfer[UPGT_BULK_TX]); +} + +static int +upgt_device_reset(struct upgt_softc *sc) +{ + struct upgt_data *data; + char init_cmd[] = { 0x7e, 0x7e, 0x7e, 0x7e }; + + UPGT_LOCK(sc); + + data = upgt_getbuf(sc); + if (data == NULL) { + UPGT_UNLOCK(sc); + return (ENOBUFS); + } + memcpy(data->buf, init_cmd, sizeof(init_cmd)); + data->buflen = sizeof(init_cmd); + upgt_bulk_tx(sc, data); + usb_pause_mtx(&sc->sc_mtx, 100); + + UPGT_UNLOCK(sc); + DPRINTF(sc, UPGT_DEBUG_FW, "%s: device initialized\n", __func__); + return (0); +} + +static int +upgt_alloc_tx(struct upgt_softc *sc) +{ + int i; + + STAILQ_INIT(&sc->sc_tx_active); + STAILQ_INIT(&sc->sc_tx_inactive); + STAILQ_INIT(&sc->sc_tx_pending); + + for (i = 0; i < UPGT_TX_MAXCOUNT; i++) { + struct upgt_data *data = &sc->sc_tx_data[i]; + data->buf = ((uint8_t *)sc->sc_tx_dma_buf) + (i * MCLBYTES); + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); + UPGT_STAT_INC(sc, st_tx_inactive); + } + + return (0); +} + +static int +upgt_alloc_rx(struct upgt_softc *sc) +{ + int i; + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + + for (i = 0; i < UPGT_RX_MAXCOUNT; i++) { + struct upgt_data *data = &sc->sc_rx_data[i]; + data->buf = ((uint8_t *)sc->sc_rx_dma_buf) + (i * MCLBYTES); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + } + return (0); +} + +static int +upgt_detach(device_t dev) +{ + struct upgt_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + unsigned int x; + + /* + * Prevent further allocations from RX/TX/CMD + * data lists and ioctls + */ + UPGT_LOCK(sc); + sc->sc_flags |= UPGT_FLAG_DETACHED; + + STAILQ_INIT(&sc->sc_tx_active); + STAILQ_INIT(&sc->sc_tx_inactive); + STAILQ_INIT(&sc->sc_tx_pending); + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + + upgt_stop(sc); + UPGT_UNLOCK(sc); + + callout_drain(&sc->sc_led_ch); + callout_drain(&sc->sc_watchdog_ch); + + /* drain USB transfers */ + for (x = 0; x != UPGT_N_XFERS; x++) + usbd_transfer_drain(sc->sc_xfer[x]); + + /* free data buffers */ + UPGT_LOCK(sc); + upgt_free_rx(sc); + upgt_free_tx(sc); + UPGT_UNLOCK(sc); + + /* free USB transfers and some data buffers */ + usbd_transfer_unsetup(sc->sc_xfer, UPGT_N_XFERS); + + ieee80211_ifdetach(ic); + mbufq_drain(&sc->sc_snd); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +upgt_free_rx(struct upgt_softc *sc) +{ + int i; + + for (i = 0; i < UPGT_RX_MAXCOUNT; i++) { + struct upgt_data *data = &sc->sc_rx_data[i]; + + data->buf = NULL; + data->ni = NULL; + } +} + +static void +upgt_free_tx(struct upgt_softc *sc) +{ + int i; + + for (i = 0; i < UPGT_TX_MAXCOUNT; i++) { + struct upgt_data *data = &sc->sc_tx_data[i]; + + if (data->ni != NULL) + ieee80211_free_node(data->ni); + + data->buf = NULL; + data->ni = NULL; + } +} + +static void +upgt_abort_xfers_locked(struct upgt_softc *sc) +{ + int i; + + UPGT_ASSERT_LOCKED(sc); + /* abort any pending transfers */ + for (i = 0; i < UPGT_N_XFERS; i++) + usbd_transfer_stop(sc->sc_xfer[i]); +} + +static void +upgt_abort_xfers(struct upgt_softc *sc) +{ + + UPGT_LOCK(sc); + upgt_abort_xfers_locked(sc); + UPGT_UNLOCK(sc); +} + +#define UPGT_SYSCTL_STAT_ADD32(c, h, n, p, d) \ + SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) + +static void +upgt_sysctl_node(struct upgt_softc *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *child; + struct sysctl_oid *tree; + struct upgt_stat *stats; + + stats = &sc->sc_stat; + ctx = device_get_sysctl_ctx(sc->sc_dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); + + tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, + NULL, "UPGT statistics"); + child = SYSCTL_CHILDREN(tree); + UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_active", + &stats->st_tx_active, "Active numbers in TX queue"); + UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_inactive", + &stats->st_tx_inactive, "Inactive numbers in TX queue"); + UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_pending", + &stats->st_tx_pending, "Pending numbers in TX queue"); +} + +#undef UPGT_SYSCTL_STAT_ADD32 + +static struct upgt_data * +_upgt_getbuf(struct upgt_softc *sc) +{ + struct upgt_data *bf; + + bf = STAILQ_FIRST(&sc->sc_tx_inactive); + if (bf != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); + UPGT_STAT_DEC(sc, st_tx_inactive); + } else + bf = NULL; + if (bf == NULL) + DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: %s\n", __func__, + "out of xmit buffers"); + return (bf); +} + +static struct upgt_data * +upgt_getbuf(struct upgt_softc *sc) +{ + struct upgt_data *bf; + + UPGT_ASSERT_LOCKED(sc); + + bf = _upgt_getbuf(sc); + if (bf == NULL) + DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: stop queue\n", __func__); + + return (bf); +} + +static struct upgt_data * +upgt_gettxbuf(struct upgt_softc *sc) +{ + struct upgt_data *bf; + + UPGT_ASSERT_LOCKED(sc); + + bf = upgt_getbuf(sc); + if (bf == NULL) + return (NULL); + + bf->addr = upgt_mem_alloc(sc); + if (bf->addr == 0) { + DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: no free prism memory!\n", + __func__); + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); + UPGT_STAT_INC(sc, st_tx_inactive); + return (NULL); + } + return (bf); +} + +static int +upgt_tx_start(struct upgt_softc *sc, struct mbuf *m, struct ieee80211_node *ni, + struct upgt_data *data) +{ + struct ieee80211vap *vap = ni->ni_vap; + int error = 0, len; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + struct upgt_lmac_mem *mem; + struct upgt_lmac_tx_desc *txdesc; + + UPGT_ASSERT_LOCKED(sc); + + upgt_set_led(sc, UPGT_LED_BLINK); + + /* + * Software crypto. + */ + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + device_printf(sc->sc_dev, + "ieee80211_crypto_encap returns NULL.\n"); + error = EIO; + goto done; + } + + /* in case packet header moved, reset pointer */ + wh = mtod(m, struct ieee80211_frame *); + } + + /* Transmit the URB containing the TX data. */ + memset(data->buf, 0, MCLBYTES); + mem = (struct upgt_lmac_mem *)data->buf; + mem->addr = htole32(data->addr); + txdesc = (struct upgt_lmac_tx_desc *)(mem + 1); + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_MGT) { + /* mgmt frames */ + txdesc->header1.flags = UPGT_H1_FLAGS_TX_MGMT; + /* always send mgmt frames at lowest rate (DS1) */ + memset(txdesc->rates, 0x10, sizeof(txdesc->rates)); + } else { + /* data frames */ + txdesc->header1.flags = UPGT_H1_FLAGS_TX_DATA; + memcpy(txdesc->rates, sc->sc_cur_rateset, sizeof(txdesc->rates)); + } + txdesc->header1.type = UPGT_H1_TYPE_TX_DATA; + txdesc->header1.len = htole16(m->m_pkthdr.len); + txdesc->header2.reqid = htole32(data->addr); + txdesc->header2.type = htole16(UPGT_H2_TYPE_TX_ACK_YES); + txdesc->header2.flags = htole16(UPGT_H2_FLAGS_TX_ACK_YES); + txdesc->type = htole32(UPGT_TX_DESC_TYPE_DATA); + txdesc->pad3[0] = UPGT_TX_DESC_PAD3_SIZE; + + if (ieee80211_radiotap_active_vap(vap)) { + struct upgt_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = 0; /* XXX where to get from? */ + + ieee80211_radiotap_tx(vap, m); + } + + /* copy frame below our TX descriptor header */ + m_copydata(m, 0, m->m_pkthdr.len, + data->buf + (sizeof(*mem) + sizeof(*txdesc))); + /* calculate frame size */ + len = sizeof(*mem) + sizeof(*txdesc) + m->m_pkthdr.len; + /* we need to align the frame to a 4 byte boundary */ + len = (len + 3) & ~3; + /* calculate frame checksum */ + mem->chksum = upgt_chksum_le((uint32_t *)txdesc, len - sizeof(*mem)); + data->ni = ni; + data->m = m; + data->buflen = len; + + DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: TX start data sending (%d bytes)\n", + __func__, len); + KASSERT(len <= MCLBYTES, ("mbuf is small for saving data")); + + upgt_bulk_tx(sc, data); +done: + /* + * If we don't regulary read the device statistics, the RX queue + * will stall. It's strange, but it works, so we keep reading + * the statistics here. *shrug* + */ + if (!(vap->iv_ifp->if_get_counter(vap->iv_ifp, IFCOUNTER_OPACKETS) % + UPGT_TX_STAT_INTERVAL)) + upgt_get_stats(sc); + + return (error); +} + +static void +upgt_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct upgt_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m = NULL; + struct upgt_data *data; + int8_t nf; + int rssi = -1; + + UPGT_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + m = upgt_rxeof(xfer, data, &rssi); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + data = STAILQ_FIRST(&sc->sc_rx_inactive); + if (data == NULL) + return; + STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); + usbd_xfer_set_frame_data(xfer, 0, data->buf, MCLBYTES); + usbd_transfer_submit(xfer); + + /* + * To avoid LOR we should unlock our private mutex here to call + * ieee80211_input() because here is at the end of a USB + * callback and safe to unlock. + */ + UPGT_UNLOCK(sc); + if (m != NULL) { + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, + (struct ieee80211_frame_min *)wh); + nf = -95; /* XXX */ + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi, nf); + /* node is no longer needed */ + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi, nf); + m = NULL; + } + UPGT_LOCK(sc); + upgt_start(sc); + break; + default: + /* needs it to the inactive queue due to a error. */ + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + counter_u64_add(ic->ic_ierrors, 1); + goto setup; + } + break; + } +} + +static void +upgt_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct upgt_softc *sc = usbd_xfer_softc(xfer); + struct upgt_data *data; + + UPGT_ASSERT_LOCKED(sc); + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_tx_active); + if (data == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); + UPGT_STAT_DEC(sc, st_tx_active); + upgt_txeof(xfer, data); + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); + UPGT_STAT_INC(sc, st_tx_inactive); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + data = STAILQ_FIRST(&sc->sc_tx_pending); + if (data == NULL) { + DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: empty pending queue\n", + __func__); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); + UPGT_STAT_DEC(sc, st_tx_pending); + STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); + UPGT_STAT_INC(sc, st_tx_active); + + usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); + usbd_transfer_submit(xfer); + upgt_start(sc); + break; + default: + data = STAILQ_FIRST(&sc->sc_tx_active); + if (data == NULL) + goto setup; + if (data->ni != NULL) { + if_inc_counter(data->ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + ieee80211_free_node(data->ni); + data->ni = NULL; + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto setup; + } + break; + } +} + +static device_method_t upgt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, upgt_match), + DEVMETHOD(device_attach, upgt_attach), + DEVMETHOD(device_detach, upgt_detach), + DEVMETHOD_END +}; + +static driver_t upgt_driver = { + .name = "upgt", + .methods = upgt_methods, + .size = sizeof(struct upgt_softc) +}; + +static devclass_t upgt_devclass; + +DRIVER_MODULE(if_upgt, uhub, upgt_driver, upgt_devclass, NULL, 0); +MODULE_VERSION(if_upgt, 1); +MODULE_DEPEND(if_upgt, usb, 1, 1, 1); +MODULE_DEPEND(if_upgt, wlan, 1, 1, 1); +MODULE_DEPEND(if_upgt, upgtfw_fw, 1, 1, 1); +USB_PNP_HOST_INFO(upgt_devs); diff --git a/freebsd/sys/dev/usb/wlan/if_upgtvar.h b/freebsd/sys/dev/usb/wlan/if_upgtvar.h new file mode 100644 index 00000000..ce996f6a --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_upgtvar.h @@ -0,0 +1,480 @@ +/* $OpenBSD: if_upgtvar.h,v 1.14 2008/02/02 13:48:44 mglocker Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct upgt_softc; + +/* + * General values. + */ +enum { + UPGT_BULK_RX, + UPGT_BULK_TX, + UPGT_N_XFERS = 2, +}; + +#define UPGT_CONFIG_INDEX 0 +#define UPGT_IFACE_INDEX 0 +#define UPGT_USB_TIMEOUT 1000 +#define UPGT_FIRMWARE_TIMEOUT 10 + +#define UPGT_MEMADDR_FIRMWARE_START 0x00020000 /* 512 bytes large */ +#define UPGT_MEMSIZE_FRAME_HEAD 0x0070 +#define UPGT_MEMSIZE_RX 0x3500 + +#define UPGT_RX_MAXCOUNT 6 +#define UPGT_TX_MAXCOUNT 128 +#define UPGT_TX_STAT_INTERVAL 5 +#define UPGT_RX_MINSZ (sizeof(struct upgt_lmac_header) + 4) + +/* device flags */ +#define UPGT_DEVICE_ATTACHED (1 << 0) + +/* leds */ +#define UPGT_LED_OFF 0 +#define UPGT_LED_ON 1 +#define UPGT_LED_BLINK 2 + +/* + * Firmware. + */ +#define UPGT_FW_BLOCK_SIZE 256 + +#define UPGT_BRA_FWTYPE_SIZE 4 +#define UPGT_BRA_FWTYPE_LM86 "LM86" +#define UPGT_BRA_FWTYPE_LM87 "LM87" +enum upgt_fw_type { + UPGT_FWTYPE_LM86, + UPGT_FWTYPE_LM87 +}; + +#define UPGT_BRA_TYPE_FW 0x80000001 +#define UPGT_BRA_TYPE_VERSION 0x80000002 +#define UPGT_BRA_TYPE_DEPIF 0x80000003 +#define UPGT_BRA_TYPE_EXPIF 0x80000004 +#define UPGT_BRA_TYPE_DESCR 0x80000101 +#define UPGT_BRA_TYPE_END 0xff0000ff +struct upgt_fw_bra_option { + uint32_t type; + uint32_t len; + uint8_t data[]; +} __packed; + +struct upgt_fw_bra_descr { + uint32_t unknown1; + uint32_t memaddr_space_start; + uint32_t memaddr_space_end; + uint32_t unknown2; + uint32_t unknown3; + uint8_t rates[20]; +} __packed; + +#define UPGT_X2_SIGNATURE_SIZE 4 +#define UPGT_X2_SIGNATURE "x2 " +struct upgt_fw_x2_header { + uint8_t signature[4]; + uint32_t startaddr; + uint32_t len; + uint32_t crc; +} __packed; + +/* + * EEPROM. + */ +#define UPGT_EEPROM_SIZE 8192 +#define UPGT_EEPROM_BLOCK_SIZE 1020 + +struct upgt_eeprom_header { + /* 14 bytes */ + uint32_t magic; + uint16_t pad1; + uint16_t preamble_len; + uint32_t pad2; + /* data */ +} __packed; + +#define UPGT_EEPROM_TYPE_END 0x0000 +#define UPGT_EEPROM_TYPE_NAME 0x0001 +#define UPGT_EEPROM_TYPE_SERIAL 0x0003 +#define UPGT_EEPROM_TYPE_MAC 0x0101 +#define UPGT_EEPROM_TYPE_HWRX 0x1001 +#define UPGT_EEPROM_TYPE_CHIP 0x1002 +#define UPGT_EEPROM_TYPE_FREQ3 0x1903 +#define UPGT_EEPROM_TYPE_FREQ4 0x1904 +#define UPGT_EEPROM_TYPE_FREQ5 0x1905 +#define UPGT_EEPROM_TYPE_FREQ6 0x1906 +#define UPGT_EEPROM_TYPE_OFF 0xffff +struct upgt_eeprom_option { + uint16_t len; + uint16_t type; + uint8_t data[]; + /* data */ +} __packed; + +#define UPGT_EEPROM_RX_CONST 0x88 +struct upgt_eeprom_option_hwrx { + uint32_t pad1; + uint8_t rxfilter; + uint8_t pad2[15]; +} __packed; + +struct upgt_eeprom_freq3_header { + uint8_t flags; + uint8_t elements; +} __packed; + +struct upgt_eeprom_freq4_header { + uint8_t flags; + uint8_t elements; + uint8_t settings; + uint8_t type; +} __packed; + +struct upgt_eeprom_freq4_1 { + uint16_t freq; + uint8_t data[50]; +} __packed; + +struct upgt_eeprom_freq4_2 { + uint16_t head; + uint8_t subtails[4]; + uint8_t tail; +} __packed; + +/* + * LMAC protocol. + */ +struct upgt_lmac_mem { + uint32_t addr; + uint32_t chksum; +} __packed; + +#define UPGT_H1_FLAGS_TX_MGMT 0x00 /* for TX: mgmt frame */ +#define UPGT_H1_FLAGS_TX_NO_CALLBACK 0x01 /* for TX: no USB callback */ +#define UPGT_H1_FLAGS_TX_DATA 0x10 /* for TX: data frame */ +#define UPGT_H1_TYPE_RX_DATA 0x00 /* 802.11 RX data frame */ +#define UPGT_H1_TYPE_RX_DATA_MGMT 0x04 /* 802.11 RX mgmt frame */ +#define UPGT_H1_TYPE_TX_DATA 0x40 /* 802.11 TX data frame */ +#define UPGT_H1_TYPE_CTRL 0x80 /* control frame */ +struct upgt_lmac_h1 { + /* 4 bytes */ + uint8_t flags; + uint8_t type; + uint16_t len; +} __packed; + +#define UPGT_H2_TYPE_TX_ACK_NO 0x0000 +#define UPGT_H2_TYPE_TX_ACK_YES 0x0001 +#define UPGT_H2_TYPE_MACFILTER 0x0000 +#define UPGT_H2_TYPE_CHANNEL 0x0001 +#define UPGT_H2_TYPE_TX_DONE 0x0008 +#define UPGT_H2_TYPE_STATS 0x000a +#define UPGT_H2_TYPE_EEPROM 0x000c +#define UPGT_H2_TYPE_LED 0x000d +#define UPGT_H2_FLAGS_TX_ACK_NO 0x0101 +#define UPGT_H2_FLAGS_TX_ACK_YES 0x0707 +struct upgt_lmac_h2 { + /* 8 bytes */ + uint32_t reqid; + uint16_t type; + uint16_t flags; +} __packed; + +struct upgt_lmac_header { + /* 12 bytes */ + struct upgt_lmac_h1 header1; + struct upgt_lmac_h2 header2; +} __packed; + +struct upgt_lmac_eeprom { + /* 16 bytes */ + struct upgt_lmac_h1 header1; + struct upgt_lmac_h2 header2; + uint16_t offset; + uint16_t len; + /* data */ +} __packed; + +#define UPGT_FILTER_TYPE_NONE 0x0000 +#define UPGT_FILTER_TYPE_STA 0x0001 +#define UPGT_FILTER_TYPE_IBSS 0x0002 +#define UPGT_FILTER_TYPE_HOSTAP 0x0004 +#define UPGT_FILTER_TYPE_MONITOR 0x0010 +#define UPGT_FILTER_TYPE_RESET 0x0020 +#define UPGT_FILTER_UNKNOWN1 0x0002 +#define UPGT_FILTER_UNKNOWN2 0x0ca8 +#define UPGT_FILTER_UNKNOWN3 0xffff +#define UPGT_FILTER_MONITOR_UNKNOWN1 0x0000 +#define UPGT_FILTER_MONITOR_UNKNOWN2 0x0000 +#define UPGT_FILTER_MONITOR_UNKNOWN3 0x0000 +struct upgt_lmac_filter { + struct upgt_lmac_h1 header1; + struct upgt_lmac_h2 header2; + /* 32 bytes */ + uint16_t type; + uint8_t dst[IEEE80211_ADDR_LEN]; + uint8_t src[IEEE80211_ADDR_LEN]; + uint16_t unknown1; + uint32_t rxaddr; + uint16_t unknown2; + uint32_t rxhw; + uint16_t unknown3; + uint32_t unknown4; +} __packed; + +/* frequence 3 data */ +struct upgt_lmac_freq3 { + uint16_t freq; + uint8_t data[6]; +} __packed; + +/* frequence 4 data */ +struct upgt_lmac_freq4 { + struct upgt_eeprom_freq4_2 cmd; + uint8_t pad; +}; + +/* frequence 6 data */ +struct upgt_lmac_freq6 { + uint16_t freq; + uint8_t data[8]; +} __packed; + +#define UPGT_CHANNEL_UNKNOWN1 0x0001 +#define UPGT_CHANNEL_UNKNOWN2 0x0000 +#define UPGT_CHANNEL_UNKNOWN3 0x48 +struct upgt_lmac_channel { + struct upgt_lmac_h1 header1; + struct upgt_lmac_h2 header2; + /* 112 bytes */ + uint16_t unknown1; + uint16_t unknown2; + uint8_t pad1[20]; + struct upgt_lmac_freq6 freq6; + uint8_t settings; + uint8_t unknown3; + uint8_t freq3_1[4]; + struct upgt_lmac_freq4 freq4[8]; + uint8_t freq3_2[4]; + uint32_t pad2; +} __packed; + +#define UPGT_LED_MODE_SET 0x0003 +#define UPGT_LED_ACTION_OFF 0x0002 +#define UPGT_LED_ACTION_ON 0x0003 +#define UPGT_LED_ACTION_TMP_DUR 100 /* ms */ +struct upgt_lmac_led { + struct upgt_lmac_h1 header1; + struct upgt_lmac_h2 header2; + uint16_t mode; + uint16_t action_fix; + uint16_t action_tmp; + uint16_t action_tmp_dur; +} __packed; + +struct upgt_lmac_stats { + struct upgt_lmac_h1 header1; + struct upgt_lmac_h2 header2; + uint8_t data[76]; +} __packed; + +struct upgt_lmac_rx_desc { + struct upgt_lmac_h1 header1; + /* 16 bytes */ + uint16_t freq; + uint8_t unknown1; + uint8_t rate; + uint8_t rssi; + uint8_t pad; + uint16_t unknown2; + uint32_t timestamp; + uint32_t unknown3; + uint8_t data[]; +} __packed; + +#define UPGT_TX_DESC_KEY_EXISTS 0x01 +struct upgt_lmac_tx_desc_wep { + uint8_t key_exists; + uint8_t key_len; + uint8_t key_val[16]; +} __packed; + +#define UPGT_TX_DESC_TYPE_BEACON 0x00000000 +#define UPGT_TX_DESC_TYPE_PROBE 0x00000001 +#define UPGT_TX_DESC_TYPE_MGMT 0x00000002 +#define UPGT_TX_DESC_TYPE_DATA 0x00000004 +#define UPGT_TX_DESC_PAD3_SIZE 2 +struct upgt_lmac_tx_desc { + struct upgt_lmac_h1 header1; + struct upgt_lmac_h2 header2; + uint8_t rates[8]; + uint16_t pad1; + struct upgt_lmac_tx_desc_wep wep_key; + uint32_t type; + uint32_t pad2; + uint32_t unknown1; + uint32_t unknown2; + uint8_t pad3[2]; + /* 802.11 frame data */ +} __packed; + +#define UPGT_TX_DONE_DESC_STATUS_OK 0x0001 +struct upgt_lmac_tx_done_desc { + struct upgt_lmac_h1 header1; + struct upgt_lmac_h2 header2; + uint16_t status; + uint16_t rssi; + uint16_t seq; + uint16_t unknown; +} __packed; + +/* + * USB xfers. + */ +struct upgt_data { + uint8_t *buf; + uint32_t buflen; + struct ieee80211_node *ni; + struct mbuf *m; + uint32_t addr; + STAILQ_ENTRY(upgt_data) next; +}; +typedef STAILQ_HEAD(, upgt_data) upgt_datahead; + +/* + * Prism memory. + */ +struct upgt_memory_page { + uint8_t used; + uint32_t addr; +} __packed; + +#define UPGT_MEMORY_MAX_PAGES 8 +struct upgt_memory { + uint8_t pages; + struct upgt_memory_page page[UPGT_MEMORY_MAX_PAGES]; +} __packed; + +/* + * BPF + */ +struct upgt_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_antsignal; +} __packed __aligned(8); + +#define UPGT_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) + +struct upgt_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed __aligned(8); + +#define UPGT_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct upgt_stat { + uint32_t st_tx_active; + uint32_t st_tx_inactive; + uint32_t st_tx_pending; +}; + +#define UPGT_STAT_INC(sc, var) (sc)->sc_stat.var++ +#define UPGT_STAT_DEC(sc, var) (sc)->sc_stat.var-- + +struct upgt_vap { + struct ieee80211vap vap; + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define UPGT_VAP(vap) ((struct upgt_vap *)(vap)) + +struct upgt_softc { + struct ieee80211com sc_ic; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + void *sc_rx_dma_buf; + void *sc_tx_dma_buf; + struct mtx sc_mtx; + struct upgt_stat sc_stat; + int sc_flags; +#define UPGT_FLAG_FWLOADED (1 << 0) +#define UPGT_FLAG_INITDONE (1 << 1) +#define UPGT_FLAG_DETACHED (1 << 2) + int sc_debug; + + enum ieee80211_state sc_state; + int sc_arg; + int sc_led_blink; + struct callout sc_led_ch; + uint8_t sc_cur_rateset[8]; + + /* watchdog */ + int sc_tx_timer; + struct callout sc_watchdog_ch; + + /* Firmware. */ + int sc_fw_type; + /* memory addresses on device */ + uint32_t sc_memaddr_frame_start; + uint32_t sc_memaddr_frame_end; + uint32_t sc_memaddr_rx_start; + struct upgt_memory sc_memory; + + /* data which we found in the EEPROM */ + uint8_t sc_eeprom[2 * UPGT_EEPROM_SIZE] __aligned(4); + uint16_t sc_eeprom_hwrx; + struct upgt_lmac_freq3 sc_eeprom_freq3[IEEE80211_CHAN_MAX]; + struct upgt_lmac_freq4 sc_eeprom_freq4[IEEE80211_CHAN_MAX][8]; + struct upgt_lmac_freq6 sc_eeprom_freq6[IEEE80211_CHAN_MAX]; + uint8_t sc_eeprom_freq6_settings; + + /* RX/TX */ + struct usb_xfer *sc_xfer[UPGT_N_XFERS]; + int sc_rx_no; + int sc_tx_no; + struct upgt_data sc_rx_data[UPGT_RX_MAXCOUNT]; + upgt_datahead sc_rx_active; + upgt_datahead sc_rx_inactive; + struct upgt_data sc_tx_data[UPGT_TX_MAXCOUNT]; + upgt_datahead sc_tx_active; + upgt_datahead sc_tx_inactive; + upgt_datahead sc_tx_pending; + + /* BPF */ + struct upgt_rx_radiotap_header sc_rxtap; + struct upgt_tx_radiotap_header sc_txtap; +}; + +#define UPGT_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define UPGT_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define UPGT_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) diff --git a/freebsd/sys/dev/usb/wlan/if_ural.c b/freebsd/sys/dev/usb/wlan/if_ural.c new file mode 100644 index 00000000..d6a199ff --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_ural.c @@ -0,0 +1,2246 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 + * Damien Bergamini <damien.bergamini@free.fr> + * + * Copyright (c) 2006, 2008 + * Hans Petter Selasky <hselasky@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2500USB chipset driver + * http://www.ralinktech.com/ + */ + +#include <rtems/bsd/sys/param.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/kdb.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_ratectl.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <rtems/bsd/local/usbdevs.h> + +#define USB_DEBUG_VAR ural_debug +#include <dev/usb/usb_debug.h> + +#include <dev/usb/wlan/if_uralreg.h> +#include <dev/usb/wlan/if_uralvar.h> + +#ifdef USB_DEBUG +static int ural_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural"); +SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RWTUN, &ural_debug, 0, + "Debug level"); +#endif + +#define URAL_RSSI(rssi) \ + ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ + ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) + +/* various supported device vendors/products */ +static const STRUCT_USB_HOST_ID ural_devs[] = { +#define URAL_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + URAL_DEV(ASUS, WL167G), + URAL_DEV(ASUS, RT2570), + URAL_DEV(BELKIN, F5D7050), + URAL_DEV(BELKIN, F5D7051), + URAL_DEV(CISCOLINKSYS, HU200TS), + URAL_DEV(CISCOLINKSYS, WUSB54G), + URAL_DEV(CISCOLINKSYS, WUSB54GP), + URAL_DEV(CONCEPTRONIC2, C54RU), + URAL_DEV(DLINK, DWLG122), + URAL_DEV(GIGABYTE, GN54G), + URAL_DEV(GIGABYTE, GNWBKG), + URAL_DEV(GUILLEMOT, HWGUSB254), + URAL_DEV(MELCO, KG54), + URAL_DEV(MELCO, KG54AI), + URAL_DEV(MELCO, KG54YB), + URAL_DEV(MELCO, NINWIFI), + URAL_DEV(MSI, RT2570), + URAL_DEV(MSI, RT2570_2), + URAL_DEV(MSI, RT2570_3), + URAL_DEV(NOVATECH, NV902), + URAL_DEV(RALINK, RT2570), + URAL_DEV(RALINK, RT2570_2), + URAL_DEV(RALINK, RT2570_3), + URAL_DEV(SIEMENS2, WL54G), + URAL_DEV(SMC, 2862WG), + URAL_DEV(SPHAIRON, UB801R), + URAL_DEV(SURECOM, RT2570), + URAL_DEV(VTECH, RT2570), + URAL_DEV(ZINWELL, RT2570), +#undef URAL_DEV +}; + +static usb_callback_t ural_bulk_read_callback; +static usb_callback_t ural_bulk_write_callback; + +static usb_error_t ural_do_request(struct ural_softc *sc, + struct usb_device_request *req, void *data); +static struct ieee80211vap *ural_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, + int, const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void ural_vap_delete(struct ieee80211vap *); +static void ural_tx_free(struct ural_tx_data *, int); +static void ural_setup_tx_list(struct ural_softc *); +static void ural_unsetup_tx_list(struct ural_softc *); +static int ural_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static void ural_setup_tx_desc(struct ural_softc *, + struct ural_tx_desc *, uint32_t, int, int); +static int ural_tx_bcn(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static int ural_tx_mgt(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static int ural_tx_data(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static int ural_transmit(struct ieee80211com *, struct mbuf *); +static void ural_start(struct ural_softc *); +static void ural_parent(struct ieee80211com *); +static void ural_set_testmode(struct ural_softc *); +static void ural_eeprom_read(struct ural_softc *, uint16_t, void *, + int); +static uint16_t ural_read(struct ural_softc *, uint16_t); +static void ural_read_multi(struct ural_softc *, uint16_t, void *, + int); +static void ural_write(struct ural_softc *, uint16_t, uint16_t); +static void ural_write_multi(struct ural_softc *, uint16_t, void *, + int) __unused; +static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t); +static uint8_t ural_bbp_read(struct ural_softc *, uint8_t); +static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t); +static void ural_scan_start(struct ieee80211com *); +static void ural_scan_end(struct ieee80211com *); +static void ural_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static void ural_set_channel(struct ieee80211com *); +static void ural_set_chan(struct ural_softc *, + struct ieee80211_channel *); +static void ural_disable_rf_tune(struct ural_softc *); +static void ural_enable_tsf_sync(struct ural_softc *); +static void ural_enable_tsf(struct ural_softc *); +static void ural_update_slot(struct ural_softc *); +static void ural_set_txpreamble(struct ural_softc *); +static void ural_set_basicrates(struct ural_softc *, + const struct ieee80211_channel *); +static void ural_set_bssid(struct ural_softc *, const uint8_t *); +static void ural_set_macaddr(struct ural_softc *, const uint8_t *); +static void ural_update_promisc(struct ieee80211com *); +static void ural_setpromisc(struct ural_softc *); +static const char *ural_get_rf(int); +static void ural_read_eeprom(struct ural_softc *); +static int ural_bbp_init(struct ural_softc *); +static void ural_set_txantenna(struct ural_softc *, int); +static void ural_set_rxantenna(struct ural_softc *, int); +static void ural_init(struct ural_softc *); +static void ural_stop(struct ural_softc *); +static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void ural_ratectl_start(struct ural_softc *, + struct ieee80211_node *); +static void ural_ratectl_timeout(void *); +static void ural_ratectl_task(void *, int); +static int ural_pause(struct ural_softc *sc, int timeout); + +/* + * Default values for MAC registers; values taken from the reference driver. + */ +static const struct { + uint16_t reg; + uint16_t val; +} ural_def_mac[] = { + { RAL_TXRX_CSR5, 0x8c8d }, + { RAL_TXRX_CSR6, 0x8b8a }, + { RAL_TXRX_CSR7, 0x8687 }, + { RAL_TXRX_CSR8, 0x0085 }, + { RAL_MAC_CSR13, 0x1111 }, + { RAL_MAC_CSR14, 0x1e11 }, + { RAL_TXRX_CSR21, 0xe78f }, + { RAL_MAC_CSR9, 0xff1d }, + { RAL_MAC_CSR11, 0x0002 }, + { RAL_MAC_CSR22, 0x0053 }, + { RAL_MAC_CSR15, 0x0000 }, + { RAL_MAC_CSR8, RAL_FRAME_SIZE }, + { RAL_TXRX_CSR19, 0x0000 }, + { RAL_TXRX_CSR18, 0x005a }, + { RAL_PHY_CSR2, 0x0000 }, + { RAL_TXRX_CSR0, 0x1ec0 }, + { RAL_PHY_CSR4, 0x000f } +}; + +/* + * Default values for BBP registers; values taken from the reference driver. + */ +static const struct { + uint8_t reg; + uint8_t val; +} ural_def_bbp[] = { + { 3, 0x02 }, + { 4, 0x19 }, + { 14, 0x1c }, + { 15, 0x30 }, + { 16, 0xac }, + { 17, 0x48 }, + { 18, 0x18 }, + { 19, 0xff }, + { 20, 0x1e }, + { 21, 0x08 }, + { 22, 0x08 }, + { 23, 0x08 }, + { 24, 0x80 }, + { 25, 0x50 }, + { 26, 0x08 }, + { 27, 0x23 }, + { 30, 0x10 }, + { 31, 0x2b }, + { 32, 0xb9 }, + { 34, 0x12 }, + { 35, 0x50 }, + { 39, 0xc4 }, + { 40, 0x02 }, + { 41, 0x60 }, + { 53, 0x10 }, + { 54, 0x18 }, + { 56, 0x08 }, + { 57, 0x10 }, + { 58, 0x08 }, + { 61, 0x60 }, + { 62, 0x10 }, + { 75, 0xff } +}; + +/* + * Default values for RF register R2 indexed by channel numbers. + */ +static const uint32_t ural_rf2522_r2[] = { + 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, + 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e +}; + +static const uint32_t ural_rf2523_r2[] = { + 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, + 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 +}; + +static const uint32_t ural_rf2524_r2[] = { + 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, + 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 +}; + +static const uint32_t ural_rf2525_r2[] = { + 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, + 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 +}; + +static const uint32_t ural_rf2525_hi_r2[] = { + 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, + 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e +}; + +static const uint32_t ural_rf2525e_r2[] = { + 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, + 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b +}; + +static const uint32_t ural_rf2526_hi_r2[] = { + 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, + 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 +}; + +static const uint32_t ural_rf2526_r2[] = { + 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, + 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d +}; + +/* + * For dual-band RF, RF registers R1 and R4 also depend on channel number; + * values taken from the reference driver. + */ +static const struct { + uint8_t chan; + uint32_t r1; + uint32_t r2; + uint32_t r4; +} ural_rf5222[] = { + { 1, 0x08808, 0x0044d, 0x00282 }, + { 2, 0x08808, 0x0044e, 0x00282 }, + { 3, 0x08808, 0x0044f, 0x00282 }, + { 4, 0x08808, 0x00460, 0x00282 }, + { 5, 0x08808, 0x00461, 0x00282 }, + { 6, 0x08808, 0x00462, 0x00282 }, + { 7, 0x08808, 0x00463, 0x00282 }, + { 8, 0x08808, 0x00464, 0x00282 }, + { 9, 0x08808, 0x00465, 0x00282 }, + { 10, 0x08808, 0x00466, 0x00282 }, + { 11, 0x08808, 0x00467, 0x00282 }, + { 12, 0x08808, 0x00468, 0x00282 }, + { 13, 0x08808, 0x00469, 0x00282 }, + { 14, 0x08808, 0x0046b, 0x00286 }, + + { 36, 0x08804, 0x06225, 0x00287 }, + { 40, 0x08804, 0x06226, 0x00287 }, + { 44, 0x08804, 0x06227, 0x00287 }, + { 48, 0x08804, 0x06228, 0x00287 }, + { 52, 0x08804, 0x06229, 0x00287 }, + { 56, 0x08804, 0x0622a, 0x00287 }, + { 60, 0x08804, 0x0622b, 0x00287 }, + { 64, 0x08804, 0x0622c, 0x00287 }, + + { 100, 0x08804, 0x02200, 0x00283 }, + { 104, 0x08804, 0x02201, 0x00283 }, + { 108, 0x08804, 0x02202, 0x00283 }, + { 112, 0x08804, 0x02203, 0x00283 }, + { 116, 0x08804, 0x02204, 0x00283 }, + { 120, 0x08804, 0x02205, 0x00283 }, + { 124, 0x08804, 0x02206, 0x00283 }, + { 128, 0x08804, 0x02207, 0x00283 }, + { 132, 0x08804, 0x02208, 0x00283 }, + { 136, 0x08804, 0x02209, 0x00283 }, + { 140, 0x08804, 0x0220a, 0x00283 }, + + { 149, 0x08808, 0x02429, 0x00281 }, + { 153, 0x08808, 0x0242b, 0x00281 }, + { 157, 0x08808, 0x0242d, 0x00281 }, + { 161, 0x08808, 0x0242f, 0x00281 } +}; + +static const uint8_t ural_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + +static const uint8_t ural_chan_5ghz[] = + { 36, 40, 44, 48, 52, 56, 60, 64, + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, + 149, 153, 157, 161 }; + +static const struct usb_config ural_config[URAL_N_TRANSFER] = { + [URAL_BULK_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = ural_bulk_write_callback, + .timeout = 5000, /* ms */ + }, + [URAL_BULK_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = ural_bulk_read_callback, + }, +}; + +static device_probe_t ural_match; +static device_attach_t ural_attach; +static device_detach_t ural_detach; + +static device_method_t ural_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ural_match), + DEVMETHOD(device_attach, ural_attach), + DEVMETHOD(device_detach, ural_detach), + DEVMETHOD_END +}; + +static driver_t ural_driver = { + .name = "ural", + .methods = ural_methods, + .size = sizeof(struct ural_softc), +}; + +static devclass_t ural_devclass; + +DRIVER_MODULE(ural, uhub, ural_driver, ural_devclass, NULL, 0); +MODULE_DEPEND(ural, usb, 1, 1, 1); +MODULE_DEPEND(ural, wlan, 1, 1, 1); +MODULE_VERSION(ural, 1); +USB_PNP_HOST_INFO(ural_devs); + +static int +ural_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != 0) + return (ENXIO); + if (uaa->info.bIfaceIndex != RAL_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa)); +} + +static int +ural_attach(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + struct ural_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + uint8_t iface_index; + int error; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + + mtx_init(&sc->sc_mtx, device_get_nameunit(self), + MTX_NETWORK_LOCK, MTX_DEF); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + iface_index = RAL_IFACE_INDEX; + error = usbd_transfer_setup(uaa->device, + &iface_index, sc->sc_xfer, ural_config, + URAL_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(self, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto detach; + } + + RAL_LOCK(sc); + /* retrieve RT2570 rev. no */ + sc->asic_rev = ural_read(sc, RAL_MAC_CSR0); + + /* retrieve MAC address and various other things from EEPROM */ + ural_read_eeprom(sc); + RAL_UNLOCK(sc); + + device_printf(self, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n", + sc->asic_rev, ural_get_rf(sc->rf_rev)); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(self); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_HOSTAP /* HostAp mode supported */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* bg scanning supported */ + | IEEE80211_C_WPA /* 802.11i */ + ; + + ural_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + ic->ic_update_promisc = ural_update_promisc; + ic->ic_raw_xmit = ural_raw_xmit; + ic->ic_scan_start = ural_scan_start; + ic->ic_scan_end = ural_scan_end; + ic->ic_getradiocaps = ural_getradiocaps; + ic->ic_set_channel = ural_set_channel; + ic->ic_parent = ural_parent; + ic->ic_transmit = ural_transmit; + ic->ic_vap_create = ural_vap_create; + ic->ic_vap_delete = ural_vap_delete; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + RAL_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RAL_RX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +detach: + ural_detach(self); + return (ENXIO); /* failure */ +} + +static int +ural_detach(device_t self) +{ + struct ural_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + + /* prevent further ioctls */ + RAL_LOCK(sc); + sc->sc_detached = 1; + RAL_UNLOCK(sc); + + /* stop all USB transfers */ + usbd_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); + + /* free TX list, if any */ + RAL_LOCK(sc); + ural_unsetup_tx_list(sc); + RAL_UNLOCK(sc); + + if (ic->ic_softc == sc) + ieee80211_ifdetach(ic); + mbufq_drain(&sc->sc_snd); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static usb_error_t +ural_do_request(struct ural_softc *sc, + struct usb_device_request *req, void *data) +{ + usb_error_t err; + int ntries = 10; + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0) + break; + + DPRINTFN(1, "Control request failed, %s (retrying)\n", + usbd_errstr(err)); + if (ural_pause(sc, hz / 100)) + break; + } + return (err); +} + +static struct ieee80211vap * +ural_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ural_softc *sc = ic->ic_softc; + struct ural_vap *uvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + uvp = malloc(sizeof(struct ural_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &uvp->vap; + /* enable s/w bmiss handling for sta mode */ + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(uvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + vap->iv_newstate = ural_newstate; + + usb_callout_init_mtx(&uvp->ratectl_ch, &sc->sc_mtx, 0); + TASK_INIT(&uvp->ratectl_task, 0, ural_ratectl_task, uvp); + ieee80211_ratectl_init(vap); + ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return vap; +} + +static void +ural_vap_delete(struct ieee80211vap *vap) +{ + struct ural_vap *uvp = URAL_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + + usb_callout_drain(&uvp->ratectl_ch); + ieee80211_draintask(ic, &uvp->ratectl_task); + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static void +ural_tx_free(struct ural_tx_data *data, int txerr) +{ + struct ural_softc *sc = data->sc; + + if (data->m != NULL) { + ieee80211_tx_complete(data->ni, data->m, txerr); + data->m = NULL; + data->ni = NULL; + } + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; +} + +static void +ural_setup_tx_list(struct ural_softc *sc) +{ + struct ural_tx_data *data; + int i; + + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + for (i = 0; i < RAL_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + data->sc = sc; + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } +} + +static void +ural_unsetup_tx_list(struct ural_softc *sc) +{ + struct ural_tx_data *data; + int i; + + /* make sure any subsequent use of the queues will fail */ + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + /* free up all node references and mbufs */ + for (i = 0; i < RAL_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +static int +ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ural_vap *uvp = URAL_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ural_softc *sc = ic->ic_softc; + const struct ieee80211_txparam *tp; + struct ieee80211_node *ni; + struct mbuf *m; + + DPRINTF("%s -> %s\n", + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + RAL_LOCK(sc); + usb_callout_stop(&uvp->ratectl_ch); + + switch (nstate) { + case IEEE80211_S_INIT: + if (vap->iv_state == IEEE80211_S_RUN) { + /* abort TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + + /* force tx led to stop blinking */ + ural_write(sc, RAL_MAC_CSR20, 0); + } + break; + + case IEEE80211_S_RUN: + ni = ieee80211_ref_node(vap->iv_bss); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) { + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) + goto fail; + + ural_update_slot(sc); + ural_set_txpreamble(sc); + ural_set_basicrates(sc, ic->ic_bsschan); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + ural_set_bssid(sc, sc->sc_bssid); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) { + m = ieee80211_beacon_alloc(ni); + if (m == NULL) { + device_printf(sc->sc_dev, + "could not allocate beacon\n"); + goto fail; + } + ieee80211_ref_node(ni); + if (ural_tx_bcn(sc, m, ni) != 0) { + device_printf(sc->sc_dev, + "could not send beacon\n"); + goto fail; + } + } + + /* make tx led blink on tx (controlled by ASIC) */ + ural_write(sc, RAL_MAC_CSR20, 1); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) + ural_enable_tsf_sync(sc); + else + ural_enable_tsf(sc); + + /* enable automatic rate adaptation */ + /* XXX should use ic_bsschan but not valid until after newstate call below */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + ural_ratectl_start(sc, ni); + ieee80211_free_node(ni); + break; + + default: + break; + } + RAL_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); + +fail: + RAL_UNLOCK(sc); + IEEE80211_LOCK(ic); + ieee80211_free_node(ni); + return (-1); +} + + +static void +ural_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ural_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211vap *vap; + struct ural_tx_data *data; + struct mbuf *m; + struct usb_page_cache *pc; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete, %d bytes\n", len); + + /* free resources */ + data = usbd_xfer_get_priv(xfer); + ural_tx_free(data, 0); + usbd_xfer_set_priv(xfer, NULL); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->tx_q); + if (data) { + STAILQ_REMOVE_HEAD(&sc->tx_q, next); + m = data->m; + + if (m->m_pkthdr.len > (int)(RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) { + DPRINTFN(0, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE); + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &data->desc, RAL_TX_DESC_SIZE); + usbd_m_copy_in(pc, RAL_TX_DESC_SIZE, m, 0, + m->m_pkthdr.len); + + vap = data->ni->ni_vap; + if (ieee80211_radiotap_active_vap(vap)) { + struct ural_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = data->rate; + tap->wt_antenna = sc->tx_ant; + + ieee80211_radiotap_tx(vap, m); + } + + /* xfer length needs to be a multiple of two! */ + len = (RAL_TX_DESC_SIZE + m->m_pkthdr.len + 1) & ~1; + if ((len % 64) == 0) + len += 2; + + DPRINTFN(11, "sending frame len=%u xferlen=%u\n", + m->m_pkthdr.len, len); + + usbd_xfer_set_frame_len(xfer, 0, len); + usbd_xfer_set_priv(xfer, data); + + usbd_transfer_submit(xfer); + } + ural_start(sc); + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + data = usbd_xfer_get_priv(xfer); + if (data != NULL) { + ural_tx_free(data, error); + usbd_xfer_set_priv(xfer, NULL); + } + + if (error == USB_ERR_STALLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + if (error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + break; + } +} + +static void +ural_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ural_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct mbuf *m = NULL; + struct usb_page_cache *pc; + uint32_t flags; + int8_t rssi = 0, nf = 0; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(15, "rx done, actlen=%d\n", len); + + if (len < (int)(RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { + DPRINTF("%s: xfer too short %d\n", + device_get_nameunit(sc->sc_dev), len); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + + len -= RAL_RX_DESC_SIZE; + /* rx descriptor is located at the end */ + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, len, &sc->sc_rx_desc, RAL_RX_DESC_SIZE); + + rssi = URAL_RSSI(sc->sc_rx_desc.rssi); + nf = RAL_NOISE_FLOOR; + flags = le32toh(sc->sc_rx_desc.flags); + if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) { + /* + * This should not happen since we did not + * request to receive those frames when we + * filled RAL_TXRX_CSR2: + */ + DPRINTFN(5, "PHY or CRC error\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + DPRINTF("could not allocate mbuf\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + usbd_copy_out(pc, 0, mtod(m, uint8_t *), len); + + /* finalize mbuf */ + m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; + + if (ieee80211_radiotap_active(ic)) { + struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; + + /* XXX set once */ + tap->wr_flags = 0; + tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, + (flags & RAL_RX_OFDM) ? + IEEE80211_T_OFDM : IEEE80211_T_CCK); + tap->wr_antenna = sc->rx_ant; + tap->wr_antsignal = nf + rssi; + tap->wr_antnoise = nf; + } + /* Strip trailing 802.11 MAC FCS. */ + m_adj(m, -IEEE80211_CRC_LEN); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "ieee80211_input" here, and not some lines up! + */ + RAL_UNLOCK(sc); + if (m) { + ni = ieee80211_find_rxnode(ic, + mtod(m, struct ieee80211_frame_min *)); + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi, nf); + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi, nf); + } + RAL_LOCK(sc); + ural_start(sc); + return; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static uint8_t +ural_plcp_signal(int rate) +{ + switch (rate) { + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: return 0xb; + case 18: return 0xf; + case 24: return 0xa; + case 36: return 0xe; + case 48: return 0x9; + case 72: return 0xd; + case 96: return 0x8; + case 108: return 0xc; + + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: return 0x0; + case 4: return 0x1; + case 11: return 0x2; + case 22: return 0x3; + } + return 0xff; /* XXX unsupported/unknown rate */ +} + +static void +ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc, + uint32_t flags, int len, int rate) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t plcp_length; + int remainder; + + desc->flags = htole32(flags); + desc->flags |= htole32(RAL_TX_NEWSEQ); + desc->flags |= htole32(len << 16); + + desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5)); + desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame))); + + /* setup PLCP fields */ + desc->plcp_signal = ural_plcp_signal(rate); + desc->plcp_service = 4; + + len += IEEE80211_CRC_LEN; + if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) { + desc->flags |= htole32(RAL_TX_OFDM); + + plcp_length = len & 0xfff; + desc->plcp_length_hi = plcp_length >> 6; + desc->plcp_length_lo = plcp_length & 0x3f; + } else { + if (rate == 0) + rate = 2; /* avoid division by zero */ + plcp_length = howmany(16 * len, rate); + if (rate == 22) { + remainder = (16 * len) % 22; + if (remainder != 0 && remainder < 7) + desc->plcp_service |= RAL_PLCP_LENGEXT; + } + desc->plcp_length_hi = plcp_length >> 8; + desc->plcp_length_lo = plcp_length & 0xff; + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->plcp_signal |= 0x08; + } + + desc->iv = 0; + desc->eiv = 0; +} + +#define RAL_TX_TIMEOUT 5000 + +static int +ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ural_tx_data *data; + + if (sc->tx_nfree == 0) { + m_freem(m0); + ieee80211_free_node(ni); + return (EIO); + } + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { + m_freem(m0); + ieee80211_free_node(ni); + return (ENXIO); + } + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + ural_setup_tx_desc(sc, &data->desc, + RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len, + tp->mgmtrate); + + DPRINTFN(10, "sending beacon frame len=%u rate=%u\n", + m0->m_pkthdr.len, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return (0); +} + +static int +ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ural_tx_data *data; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint32_t flags; + uint16_t dur; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + + wh = mtod(m0, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + wh = mtod(m0, struct ieee80211_frame *); + } + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + flags = 0; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RAL_TX_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + USETW(wh->i_dur, dur); + + /* tell hardware to add timestamp for probe responses */ + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_MGT && + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= RAL_TX_TIMESTAMP; + } + + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, tp->mgmtrate); + + DPRINTFN(10, "sending mgt frame len=%u rate=%u\n", + m0->m_pkthdr.len, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_sendprot(struct ural_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_frame *wh; + struct ural_tx_data *data; + struct mbuf *mprot; + int protrate, ackrate, pktlen, flags, isshort; + uint16_t dur; + + KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, + ("protection %d", prot)); + + wh = mtod(m, const struct ieee80211_frame *); + pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; + + protrate = ieee80211_ctl_rate(ic->ic_rt, rate); + ackrate = ieee80211_ack_rate(ic->ic_rt, rate); + + isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; + dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort) + + ieee80211_ack_duration(ic->ic_rt, rate, isshort); + flags = RAL_TX_RETRY(7); + if (prot == IEEE80211_PROT_RTSCTS) { + /* NB: CTS is the same size as an ACK */ + dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); + flags |= RAL_TX_ACK; + mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); + } else { + mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); + } + if (mprot == NULL) { + /* XXX stat + msg */ + return ENOBUFS; + } + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = mprot; + data->ni = ieee80211_ref_node(ni); + data->rate = protrate; + ural_setup_tx_desc(sc, &data->desc, flags, mprot->m_pkthdr.len, protrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ural_tx_data *data; + uint32_t flags; + int error; + int rate; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + KASSERT(params != NULL, ("no raw xmit params")); + + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { + m_freem(m0); + return EINVAL; + } + flags = 0; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= RAL_TX_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { + error = ural_sendprot(sc, m0, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + if (error || sc->tx_nfree == 0) { + m_freem(m0); + return ENOBUFS; + } + flags |= RAL_TX_IFS_SIFS; + } + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + /* XXX need to setup descriptor ourself */ + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending raw frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ural_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k; + uint32_t flags = 0; + uint16_t dur; + int error, rate; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m0, struct ieee80211_frame *); + + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else { + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = ni->ni_txrate; + } + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + int prot = IEEE80211_PROT_NONE; + if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) + prot = IEEE80211_PROT_RTSCTS; + else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) + prot = ic->ic_protmode; + if (prot != IEEE80211_PROT_NONE) { + error = ural_sendprot(sc, m0, ni, prot, rate); + if (error || sc->tx_nfree == 0) { + m_freem(m0); + return ENOBUFS; + } + flags |= RAL_TX_IFS_SIFS; + } + } + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RAL_TX_ACK; + flags |= RAL_TX_RETRY(7); + + dur = ieee80211_ack_duration(ic->ic_rt, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + USETW(wh->i_dur, dur); + } + + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending data frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct ural_softc *sc = ic->ic_softc; + int error; + + RAL_LOCK(sc); + if (!sc->sc_running) { + RAL_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RAL_UNLOCK(sc); + return (error); + } + ural_start(sc); + RAL_UNLOCK(sc); + + return (0); +} + +static void +ural_start(struct ural_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + if (sc->sc_running == 0) + return; + + while (sc->tx_nfree >= RAL_TX_MINFREE && + (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + if (ural_tx_data(sc, m, ni) != 0) { + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + break; + } + } +} + +static void +ural_parent(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + int startall = 0; + + RAL_LOCK(sc); + if (sc->sc_detached) { + RAL_UNLOCK(sc); + return; + } + if (ic->ic_nrunning > 0) { + if (sc->sc_running == 0) { + ural_init(sc); + startall = 1; + } else + ural_setpromisc(sc); + } else if (sc->sc_running) + ural_stop(sc); + RAL_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static void +ural_set_testmode(struct ural_softc *sc) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_VENDOR_REQUEST; + USETW(req.wValue, 4); + USETW(req.wIndex, 1); + USETW(req.wLength, 0); + + error = ural_do_request(sc, &req, NULL); + if (error != 0) { + device_printf(sc->sc_dev, "could not set test mode: %s\n", + usbd_errstr(error)); + } +} + +static void +ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_EEPROM; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read EEPROM: %s\n", + usbd_errstr(error)); + } +} + +static uint16_t +ural_read(struct ural_softc *sc, uint16_t reg) +{ + struct usb_device_request req; + usb_error_t error; + uint16_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, sizeof (uint16_t)); + + error = ural_do_request(sc, &req, &val); + if (error != 0) { + device_printf(sc->sc_dev, "could not read MAC register: %s\n", + usbd_errstr(error)); + return 0; + } + + return le16toh(val); +} + +static void +ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read MAC register: %s\n", + usbd_errstr(error)); + } +} + +static void +ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_WRITE_MAC; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + error = ural_do_request(sc, &req, NULL); + if (error != 0) { + device_printf(sc->sc_dev, "could not write MAC register: %s\n", + usbd_errstr(error)); + } +} + +static void +ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_WRITE_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not write MAC register: %s\n", + usbd_errstr(error)); + } +} + +static void +ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) +{ + uint16_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to BBP\n"); + return; + } + + tmp = reg << 8 | val; + ural_write(sc, RAL_PHY_CSR7, tmp); +} + +static uint8_t +ural_bbp_read(struct ural_softc *sc, uint8_t reg) +{ + uint16_t val; + int ntries; + + val = RAL_BBP_WRITE | reg << 8; + ural_write(sc, RAL_PHY_CSR7, val); + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; + } + + return ural_read(sc, RAL_PHY_CSR7) & 0xff; +} + +static void +ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to RF\n"); + return; + } + + tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3); + ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff); + ural_write(sc, RAL_PHY_CSR10, tmp >> 16); + + /* remember last written value in sc */ + sc->rf_regs[reg] = val; + + DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff); +} + +static void +ural_scan_start(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + ural_write(sc, RAL_TXRX_CSR19, 0); + ural_set_bssid(sc, ieee80211broadcastaddr); + RAL_UNLOCK(sc); +} + +static void +ural_scan_end(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + ural_enable_tsf_sync(sc); + ural_set_bssid(sc, sc->sc_bssid); + RAL_UNLOCK(sc); + +} + +static void +ural_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct ural_softc *sc = ic->ic_softc; + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + ural_chan_2ghz, nitems(ural_chan_2ghz), bands, 0); + + if (sc->rf_rev == RAL_RF_5222) { + setbit(bands, IEEE80211_MODE_11A); + ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, + ural_chan_5ghz, nitems(ural_chan_5ghz), bands, 0); + } +} + +static void +ural_set_channel(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + ural_set_chan(sc, ic->ic_curchan); + RAL_UNLOCK(sc); +} + +static void +ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint8_t power, tmp; + int i, chan; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + if (IEEE80211_IS_CHAN_2GHZ(c)) + power = min(sc->txpow[chan - 1], 31); + else + power = 31; + + /* adjust txpower using ifconfig settings */ + power -= (100 - ic->ic_txpowlimit) / 8; + + DPRINTFN(2, "setting channel to %u, txpower to %u\n", chan, power); + + switch (sc->rf_rev) { + case RAL_RF_2522: + ural_rf_write(sc, RAL_RF1, 0x00814); + ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + break; + + case RAL_RF_2523: + ural_rf_write(sc, RAL_RF1, 0x08804); + ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2524: + ural_rf_write(sc, RAL_RF1, 0x0c808); + ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2525: + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2525E: + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); + break; + + case RAL_RF_2526: + ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); + ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); + ural_rf_write(sc, RAL_RF1, 0x08804); + + ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); + break; + + /* dual-band RF */ + case RAL_RF_5222: + for (i = 0; ural_rf5222[i].chan != chan; i++); + + ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); + ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); + break; + } + + if (ic->ic_opmode != IEEE80211_M_MONITOR && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) { + /* set Japan filter bit for channel 14 */ + tmp = ural_bbp_read(sc, 70); + + tmp &= ~RAL_JAPAN_FILTER; + if (chan == 14) + tmp |= RAL_JAPAN_FILTER; + + ural_bbp_write(sc, 70, tmp); + + /* clear CRC errors */ + ural_read(sc, RAL_STA_CSR0); + + ural_pause(sc, hz / 100); + ural_disable_rf_tune(sc); + } + + /* XXX doesn't belong here */ + /* update basic rate set */ + ural_set_basicrates(sc, c); + + /* give the hardware some time to do the switchover */ + ural_pause(sc, hz / 100); +} + +/* + * Disable RF auto-tuning. + */ +static void +ural_disable_rf_tune(struct ural_softc *sc) +{ + uint32_t tmp; + + if (sc->rf_rev != RAL_RF_2523) { + tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; + ural_rf_write(sc, RAL_RF1, tmp); + } + + tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; + ural_rf_write(sc, RAL_RF3, tmp); + + DPRINTFN(2, "disabling RF autotune\n"); +} + +/* + * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF + * synchronization. + */ +static void +ural_enable_tsf_sync(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint16_t logcwmin, preload, tmp; + + /* first, disable TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + + tmp = (16 * vap->iv_bss->ni_intval) << 4; + ural_write(sc, RAL_TXRX_CSR18, tmp); + + logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; + preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; + tmp = logcwmin << 12 | preload; + ural_write(sc, RAL_TXRX_CSR20, tmp); + + /* finally, enable TSF synchronization */ + tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; + if (ic->ic_opmode == IEEE80211_M_STA) + tmp |= RAL_ENABLE_TSF_SYNC(1); + else + tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; + ural_write(sc, RAL_TXRX_CSR19, tmp); + + DPRINTF("enabling TSF synchronization\n"); +} + +static void +ural_enable_tsf(struct ural_softc *sc) +{ + /* first, disable TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + ural_write(sc, RAL_TXRX_CSR19, RAL_ENABLE_TSF | RAL_ENABLE_TSF_SYNC(2)); +} + +#define RAL_RXTX_TURNAROUND 5 /* us */ +static void +ural_update_slot(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t slottime, sifs, eifs; + + slottime = IEEE80211_GET_SLOTTIME(ic); + + /* + * These settings may sound a bit inconsistent but this is what the + * reference driver does. + */ + if (ic->ic_curmode == IEEE80211_MODE_11B) { + sifs = 16 - RAL_RXTX_TURNAROUND; + eifs = 364; + } else { + sifs = 10 - RAL_RXTX_TURNAROUND; + eifs = 64; + } + + ural_write(sc, RAL_MAC_CSR10, slottime); + ural_write(sc, RAL_MAC_CSR11, sifs); + ural_write(sc, RAL_MAC_CSR12, eifs); +} + +static void +ural_set_txpreamble(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t tmp; + + tmp = ural_read(sc, RAL_TXRX_CSR10); + + tmp &= ~RAL_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RAL_SHORT_PREAMBLE; + + ural_write(sc, RAL_TXRX_CSR10, tmp); +} + +static void +ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c) +{ + /* XXX wrong, take from rate set */ + /* update basic rate set */ + if (IEEE80211_IS_CHAN_5GHZ(c)) { + /* 11a basic rates: 6, 12, 24Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x150); + } else if (IEEE80211_IS_CHAN_ANYG(c)) { + /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x15f); + } else { + /* 11b basic rates: 1, 2Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x3); + } +} + +static void +ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid) +{ + uint16_t tmp; + + tmp = bssid[0] | bssid[1] << 8; + ural_write(sc, RAL_MAC_CSR5, tmp); + + tmp = bssid[2] | bssid[3] << 8; + ural_write(sc, RAL_MAC_CSR6, tmp); + + tmp = bssid[4] | bssid[5] << 8; + ural_write(sc, RAL_MAC_CSR7, tmp); + + DPRINTF("setting BSSID to %6D\n", bssid, ":"); +} + +static void +ural_set_macaddr(struct ural_softc *sc, const uint8_t *addr) +{ + uint16_t tmp; + + tmp = addr[0] | addr[1] << 8; + ural_write(sc, RAL_MAC_CSR2, tmp); + + tmp = addr[2] | addr[3] << 8; + ural_write(sc, RAL_MAC_CSR3, tmp); + + tmp = addr[4] | addr[5] << 8; + ural_write(sc, RAL_MAC_CSR4, tmp); + + DPRINTF("setting MAC address to %6D\n", addr, ":"); +} + +static void +ural_setpromisc(struct ural_softc *sc) +{ + uint32_t tmp; + + tmp = ural_read(sc, RAL_TXRX_CSR2); + + tmp &= ~RAL_DROP_NOT_TO_ME; + if (sc->sc_ic.ic_promisc == 0) + tmp |= RAL_DROP_NOT_TO_ME; + + ural_write(sc, RAL_TXRX_CSR2, tmp); + + DPRINTF("%s promiscuous mode\n", sc->sc_ic.ic_promisc ? + "entering" : "leaving"); +} + +static void +ural_update_promisc(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + if (sc->sc_running) + ural_setpromisc(sc); + RAL_UNLOCK(sc); +} + +static const char * +ural_get_rf(int rev) +{ + switch (rev) { + case RAL_RF_2522: return "RT2522"; + case RAL_RF_2523: return "RT2523"; + case RAL_RF_2524: return "RT2524"; + case RAL_RF_2525: return "RT2525"; + case RAL_RF_2525E: return "RT2525e"; + case RAL_RF_2526: return "RT2526"; + case RAL_RF_5222: return "RT5222"; + default: return "unknown"; + } +} + +static void +ural_read_eeprom(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t val; + + ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); + val = le16toh(val); + sc->rf_rev = (val >> 11) & 0x7; + sc->hw_radio = (val >> 10) & 0x1; + sc->led_mode = (val >> 6) & 0x7; + sc->rx_ant = (val >> 4) & 0x3; + sc->tx_ant = (val >> 2) & 0x3; + sc->nb_ant = val & 0x3; + + /* read MAC address */ + ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, ic->ic_macaddr, 6); + + /* read default values for BBP registers */ + ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); + + /* read Tx power for all b/g channels */ + ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14); +} + +static int +ural_bbp_init(struct ural_softc *sc) +{ + int i, ntries; + + /* wait for BBP to be ready */ + for (ntries = 0; ntries < 100; ntries++) { + if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for BBP\n"); + return EIO; + } + + /* initialize BBP registers to default values */ + for (i = 0; i < nitems(ural_def_bbp); i++) + ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); + +#if 0 + /* initialize BBP registers to values stored in EEPROM */ + for (i = 0; i < 16; i++) { + if (sc->bbp_prom[i].reg == 0xff) + continue; + ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); + } +#endif + + return 0; +} + +static void +ural_set_txantenna(struct ural_softc *sc, int antenna) +{ + uint16_t tmp; + uint8_t tx; + + tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; + if (antenna == 1) + tx |= RAL_BBP_ANTA; + else if (antenna == 2) + tx |= RAL_BBP_ANTB; + else + tx |= RAL_BBP_DIVERSITY; + + /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ + if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 || + sc->rf_rev == RAL_RF_5222) + tx |= RAL_BBP_FLIPIQ; + + ural_bbp_write(sc, RAL_BBP_TX, tx); + + /* update values in PHY_CSR5 and PHY_CSR6 */ + tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7; + ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); + + tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7; + ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); +} + +static void +ural_set_rxantenna(struct ural_softc *sc, int antenna) +{ + uint8_t rx; + + rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; + if (antenna == 1) + rx |= RAL_BBP_ANTA; + else if (antenna == 2) + rx |= RAL_BBP_ANTB; + else + rx |= RAL_BBP_DIVERSITY; + + /* need to force no I/Q flip for RF 2525e and 2526 */ + if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526) + rx &= ~RAL_BBP_FLIPIQ; + + ural_bbp_write(sc, RAL_BBP_RX, rx); +} + +static void +ural_init(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint16_t tmp; + int i, ntries; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + ural_set_testmode(sc); + ural_write(sc, 0x308, 0x00f0); /* XXX magic */ + + ural_stop(sc); + + /* initialize MAC registers to default values */ + for (i = 0; i < nitems(ural_def_mac); i++) + ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); + + /* wait for BBP and RF to wake up (this can take a long time!) */ + for (ntries = 0; ntries < 100; ntries++) { + tmp = ural_read(sc, RAL_MAC_CSR17); + if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == + (RAL_BBP_AWAKE | RAL_RF_AWAKE)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for BBP/RF to wakeup\n"); + goto fail; + } + + /* we're ready! */ + ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); + + /* set basic rate set (will be updated later) */ + ural_write(sc, RAL_TXRX_CSR11, 0x15f); + + if (ural_bbp_init(sc) != 0) + goto fail; + + ural_set_chan(sc, ic->ic_curchan); + + /* clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); + + ural_set_txantenna(sc, sc->tx_ant); + ural_set_rxantenna(sc, sc->rx_ant); + + ural_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + + /* + * Allocate Tx and Rx xfer queues. + */ + ural_setup_tx_list(sc); + + /* kick Rx */ + tmp = RAL_DROP_PHY | RAL_DROP_CRC; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + tmp |= RAL_DROP_TODS; + if (ic->ic_promisc == 0) + tmp |= RAL_DROP_NOT_TO_ME; + } + ural_write(sc, RAL_TXRX_CSR2, tmp); + + sc->sc_running = 1; + usbd_xfer_set_stall(sc->sc_xfer[URAL_BULK_WR]); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_RD]); + return; + +fail: ural_stop(sc); +} + +static void +ural_stop(struct ural_softc *sc) +{ + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_running = 0; + + /* + * Drain all the transfers, if not already drained: + */ + RAL_UNLOCK(sc); + usbd_transfer_drain(sc->sc_xfer[URAL_BULK_WR]); + usbd_transfer_drain(sc->sc_xfer[URAL_BULK_RD]); + RAL_LOCK(sc); + + ural_unsetup_tx_list(sc); + + /* disable Rx */ + ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); + /* reset ASIC and BBP (but won't reset MAC registers!) */ + ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); + /* wait a little */ + ural_pause(sc, hz / 10); + ural_write(sc, RAL_MAC_CSR1, 0); + /* wait a little */ + ural_pause(sc, hz / 10); +} + +static int +ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!sc->sc_running) { + RAL_UNLOCK(sc); + m_freem(m); + return ENETDOWN; + } + if (sc->tx_nfree < RAL_TX_MINFREE) { + RAL_UNLOCK(sc); + m_freem(m); + return EIO; + } + + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + if (ural_tx_mgt(sc, m, ni) != 0) + goto bad; + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + if (ural_tx_raw(sc, m, ni, params) != 0) + goto bad; + } + RAL_UNLOCK(sc); + return 0; +bad: + RAL_UNLOCK(sc); + return EIO; /* XXX */ +} + +static void +ural_ratectl_start(struct ural_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ural_vap *uvp = URAL_VAP(vap); + + /* clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); + + usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp); +} + +static void +ural_ratectl_timeout(void *arg) +{ + struct ural_vap *uvp = arg; + struct ieee80211vap *vap = &uvp->vap; + struct ieee80211com *ic = vap->iv_ic; + + ieee80211_runtask(ic, &uvp->ratectl_task); +} + +static void +ural_ratectl_task(void *arg, int pending) +{ + struct ural_vap *uvp = arg; + struct ieee80211vap *vap = &uvp->vap; + struct ural_softc *sc = vap->iv_ic->ic_softc; + struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; + int fail; + + RAL_LOCK(sc); + /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta)); + + txs->flags = IEEE80211_RATECTL_TX_STATS_RETRIES; + txs->nsuccess = sc->sta[7] + /* TX ok w/o retry */ + sc->sta[8]; /* TX ok w/ retry */ + fail = sc->sta[9]; /* TX retry-fail count */ + txs->nframes = txs->nsuccess + fail; + /* XXX fail * maxretry */ + txs->nretries = sc->sta[8] + fail; + + ieee80211_ratectl_tx_update(vap, txs); + + /* count TX retry-fail as Tx errors */ + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, fail); + + usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp); + RAL_UNLOCK(sc); +} + +static int +ural_pause(struct ural_softc *sc, int timeout) +{ + + usb_pause_mtx(&sc->sc_mtx, timeout); + return (0); +} diff --git a/freebsd/sys/dev/usb/wlan/if_uralreg.h b/freebsd/sys/dev/usb/wlan/if_uralreg.h new file mode 100644 index 00000000..3554fe6f --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_uralreg.h @@ -0,0 +1,211 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 + * Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define RAL_NOISE_FLOOR -95 +#define RAL_RSSI_CORR 120 + +#define RAL_RX_DESC_SIZE (sizeof (struct ural_rx_desc)) +#define RAL_TX_DESC_SIZE (sizeof (struct ural_tx_desc)) +#define RAL_FRAME_SIZE 0x780 /* NOTE: using 0x980 does not work */ + +#define RAL_CONFIG_NO 1 +#define RAL_IFACE_INDEX 0 + +#define RAL_VENDOR_REQUEST 0x01 +#define RAL_WRITE_MAC 0x02 +#define RAL_READ_MAC 0x03 +#define RAL_WRITE_MULTI_MAC 0x06 +#define RAL_READ_MULTI_MAC 0x07 +#define RAL_READ_EEPROM 0x09 + +/* + * MAC registers. + */ +#define RAL_MAC_CSR0 0x0400 /* ASIC Version */ +#define RAL_MAC_CSR1 0x0402 /* System control */ +#define RAL_MAC_CSR2 0x0404 /* MAC addr0 */ +#define RAL_MAC_CSR3 0x0406 /* MAC addr1 */ +#define RAL_MAC_CSR4 0x0408 /* MAC addr2 */ +#define RAL_MAC_CSR5 0x040a /* BSSID0 */ +#define RAL_MAC_CSR6 0x040c /* BSSID1 */ +#define RAL_MAC_CSR7 0x040e /* BSSID2 */ +#define RAL_MAC_CSR8 0x0410 /* Max frame length */ +#define RAL_MAC_CSR9 0x0412 /* Timer control */ +#define RAL_MAC_CSR10 0x0414 /* Slot time */ +#define RAL_MAC_CSR11 0x0416 /* IFS */ +#define RAL_MAC_CSR12 0x0418 /* EIFS */ +#define RAL_MAC_CSR13 0x041a /* Power mode0 */ +#define RAL_MAC_CSR14 0x041c /* Power mode1 */ +#define RAL_MAC_CSR15 0x041e /* Power saving transition0 */ +#define RAL_MAC_CSR16 0x0420 /* Power saving transition1 */ +#define RAL_MAC_CSR17 0x0422 /* Power state control */ +#define RAL_MAC_CSR18 0x0424 /* Auto wake-up control */ +#define RAL_MAC_CSR19 0x0426 /* GPIO control */ +#define RAL_MAC_CSR20 0x0428 /* LED control0 */ +#define RAL_MAC_CSR22 0x042c /* XXX not documented */ + +/* + * Tx/Rx Registers. + */ +#define RAL_TXRX_CSR0 0x0440 /* Security control */ +#define RAL_TXRX_CSR2 0x0444 /* Rx control */ +#define RAL_TXRX_CSR5 0x044a /* CCK Tx BBP ID0 */ +#define RAL_TXRX_CSR6 0x044c /* CCK Tx BBP ID1 */ +#define RAL_TXRX_CSR7 0x044e /* OFDM Tx BBP ID0 */ +#define RAL_TXRX_CSR8 0x0450 /* OFDM Tx BBP ID1 */ +#define RAL_TXRX_CSR10 0x0454 /* Auto responder control */ +#define RAL_TXRX_CSR11 0x0456 /* Auto responder basic rate */ +#define RAL_TXRX_CSR18 0x0464 /* Beacon interval */ +#define RAL_TXRX_CSR19 0x0466 /* Beacon/sync control */ +#define RAL_TXRX_CSR20 0x0468 /* Beacon alignment */ +#define RAL_TXRX_CSR21 0x046a /* XXX not documented */ + +/* + * Security registers. + */ +#define RAL_SEC_CSR0 0x0480 /* Shared key 0, word 0 */ + +/* + * PHY registers. + */ +#define RAL_PHY_CSR2 0x04c4 /* Tx MAC configuration */ +#define RAL_PHY_CSR4 0x04c8 /* Interface configuration */ +#define RAL_PHY_CSR5 0x04ca /* BBP Pre-Tx CCK */ +#define RAL_PHY_CSR6 0x04cc /* BBP Pre-Tx OFDM */ +#define RAL_PHY_CSR7 0x04ce /* BBP serial control */ +#define RAL_PHY_CSR8 0x04d0 /* BBP serial status */ +#define RAL_PHY_CSR9 0x04d2 /* RF serial control0 */ +#define RAL_PHY_CSR10 0x04d4 /* RF serial control1 */ + +/* + * Statistics registers. + */ +#define RAL_STA_CSR0 0x04e0 /* FCS error */ + + +#define RAL_DISABLE_RX (1 << 0) +#define RAL_DROP_CRC (1 << 1) +#define RAL_DROP_PHY (1 << 2) +#define RAL_DROP_CTL (1 << 3) +#define RAL_DROP_NOT_TO_ME (1 << 4) +#define RAL_DROP_TODS (1 << 5) +#define RAL_DROP_BAD_VERSION (1 << 6) +#define RAL_DROP_MULTICAST (1 << 9) +#define RAL_DROP_BROADCAST (1 << 10) + +#define RAL_SHORT_PREAMBLE (1 << 2) + +#define RAL_RESET_ASIC (1 << 0) +#define RAL_RESET_BBP (1 << 1) +#define RAL_HOST_READY (1 << 2) + +#define RAL_ENABLE_TSF (1 << 0) +#define RAL_ENABLE_TSF_SYNC(x) (((x) & 0x3) << 1) +#define RAL_ENABLE_TBCN (1 << 3) +#define RAL_ENABLE_BEACON_GENERATOR (1 << 4) + +#define RAL_RF_AWAKE (3 << 7) +#define RAL_BBP_AWAKE (3 << 5) + +#define RAL_BBP_WRITE (1 << 15) +#define RAL_BBP_BUSY (1 << 0) + +#define RAL_RF1_AUTOTUNE 0x08000 +#define RAL_RF3_AUTOTUNE 0x00040 + +#define RAL_RF_2522 0x00 +#define RAL_RF_2523 0x01 +#define RAL_RF_2524 0x02 +#define RAL_RF_2525 0x03 +#define RAL_RF_2525E 0x04 +#define RAL_RF_2526 0x05 +/* dual-band RF */ +#define RAL_RF_5222 0x10 + +#define RAL_BBP_VERSION 0 +#define RAL_BBP_TX 2 +#define RAL_BBP_RX 14 + +#define RAL_BBP_ANTA 0x00 +#define RAL_BBP_DIVERSITY 0x01 +#define RAL_BBP_ANTB 0x02 +#define RAL_BBP_ANTMASK 0x03 +#define RAL_BBP_FLIPIQ 0x04 + +#define RAL_JAPAN_FILTER 0x08 + +struct ural_tx_desc { + uint32_t flags; +#define RAL_TX_RETRY(x) ((x) << 4) +#define RAL_TX_MORE_FRAG (1 << 8) +#define RAL_TX_ACK (1 << 9) +#define RAL_TX_TIMESTAMP (1 << 10) +#define RAL_TX_OFDM (1 << 11) +#define RAL_TX_NEWSEQ (1 << 12) + +#define RAL_TX_IFS_MASK 0x00006000 +#define RAL_TX_IFS_BACKOFF (0 << 13) +#define RAL_TX_IFS_SIFS (1 << 13) +#define RAL_TX_IFS_NEWBACKOFF (2 << 13) +#define RAL_TX_IFS_NONE (3 << 13) + + uint16_t wme; +#define RAL_LOGCWMAX(x) (((x) & 0xf) << 12) +#define RAL_LOGCWMIN(x) (((x) & 0xf) << 8) +#define RAL_AIFSN(x) (((x) & 0x3) << 6) +#define RAL_IVOFFSET(x) (((x) & 0x3f)) + + uint16_t reserved1; + uint8_t plcp_signal; + uint8_t plcp_service; +#define RAL_PLCP_LENGEXT 0x80 + + uint8_t plcp_length_lo; + uint8_t plcp_length_hi; + uint32_t iv; + uint32_t eiv; +} __packed; + +struct ural_rx_desc { + uint32_t flags; +#define RAL_RX_CRC_ERROR (1 << 5) +#define RAL_RX_OFDM (1 << 6) +#define RAL_RX_PHY_ERROR (1 << 7) + + uint8_t rssi; + uint8_t rate; + uint16_t reserved; + + uint32_t iv; + uint32_t eiv; +} __packed; + +#define RAL_RF_LOBUSY (1 << 15) +#define RAL_RF_BUSY (1U << 31) +#define RAL_RF_20BIT (20 << 24) + +#define RAL_RF1 0 +#define RAL_RF2 2 +#define RAL_RF3 1 +#define RAL_RF4 3 + +#define RAL_EEPROM_ADDRESS 0x0004 +#define RAL_EEPROM_TXPOWER 0x003c +#define RAL_EEPROM_CONFIG0 0x0016 +#define RAL_EEPROM_BBP_BASE 0x001c diff --git a/freebsd/sys/dev/usb/wlan/if_uralvar.h b/freebsd/sys/dev/usb/wlan/if_uralvar.h new file mode 100644 index 00000000..dd863fe0 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_uralvar.h @@ -0,0 +1,136 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005 + * Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define RAL_TX_LIST_COUNT 8 +#define RAL_TX_MINFREE 2 + +#define URAL_SCAN_START 1 +#define URAL_SCAN_END 2 +#define URAL_SET_CHANNEL 3 + + +struct ural_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_antsignal; + int8_t wr_antnoise; + uint8_t wr_antenna; +} __packed __aligned(8); + +#define RAL_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)) + +struct ural_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; + uint8_t wt_antenna; +} __packed __aligned(8); + +#define RAL_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA)) + +struct ural_softc; + +struct ural_tx_data { + STAILQ_ENTRY(ural_tx_data) next; + struct ural_softc *sc; + struct ural_tx_desc desc; + struct mbuf *m; + struct ieee80211_node *ni; + int rate; +}; +typedef STAILQ_HEAD(, ural_tx_data) ural_txdhead; + +struct ural_vap { + struct ieee80211vap vap; + + struct usb_callout ratectl_ch; + struct task ratectl_task; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define URAL_VAP(vap) ((struct ural_vap *)(vap)) + +enum { + URAL_BULK_WR, + URAL_BULK_RD, + URAL_N_TRANSFER = 2, +}; + +struct ural_softc { + struct ieee80211com sc_ic; + struct ieee80211_ratectl_tx_stats sc_txs; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + + uint32_t asic_rev; + uint8_t rf_rev; + + struct usb_xfer *sc_xfer[URAL_N_TRANSFER]; + + struct ural_tx_data tx_data[RAL_TX_LIST_COUNT]; + ural_txdhead tx_q; + ural_txdhead tx_free; + int tx_nfree; + struct ural_rx_desc sc_rx_desc; + + struct mtx sc_mtx; + + uint16_t sta[11]; + uint32_t rf_regs[4]; + uint8_t txpow[14]; + u_int sc_detached:1, + sc_running:1; + + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + + struct { + uint8_t val; + uint8_t reg; + } __packed bbp_prom[16]; + + int led_mode; + int hw_radio; + int rx_ant; + int tx_ant; + int nb_ant; + + struct ural_rx_radiotap_header sc_rxtap; + struct ural_tx_radiotap_header sc_txtap; +}; + +#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RAL_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) diff --git a/freebsd/sys/dev/usb/wlan/if_urtw.c b/freebsd/sys/dev/usb/wlan/if_urtw.c new file mode 100644 index 00000000..501cc50c --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_urtw.c @@ -0,0 +1,4406 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <rtems/bsd/sys/param.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/kdb.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_radiotap.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <rtems/bsd/local/usbdevs.h> + +#include <dev/usb/wlan/if_urtwreg.h> +#include <dev/usb/wlan/if_urtwvar.h> + +static SYSCTL_NODE(_hw_usb, OID_AUTO, urtw, CTLFLAG_RW, 0, "USB Realtek 8187L"); +#ifdef URTW_DEBUG +int urtw_debug = 0; +SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RWTUN, &urtw_debug, 0, + "control debugging printfs"); +enum { + URTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + URTW_DEBUG_RECV = 0x00000002, /* basic recv operation */ + URTW_DEBUG_RESET = 0x00000004, /* reset processing */ + URTW_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */ + URTW_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */ + URTW_DEBUG_STATE = 0x00000020, /* 802.11 state transitions */ + URTW_DEBUG_STAT = 0x00000040, /* statistic */ + URTW_DEBUG_INIT = 0x00000080, /* initialization of dev */ + URTW_DEBUG_TXSTATUS = 0x00000100, /* tx status */ + URTW_DEBUG_ANY = 0xffffffff +}; +#define DPRINTF(sc, m, fmt, ...) do { \ + if (sc->sc_debug & (m)) \ + printf(fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(sc, m, fmt, ...) do { \ + (void) sc; \ +} while (0) +#endif +static int urtw_preamble_mode = URTW_PREAMBLE_MODE_LONG; +SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RWTUN, + &urtw_preamble_mode, 0, "set the preable mode (long or short)"); + +/* recognized device vendors/products */ +#define urtw_lookup(v, p) \ + ((const struct urtw_type *)usb_lookup(urtw_devs, v, p)) +#define URTW_DEV_B(v,p) \ + { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187B) } +#define URTW_DEV_L(v,p) \ + { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187L) } +#define URTW_REV_RTL8187B 0 +#define URTW_REV_RTL8187L 1 +static const STRUCT_USB_HOST_ID urtw_devs[] = { + URTW_DEV_B(NETGEAR, WG111V3), + URTW_DEV_B(REALTEK, RTL8187B_0), + URTW_DEV_B(REALTEK, RTL8187B_1), + URTW_DEV_B(REALTEK, RTL8187B_2), + URTW_DEV_B(SITECOMEU, WL168V4), + URTW_DEV_L(ASUS, P5B_WIFI), + URTW_DEV_L(BELKIN, F5D7050E), + URTW_DEV_L(LINKSYS4, WUSB54GCV2), + URTW_DEV_L(NETGEAR, WG111V2), + URTW_DEV_L(REALTEK, RTL8187), + URTW_DEV_L(SITECOMEU, WL168V1), + URTW_DEV_L(SURECOM, EP9001G2A), + { USB_VPI(USB_VENDOR_OVISLINK, 0x8187, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_DICKSMITH, 0x9401, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_HP, 0xca02, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_LOGITEC, 0x010c, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_NETGEAR, 0x6100, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_SPHAIRON, 0x0150, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_QCOM, 0x6232, URTW_REV_RTL8187L) }, +#undef URTW_DEV_L +#undef URTW_DEV_B +}; + +#define urtw_read8_m(sc, val, data) do { \ + error = urtw_read8_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write8_m(sc, val, data) do { \ + error = urtw_write8_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_read16_m(sc, val, data) do { \ + error = urtw_read16_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write16_m(sc, val, data) do { \ + error = urtw_write16_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_read32_m(sc, val, data) do { \ + error = urtw_read32_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write32_m(sc, val, data) do { \ + error = urtw_write32_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8187_write_phy_ofdm(sc, val, data) do { \ + error = urtw_8187_write_phy_ofdm_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8187_write_phy_cck(sc, val, data) do { \ + error = urtw_8187_write_phy_cck_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8225_write(sc, val, data) do { \ + error = urtw_8225_write_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) + +struct urtw_pair { + uint32_t reg; + uint32_t val; +}; + +static uint8_t urtw_8225_agc[] = { + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b, + 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, + 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, + 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, + 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, + 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, + 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static uint8_t urtw_8225z2_agc[] = { + 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, + 0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, + 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, + 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, + 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2a, + 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 +}; + +static const uint8_t urtw_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + +static uint32_t urtw_8225_channel[] = { + 0x0000, /* dummy channel 0 */ + 0x085c, /* 1 */ + 0x08dc, /* 2 */ + 0x095c, /* 3 */ + 0x09dc, /* 4 */ + 0x0a5c, /* 5 */ + 0x0adc, /* 6 */ + 0x0b5c, /* 7 */ + 0x0bdc, /* 8 */ + 0x0c5c, /* 9 */ + 0x0cdc, /* 10 */ + 0x0d5c, /* 11 */ + 0x0ddc, /* 12 */ + 0x0e5c, /* 13 */ + 0x0f72, /* 14 */ +}; + +static uint8_t urtw_8225_gain[] = { + 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ + 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ + 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ + 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ + 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ +}; + +static struct urtw_pair urtw_8225_rf_part1[] = { + { 0x00, 0x0067 }, { 0x01, 0x0fe0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, + { 0x04, 0x0486 }, { 0x05, 0x0bc0 }, { 0x06, 0x0ae6 }, { 0x07, 0x082a }, + { 0x08, 0x001f }, { 0x09, 0x0334 }, { 0x0a, 0x0fd4 }, { 0x0b, 0x0391 }, + { 0x0c, 0x0050 }, { 0x0d, 0x06db }, { 0x0e, 0x0029 }, { 0x0f, 0x0914 }, +}; + +static struct urtw_pair urtw_8225_rf_part2[] = { + { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, + { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, + { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x09 }, { 0x0b, 0x80 }, + { 0x0c, 0x01 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 }, + { 0x11, 0x06 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 }, + { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef }, + { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x76 }, { 0x1c, 0x04 }, + { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x27 }, + { 0x22, 0x16 }, { 0x24, 0x46 }, { 0x25, 0x20 }, { 0x26, 0x90 }, + { 0x27, 0x88 } +}; + +static struct urtw_pair urtw_8225_rf_part3[] = { + { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, + { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x10, 0x9b }, + { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, + { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x40, 0x86 }, { 0x41, 0x8d }, + { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x1f }, { 0x45, 0x1e }, + { 0x46, 0x1a }, { 0x47, 0x15 }, { 0x48, 0x10 }, { 0x49, 0x0a }, + { 0x4a, 0x05 }, { 0x4b, 0x02 }, { 0x4c, 0x05 } +}; + +static uint16_t urtw_8225_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb +}; + +static uint8_t urtw_8225_threshold[] = { + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd, +}; + +static uint8_t urtw_8225_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static uint8_t urtw_8225_txpwr_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static uint8_t urtw_8225_txpwr_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static uint8_t urtw_8225_txpwr_ofdm[]={ + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static uint8_t urtw_8225v2_gain_bg[]={ + 0x23, 0x15, 0xa5, /* -82-1dbm */ + 0x23, 0x15, 0xb5, /* -82-2dbm */ + 0x23, 0x15, 0xc5, /* -82-3dbm */ + 0x33, 0x15, 0xc5, /* -78dbm */ + 0x43, 0x15, 0xc5, /* -74dbm */ + 0x53, 0x15, 0xc5, /* -70dbm */ + 0x63, 0x15, 0xc5, /* -66dbm */ +}; + +static struct urtw_pair urtw_8225v2_rf_part1[] = { + { 0x00, 0x02bf }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, + { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, + { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, + { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } +}; + +static struct urtw_pair urtw_8225v2b_rf_part0[] = { + { 0x00, 0x00b7 }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, + { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, + { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, + { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } +}; + +static struct urtw_pair urtw_8225v2b_rf_part1[] = { + {0x0f0, 0x32}, {0x0f1, 0x32}, {0x0f2, 0x00}, + {0x0f3, 0x00}, {0x0f4, 0x32}, {0x0f5, 0x43}, + {0x0f6, 0x00}, {0x0f7, 0x00}, {0x0f8, 0x46}, + {0x0f9, 0xa4}, {0x0fa, 0x00}, {0x0fb, 0x00}, + {0x0fc, 0x96}, {0x0fd, 0xa4}, {0x0fe, 0x00}, + {0x0ff, 0x00}, {0x158, 0x4b}, {0x159, 0x00}, + {0x15a, 0x4b}, {0x15b, 0x00}, {0x160, 0x4b}, + {0x161, 0x09}, {0x162, 0x4b}, {0x163, 0x09}, + {0x1ce, 0x0f}, {0x1cf, 0x00}, {0x1e0, 0xff}, + {0x1e1, 0x0f}, {0x1e2, 0x00}, {0x1f0, 0x4e}, + {0x1f1, 0x01}, {0x1f2, 0x02}, {0x1f3, 0x03}, + {0x1f4, 0x04}, {0x1f5, 0x05}, {0x1f6, 0x06}, + {0x1f7, 0x07}, {0x1f8, 0x08}, {0x24e, 0x00}, + {0x20c, 0x04}, {0x221, 0x61}, {0x222, 0x68}, + {0x223, 0x6f}, {0x224, 0x76}, {0x225, 0x7d}, + {0x226, 0x84}, {0x227, 0x8d}, {0x24d, 0x08}, + {0x250, 0x05}, {0x251, 0xf5}, {0x252, 0x04}, + {0x253, 0xa0}, {0x254, 0x1f}, {0x255, 0x23}, + {0x256, 0x45}, {0x257, 0x67}, {0x258, 0x08}, + {0x259, 0x08}, {0x25a, 0x08}, {0x25b, 0x08}, + {0x260, 0x08}, {0x261, 0x08}, {0x262, 0x08}, + {0x263, 0x08}, {0x264, 0xcf}, {0x272, 0x56}, + {0x273, 0x9a}, {0x034, 0xf0}, {0x035, 0x0f}, + {0x05b, 0x40}, {0x084, 0x88}, {0x085, 0x24}, + {0x088, 0x54}, {0x08b, 0xb8}, {0x08c, 0x07}, + {0x08d, 0x00}, {0x094, 0x1b}, {0x095, 0x12}, + {0x096, 0x00}, {0x097, 0x06}, {0x09d, 0x1a}, + {0x09f, 0x10}, {0x0b4, 0x22}, {0x0be, 0x80}, + {0x0db, 0x00}, {0x0ee, 0x00}, {0x091, 0x03}, + {0x24c, 0x00}, {0x39f, 0x00}, {0x08c, 0x01}, + {0x08d, 0x10}, {0x08e, 0x08}, {0x08f, 0x00} +}; + +static struct urtw_pair urtw_8225v2_rf_part2[] = { + { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, + { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, + { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x08 }, { 0x0b, 0x80 }, + { 0x0c, 0x01 }, { 0x0d, 0x43 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, + { 0x10, 0x84 }, { 0x11, 0x07 }, { 0x12, 0x20 }, { 0x13, 0x20 }, + { 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, + { 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x15 }, + { 0x1c, 0x04 }, { 0x1d, 0xc5 }, { 0x1e, 0x95 }, { 0x1f, 0x75 }, + { 0x20, 0x1f }, { 0x21, 0x17 }, { 0x22, 0x16 }, { 0x23, 0x80 }, + { 0x24, 0x46 }, { 0x25, 0x00 }, { 0x26, 0x90 }, { 0x27, 0x88 } +}; + +static struct urtw_pair urtw_8225v2b_rf_part2[] = { + { 0x00, 0x10 }, { 0x01, 0x0d }, { 0x02, 0x01 }, { 0x03, 0x00 }, + { 0x04, 0x14 }, { 0x05, 0xfb }, { 0x06, 0xfb }, { 0x07, 0x60 }, + { 0x08, 0x00 }, { 0x09, 0x60 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, + { 0x0c, 0x00 }, { 0x0d, 0x5c }, { 0x0e, 0x00 }, { 0x0f, 0x00 }, + { 0x10, 0x40 }, { 0x11, 0x00 }, { 0x12, 0x40 }, { 0x13, 0x00 }, + { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0xa8 }, { 0x17, 0x26 }, + { 0x18, 0x32 }, { 0x19, 0x33 }, { 0x1a, 0x07 }, { 0x1b, 0xa5 }, + { 0x1c, 0x6f }, { 0x1d, 0x55 }, { 0x1e, 0xc8 }, { 0x1f, 0xb3 }, + { 0x20, 0x0a }, { 0x21, 0xe1 }, { 0x22, 0x2C }, { 0x23, 0x8a }, + { 0x24, 0x86 }, { 0x25, 0x83 }, { 0x26, 0x34 }, { 0x27, 0x0f }, + { 0x28, 0x4f }, { 0x29, 0x24 }, { 0x2a, 0x6f }, { 0x2b, 0xc2 }, + { 0x2c, 0x6b }, { 0x2d, 0x40 }, { 0x2e, 0x80 }, { 0x2f, 0x00 }, + { 0x30, 0xc0 }, { 0x31, 0xc1 }, { 0x32, 0x58 }, { 0x33, 0xf1 }, + { 0x34, 0x00 }, { 0x35, 0xe4 }, { 0x36, 0x90 }, { 0x37, 0x3e }, + { 0x38, 0x6d }, { 0x39, 0x3c }, { 0x3a, 0xfb }, { 0x3b, 0x07 } +}; + +static struct urtw_pair urtw_8225v2_rf_part3[] = { + { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, + { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x09, 0x11 }, + { 0x0a, 0x17 }, { 0x0b, 0x11 }, { 0x10, 0x9b }, { 0x11, 0x88 }, + { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 }, + { 0x1b, 0x08 }, { 0x1d, 0x00 }, { 0x40, 0x86 }, { 0x41, 0x9d }, + { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x36 }, { 0x45, 0x35 }, + { 0x46, 0x2e }, { 0x47, 0x25 }, { 0x48, 0x1c }, { 0x49, 0x12 }, + { 0x4a, 0x09 }, { 0x4b, 0x04 }, { 0x4c, 0x05 } +}; + +static uint16_t urtw_8225v2_rxgain[] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, + 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, + 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, + 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, + 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, + 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, + 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, + 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, + 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, + 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static uint16_t urtw_8225v2b_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static uint8_t urtw_8225v2_tx_gain_cck_ofdm[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, +}; + +static uint8_t urtw_8225v2_txpwr_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 +}; + +static uint8_t urtw_8225v2_txpwr_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static uint8_t urtw_8225v2b_txpwr_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, + 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 +}; + +static uint8_t urtw_8225v2b_txpwr_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static struct urtw_pair urtw_ratetable[] = { + { 2, 0 }, { 4, 1 }, { 11, 2 }, { 12, 4 }, { 18, 5 }, + { 22, 3 }, { 24, 6 }, { 36, 7 }, { 48, 8 }, { 72, 9 }, + { 96, 10 }, { 108, 11 } +}; + +#if 0 +static const uint8_t urtw_8187b_reg_table[][3] = { + { 0xf0, 0x32, 0 }, { 0xf1, 0x32, 0 }, { 0xf2, 0x00, 0 }, + { 0xf3, 0x00, 0 }, { 0xf4, 0x32, 0 }, { 0xf5, 0x43, 0 }, + { 0xf6, 0x00, 0 }, { 0xf7, 0x00, 0 }, { 0xf8, 0x46, 0 }, + { 0xf9, 0xa4, 0 }, { 0xfa, 0x00, 0 }, { 0xfb, 0x00, 0 }, + { 0xfc, 0x96, 0 }, { 0xfd, 0xa4, 0 }, { 0xfe, 0x00, 0 }, + { 0xff, 0x00, 0 }, { 0x58, 0x4b, 1 }, { 0x59, 0x00, 1 }, + { 0x5a, 0x4b, 1 }, { 0x5b, 0x00, 1 }, { 0x60, 0x4b, 1 }, + { 0x61, 0x09, 1 }, { 0x62, 0x4b, 1 }, { 0x63, 0x09, 1 }, + { 0xce, 0x0f, 1 }, { 0xcf, 0x00, 1 }, { 0xe0, 0xff, 1 }, + { 0xe1, 0x0f, 1 }, { 0xe2, 0x00, 1 }, { 0xf0, 0x4e, 1 }, + { 0xf1, 0x01, 1 }, { 0xf2, 0x02, 1 }, { 0xf3, 0x03, 1 }, + { 0xf4, 0x04, 1 }, { 0xf5, 0x05, 1 }, { 0xf6, 0x06, 1 }, + { 0xf7, 0x07, 1 }, { 0xf8, 0x08, 1 }, { 0x4e, 0x00, 2 }, + { 0x0c, 0x04, 2 }, { 0x21, 0x61, 2 }, { 0x22, 0x68, 2 }, + { 0x23, 0x6f, 2 }, { 0x24, 0x76, 2 }, { 0x25, 0x7d, 2 }, + { 0x26, 0x84, 2 }, { 0x27, 0x8d, 2 }, { 0x4d, 0x08, 2 }, + { 0x50, 0x05, 2 }, { 0x51, 0xf5, 2 }, { 0x52, 0x04, 2 }, + { 0x53, 0xa0, 2 }, { 0x54, 0x1f, 2 }, { 0x55, 0x23, 2 }, + { 0x56, 0x45, 2 }, { 0x57, 0x67, 2 }, { 0x58, 0x08, 2 }, + { 0x59, 0x08, 2 }, { 0x5a, 0x08, 2 }, { 0x5b, 0x08, 2 }, + { 0x60, 0x08, 2 }, { 0x61, 0x08, 2 }, { 0x62, 0x08, 2 }, + { 0x63, 0x08, 2 }, { 0x64, 0xcf, 2 }, { 0x72, 0x56, 2 }, + { 0x73, 0x9a, 2 }, { 0x34, 0xf0, 0 }, { 0x35, 0x0f, 0 }, + { 0x5b, 0x40, 0 }, { 0x84, 0x88, 0 }, { 0x85, 0x24, 0 }, + { 0x88, 0x54, 0 }, { 0x8b, 0xb8, 0 }, { 0x8c, 0x07, 0 }, + { 0x8d, 0x00, 0 }, { 0x94, 0x1b, 0 }, { 0x95, 0x12, 0 }, + { 0x96, 0x00, 0 }, { 0x97, 0x06, 0 }, { 0x9d, 0x1a, 0 }, + { 0x9f, 0x10, 0 }, { 0xb4, 0x22, 0 }, { 0xbe, 0x80, 0 }, + { 0xdb, 0x00, 0 }, { 0xee, 0x00, 0 }, { 0x91, 0x03, 0 }, + { 0x4c, 0x00, 2 }, { 0x9f, 0x00, 3 }, { 0x8c, 0x01, 0 }, + { 0x8d, 0x10, 0 }, { 0x8e, 0x08, 0 }, { 0x8f, 0x00, 0 } +}; +#endif + +static usb_callback_t urtw_bulk_rx_callback; +static usb_callback_t urtw_bulk_tx_callback; +static usb_callback_t urtw_bulk_tx_status_callback; + +static const struct usb_config urtw_8187b_usbconfig[URTW_8187B_N_XFERS] = { + [URTW_8187B_BULK_RX] = { + .type = UE_BULK, + .endpoint = 0x83, + .direction = UE_DIR_IN, + .bufsize = MCLBYTES, + .flags = { + .ext_buffer = 1, + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = urtw_bulk_rx_callback + }, + [URTW_8187B_BULK_TX_STATUS] = { + .type = UE_BULK, + .endpoint = 0x89, + .direction = UE_DIR_IN, + .bufsize = sizeof(uint64_t), + .flags = { + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = urtw_bulk_tx_status_callback + }, + [URTW_8187B_BULK_TX_BE] = { + .type = UE_BULK, + .endpoint = URTW_8187B_TXPIPE_BE, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE * URTW_TX_DATA_LIST_COUNT, + .flags = { + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187B_BULK_TX_BK] = { + .type = UE_BULK, + .endpoint = URTW_8187B_TXPIPE_BK, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187B_BULK_TX_VI] = { + .type = UE_BULK, + .endpoint = URTW_8187B_TXPIPE_VI, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187B_BULK_TX_VO] = { + .type = UE_BULK, + .endpoint = URTW_8187B_TXPIPE_VO, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187B_BULK_TX_EP12] = { + .type = UE_BULK, + .endpoint = 0xc, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + } +}; + +static const struct usb_config urtw_8187l_usbconfig[URTW_8187L_N_XFERS] = { + [URTW_8187L_BULK_RX] = { + .type = UE_BULK, + .endpoint = 0x81, + .direction = UE_DIR_IN, + .bufsize = MCLBYTES, + .flags = { + .ext_buffer = 1, + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = urtw_bulk_rx_callback + }, + [URTW_8187L_BULK_TX_LOW] = { + .type = UE_BULK, + .endpoint = 0x2, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE * URTW_TX_DATA_LIST_COUNT, + .flags = { + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187L_BULK_TX_NORMAL] = { + .type = UE_BULK, + .endpoint = 0x3, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, +}; + +static struct ieee80211vap *urtw_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, + int, const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void urtw_vap_delete(struct ieee80211vap *); +static void urtw_init(struct urtw_softc *); +static void urtw_stop(struct urtw_softc *); +static void urtw_parent(struct ieee80211com *); +static int urtw_transmit(struct ieee80211com *, struct mbuf *); +static void urtw_start(struct urtw_softc *); +static int urtw_alloc_rx_data_list(struct urtw_softc *); +static int urtw_alloc_tx_data_list(struct urtw_softc *); +static int urtw_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void urtw_scan_start(struct ieee80211com *); +static void urtw_scan_end(struct ieee80211com *); +static void urtw_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static void urtw_set_channel(struct ieee80211com *); +static void urtw_update_mcast(struct ieee80211com *); +static int urtw_tx_start(struct urtw_softc *, + struct ieee80211_node *, struct mbuf *, + struct urtw_data *, int); +static int urtw_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static void urtw_led_ch(void *); +static void urtw_ledtask(void *, int); +static void urtw_watchdog(void *); +static void urtw_set_multi(void *); +static int urtw_isbmode(uint16_t); +static uint16_t urtw_rate2rtl(uint32_t); +static uint16_t urtw_rtl2rate(uint32_t); +static usb_error_t urtw_set_rate(struct urtw_softc *); +static usb_error_t urtw_update_msr(struct urtw_softc *); +static usb_error_t urtw_read8_c(struct urtw_softc *, int, uint8_t *); +static usb_error_t urtw_read16_c(struct urtw_softc *, int, uint16_t *); +static usb_error_t urtw_read32_c(struct urtw_softc *, int, uint32_t *); +static usb_error_t urtw_write8_c(struct urtw_softc *, int, uint8_t); +static usb_error_t urtw_write16_c(struct urtw_softc *, int, uint16_t); +static usb_error_t urtw_write32_c(struct urtw_softc *, int, uint32_t); +static usb_error_t urtw_eprom_cs(struct urtw_softc *, int); +static usb_error_t urtw_eprom_ck(struct urtw_softc *); +static usb_error_t urtw_eprom_sendbits(struct urtw_softc *, int16_t *, + int); +static usb_error_t urtw_eprom_read32(struct urtw_softc *, uint32_t, + uint32_t *); +static usb_error_t urtw_eprom_readbit(struct urtw_softc *, int16_t *); +static usb_error_t urtw_eprom_writebit(struct urtw_softc *, int16_t); +static usb_error_t urtw_get_macaddr(struct urtw_softc *); +static usb_error_t urtw_get_txpwr(struct urtw_softc *); +static usb_error_t urtw_get_rfchip(struct urtw_softc *); +static usb_error_t urtw_led_init(struct urtw_softc *); +static usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *); +static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *, uint8_t); +static usb_error_t urtw_8187_write_phy(struct urtw_softc *, uint8_t, + uint32_t); +static usb_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *, + uint8_t, uint32_t); +static usb_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t, + uint32_t); +static usb_error_t urtw_8225_setgain(struct urtw_softc *, int16_t); +static usb_error_t urtw_8225_usb_init(struct urtw_softc *); +static usb_error_t urtw_8225_write_c(struct urtw_softc *, uint8_t, + uint16_t); +static usb_error_t urtw_8225_write_s16(struct urtw_softc *, uint8_t, int, + uint16_t *); +static usb_error_t urtw_8225_read(struct urtw_softc *, uint8_t, + uint32_t *); +static usb_error_t urtw_8225_rf_init(struct urtw_softc *); +static usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *, int); +static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *, int); +static usb_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *, int); +static usb_error_t urtw_8225_rf_stop(struct urtw_softc *); +static usb_error_t urtw_8225v2_rf_init(struct urtw_softc *); +static usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *, int); +static usb_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int); +static usb_error_t urtw_8225v2_setgain(struct urtw_softc *, int16_t); +static usb_error_t urtw_8225_isv2(struct urtw_softc *, int *); +static usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *); +static usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *, int); +static usb_error_t urtw_read8e(struct urtw_softc *, int, uint8_t *); +static usb_error_t urtw_write8e(struct urtw_softc *, int, uint8_t); +static usb_error_t urtw_8180_set_anaparam(struct urtw_softc *, uint32_t); +static usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t); +static usb_error_t urtw_intr_enable(struct urtw_softc *); +static usb_error_t urtw_intr_disable(struct urtw_softc *); +static usb_error_t urtw_reset(struct urtw_softc *); +static usb_error_t urtw_led_on(struct urtw_softc *, int); +static usb_error_t urtw_led_ctl(struct urtw_softc *, int); +static usb_error_t urtw_led_blink(struct urtw_softc *); +static usb_error_t urtw_led_mode0(struct urtw_softc *, int); +static usb_error_t urtw_led_mode1(struct urtw_softc *, int); +static usb_error_t urtw_led_mode2(struct urtw_softc *, int); +static usb_error_t urtw_led_mode3(struct urtw_softc *, int); +static usb_error_t urtw_rx_setconf(struct urtw_softc *); +static usb_error_t urtw_rx_enable(struct urtw_softc *); +static usb_error_t urtw_tx_enable(struct urtw_softc *sc); +static void urtw_free_tx_data_list(struct urtw_softc *); +static void urtw_free_rx_data_list(struct urtw_softc *); +static void urtw_free_data_list(struct urtw_softc *, + struct urtw_data data[], int, int); +static usb_error_t urtw_adapter_start(struct urtw_softc *); +static usb_error_t urtw_adapter_start_b(struct urtw_softc *); +static usb_error_t urtw_set_mode(struct urtw_softc *, uint32_t); +static usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *); +static usb_error_t urtw_do_request(struct urtw_softc *, + struct usb_device_request *, void *); +static usb_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *, int); +static usb_error_t urtw_led_off(struct urtw_softc *, int); +static void urtw_abort_xfers(struct urtw_softc *); +static struct urtw_data * + urtw_getbuf(struct urtw_softc *sc); +static int urtw_compute_txtime(uint16_t, uint16_t, uint8_t, + uint8_t); +static void urtw_updateslot(struct ieee80211com *); +static void urtw_updateslottask(void *, int); +static void urtw_sysctl_node(struct urtw_softc *); + +static int +urtw_match(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != URTW_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != URTW_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(urtw_devs, sizeof(urtw_devs), uaa)); +} + +static int +urtw_attach(device_t dev) +{ + const struct usb_config *setup_start; + int ret = ENXIO; + struct urtw_softc *sc = device_get_softc(dev); + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct ieee80211com *ic = &sc->sc_ic; + uint8_t iface_index = URTW_IFACE_INDEX; /* XXX */ + uint16_t n_setup; + uint32_t data; + usb_error_t error; + + device_set_usb_desc(dev); + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + if (USB_GET_DRIVER_INFO(uaa) == URTW_REV_RTL8187B) + sc->sc_flags |= URTW_RTL8187B; +#ifdef URTW_DEBUG + sc->sc_debug = urtw_debug; +#endif + + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, + MTX_DEF); + usb_callout_init_mtx(&sc->sc_led_ch, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_led_task, 0, urtw_ledtask, sc); + TASK_INIT(&sc->sc_updateslot_task, 0, urtw_updateslottask, sc); + callout_init(&sc->sc_watchdog_ch, 0); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + if (sc->sc_flags & URTW_RTL8187B) { + setup_start = urtw_8187b_usbconfig; + n_setup = URTW_8187B_N_XFERS; + } else { + setup_start = urtw_8187l_usbconfig; + n_setup = URTW_8187L_N_XFERS; + } + + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + setup_start, n_setup, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + ret = ENXIO; + goto fail0; + } + + if (sc->sc_flags & URTW_RTL8187B) { + sc->sc_tx_dma_buf = + usbd_xfer_get_frame_buffer(sc->sc_xfer[ + URTW_8187B_BULK_TX_BE], 0); + } else { + sc->sc_tx_dma_buf = + usbd_xfer_get_frame_buffer(sc->sc_xfer[ + URTW_8187L_BULK_TX_LOW], 0); + } + + URTW_LOCK(sc); + + urtw_read32_m(sc, URTW_RX, &data); + sc->sc_epromtype = (data & URTW_RX_9356SEL) ? URTW_EEPROM_93C56 : + URTW_EEPROM_93C46; + + error = urtw_get_rfchip(sc); + if (error != 0) + goto fail; + error = urtw_get_macaddr(sc); + if (error != 0) + goto fail; + error = urtw_get_txpwr(sc); + if (error != 0) + goto fail; + error = urtw_led_init(sc); + if (error != 0) + goto fail; + + URTW_UNLOCK(sc); + + sc->sc_rts_retry = URTW_DEFAULT_RTS_RETRY; + sc->sc_tx_retry = URTW_DEFAULT_TX_RETRY; + sc->sc_currate = 3; + sc->sc_preamble_mode = urtw_preamble_mode; + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(dev); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA | /* station mode */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_TXPMGT | /* tx power management */ + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_BGSCAN | /* capable of bg scanning */ + IEEE80211_C_WPA; /* 802.11i */ + + /* XXX TODO: setup regdomain if URTW_EPROM_CHANPLAN_BY_HW bit is set.*/ + + urtw_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + ic->ic_raw_xmit = urtw_raw_xmit; + ic->ic_scan_start = urtw_scan_start; + ic->ic_scan_end = urtw_scan_end; + ic->ic_getradiocaps = urtw_getradiocaps; + ic->ic_set_channel = urtw_set_channel; + ic->ic_updateslot = urtw_updateslot; + ic->ic_vap_create = urtw_vap_create; + ic->ic_vap_delete = urtw_vap_delete; + ic->ic_update_mcast = urtw_update_mcast; + ic->ic_parent = urtw_parent; + ic->ic_transmit = urtw_transmit; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + URTW_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + URTW_RX_RADIOTAP_PRESENT); + + urtw_sysctl_node(sc); + + if (bootverbose) + ieee80211_announce(ic); + return (0); + +fail: + URTW_UNLOCK(sc); + usbd_transfer_unsetup(sc->sc_xfer, (sc->sc_flags & URTW_RTL8187B) ? + URTW_8187B_N_XFERS : URTW_8187L_N_XFERS); +fail0: + return (ret); +} + +static int +urtw_detach(device_t dev) +{ + struct urtw_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + unsigned int x; + unsigned int n_xfers; + + /* Prevent further ioctls */ + URTW_LOCK(sc); + sc->sc_flags |= URTW_DETACHED; + urtw_stop(sc); + URTW_UNLOCK(sc); + + ieee80211_draintask(ic, &sc->sc_updateslot_task); + ieee80211_draintask(ic, &sc->sc_led_task); + + usb_callout_drain(&sc->sc_led_ch); + callout_drain(&sc->sc_watchdog_ch); + + n_xfers = (sc->sc_flags & URTW_RTL8187B) ? + URTW_8187B_N_XFERS : URTW_8187L_N_XFERS; + + /* prevent further allocations from RX/TX data lists */ + URTW_LOCK(sc); + STAILQ_INIT(&sc->sc_tx_active); + STAILQ_INIT(&sc->sc_tx_inactive); + STAILQ_INIT(&sc->sc_tx_pending); + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + URTW_UNLOCK(sc); + + /* drain USB transfers */ + for (x = 0; x != n_xfers; x++) + usbd_transfer_drain(sc->sc_xfer[x]); + + /* free data buffers */ + URTW_LOCK(sc); + urtw_free_tx_data_list(sc); + urtw_free_rx_data_list(sc); + URTW_UNLOCK(sc); + + /* free USB transfers and some data buffers */ + usbd_transfer_unsetup(sc->sc_xfer, n_xfers); + + ieee80211_ifdetach(ic); + mbufq_drain(&sc->sc_snd); + mtx_destroy(&sc->sc_mtx); + return (0); +} + +static void +urtw_free_tx_data_list(struct urtw_softc *sc) +{ + urtw_free_data_list(sc, sc->sc_tx, URTW_TX_DATA_LIST_COUNT, 0); +} + +static void +urtw_free_rx_data_list(struct urtw_softc *sc) +{ + urtw_free_data_list(sc, sc->sc_rx, URTW_RX_DATA_LIST_COUNT, 1); +} + +static void +urtw_free_data_list(struct urtw_softc *sc, struct urtw_data data[], int ndata, + int fillmbuf) +{ + int i; + + for (i = 0; i < ndata; i++) { + struct urtw_data *dp = &data[i]; + + if (fillmbuf == 1) { + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + dp->buf = NULL; + } + } else { + dp->buf = NULL; + } + if (dp->ni != NULL) { + ieee80211_free_node(dp->ni); + dp->ni = NULL; + } + } +} + +static struct ieee80211vap * +urtw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct urtw_vap *uvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return (NULL); + uvp = malloc(sizeof(struct urtw_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &uvp->vap; + /* enable s/w bmiss handling for sta mode */ + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(uvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + vap->iv_newstate = urtw_newstate; + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return (vap); +} + +static void +urtw_vap_delete(struct ieee80211vap *vap) +{ + struct urtw_vap *uvp = URTW_VAP(vap); + + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static void +urtw_init(struct urtw_softc *sc) +{ + usb_error_t error; + int ret; + + URTW_ASSERT_LOCKED(sc); + + if (sc->sc_flags & URTW_RUNNING) + urtw_stop(sc); + + error = (sc->sc_flags & URTW_RTL8187B) ? urtw_adapter_start_b(sc) : + urtw_adapter_start(sc); + if (error != 0) + goto fail; + + /* reset softc variables */ + sc->sc_txtimer = 0; + + if (!(sc->sc_flags & URTW_INIT_ONCE)) { + ret = urtw_alloc_rx_data_list(sc); + if (ret != 0) + goto fail; + ret = urtw_alloc_tx_data_list(sc); + if (ret != 0) + goto fail; + sc->sc_flags |= URTW_INIT_ONCE; + } + + error = urtw_rx_enable(sc); + if (error != 0) + goto fail; + error = urtw_tx_enable(sc); + if (error != 0) + goto fail; + + if (sc->sc_flags & URTW_RTL8187B) + usbd_transfer_start(sc->sc_xfer[URTW_8187B_BULK_TX_STATUS]); + + sc->sc_flags |= URTW_RUNNING; + + callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); +fail: + return; +} + +static usb_error_t +urtw_adapter_start_b(struct urtw_softc *sc) +{ + uint8_t data8; + usb_error_t error; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_CONFIG3, &data8); + urtw_write8_m(sc, URTW_CONFIG3, + data8 | URTW_CONFIG3_ANAPARAM_WRITE | URTW_CONFIG3_GNT_SELECT); + urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8187B_8225_ANAPARAM2_ON); + urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_ON); + urtw_write8_m(sc, URTW_ANAPARAM3, URTW_8187B_8225_ANAPARAM3_ON); + + urtw_write8_m(sc, 0x61, 0x10); + urtw_read8_m(sc, 0x62, &data8); + urtw_write8_m(sc, 0x62, data8 & ~(1 << 5)); + urtw_write8_m(sc, 0x62, data8 | (1 << 5)); + + urtw_read8_m(sc, URTW_CONFIG3, &data8); + data8 &= ~URTW_CONFIG3_ANAPARAM_WRITE; + urtw_write8_m(sc, URTW_CONFIG3, data8); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_8187b_cmd_reset(sc); + if (error) + goto fail; + + error = sc->sc_rf_init(sc); + if (error != 0) + goto fail; + urtw_write8_m(sc, URTW_CMD, URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE); + + /* fix RTL8187B RX stall */ + error = urtw_intr_enable(sc); + if (error) + goto fail; + + error = urtw_write8e(sc, 0x41, 0xf4); + if (error) + goto fail; + error = urtw_write8e(sc, 0x40, 0x00); + if (error) + goto fail; + error = urtw_write8e(sc, 0x42, 0x00); + if (error) + goto fail; + error = urtw_write8e(sc, 0x42, 0x01); + if (error) + goto fail; + error = urtw_write8e(sc, 0x40, 0x0f); + if (error) + goto fail; + error = urtw_write8e(sc, 0x42, 0x00); + if (error) + goto fail; + error = urtw_write8e(sc, 0x42, 0x01); + if (error) + goto fail; + + urtw_read8_m(sc, 0xdb, &data8); + urtw_write8_m(sc, 0xdb, data8 | (1 << 2)); + urtw_write16_m(sc, 0x372, 0x59fa); + urtw_write16_m(sc, 0x374, 0x59d2); + urtw_write16_m(sc, 0x376, 0x59d2); + urtw_write16_m(sc, 0x378, 0x19fa); + urtw_write16_m(sc, 0x37a, 0x19fa); + urtw_write16_m(sc, 0x37c, 0x00d0); + urtw_write8_m(sc, 0x61, 0); + + urtw_write8_m(sc, 0x180, 0x0f); + urtw_write8_m(sc, 0x183, 0x03); + urtw_write8_m(sc, 0xda, 0x10); + urtw_write8_m(sc, 0x24d, 0x08); + urtw_write32_m(sc, URTW_HSSI_PARA, 0x0600321b); + + urtw_write16_m(sc, 0x1ec, 0x800); /* RX MAX SIZE */ +fail: + return (error); +} + +static usb_error_t +urtw_adapter_start(struct urtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + usb_error_t error; + + error = urtw_reset(sc); + if (error) + goto fail; + + urtw_write8_m(sc, URTW_ADDR_MAGIC1, 0); + urtw_write8_m(sc, URTW_GPIO, 0); + + /* for led */ + urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4); + error = urtw_led_ctl(sc, URTW_LED_CTL_POWER_ON); + if (error != 0) + goto fail; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + /* applying MAC address again. */ + urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)ic->ic_macaddr)[0]); + urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)ic->ic_macaddr)[1] & 0xffff); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_update_msr(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_INT_TIMEOUT, 0); + urtw_write8_m(sc, URTW_WPA_CONFIG, 0); + urtw_write8_m(sc, URTW_RATE_FALLBACK, URTW_RATE_FALLBACK_ENABLE | 0x1); + error = urtw_set_rate(sc); + if (error != 0) + goto fail; + + error = sc->sc_rf_init(sc); + if (error != 0) + goto fail; + if (sc->sc_rf_set_sens != NULL) + sc->sc_rf_set_sens(sc, sc->sc_sens); + + /* XXX correct? to call write16 */ + urtw_write16_m(sc, URTW_PSR, 1); + urtw_write16_m(sc, URTW_ADDR_MAGIC2, 0x10); + urtw_write8_m(sc, URTW_TALLY_SEL, 0x80); + urtw_write8_m(sc, URTW_ADDR_MAGIC3, 0x60); + /* XXX correct? to call write16 */ + urtw_write16_m(sc, URTW_PSR, 0); + urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4); + + error = urtw_intr_enable(sc); + if (error != 0) + goto fail; + +fail: + return (error); +} + +static usb_error_t +urtw_set_mode(struct urtw_softc *sc, uint32_t mode) +{ + uint8_t data; + usb_error_t error; + + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + data = (data & ~URTW_EPROM_CMD_MASK) | (mode << URTW_EPROM_CMD_SHIFT); + data = data & ~(URTW_EPROM_CS | URTW_EPROM_CK); + urtw_write8_m(sc, URTW_EPROM_CMD, data); +fail: + return (error); +} + +static usb_error_t +urtw_8187b_cmd_reset(struct urtw_softc *sc) +{ + int i; + uint8_t data8; + usb_error_t error; + + /* XXX the code can be duplicate with urtw_reset(). */ + urtw_read8_m(sc, URTW_CMD, &data8); + data8 = (data8 & 0x2) | URTW_CMD_RST; + urtw_write8_m(sc, URTW_CMD, data8); + + for (i = 0; i < 20; i++) { + usb_pause_mtx(&sc->sc_mtx, 2); + urtw_read8_m(sc, URTW_CMD, &data8); + if (!(data8 & URTW_CMD_RST)) + break; + } + if (i >= 20) { + device_printf(sc->sc_dev, "reset timeout\n"); + goto fail; + } +fail: + return (error); +} + +static usb_error_t +urtw_do_request(struct urtw_softc *sc, + struct usb_device_request *req, void *data) +{ + usb_error_t err; + int ntries = 10; + + URTW_ASSERT_LOCKED(sc); + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0) + break; + + DPRINTF(sc, URTW_DEBUG_INIT, + "Control request failed, %s (retrying)\n", + usbd_errstr(err)); + usb_pause_mtx(&sc->sc_mtx, hz / 100); + } + return (err); +} + +static void +urtw_stop(struct urtw_softc *sc) +{ + uint8_t data8; + usb_error_t error; + + URTW_ASSERT_LOCKED(sc); + + sc->sc_flags &= ~URTW_RUNNING; + + error = urtw_intr_disable(sc); + if (error) + goto fail; + urtw_read8_m(sc, URTW_CMD, &data8); + data8 &= ~(URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE); + urtw_write8_m(sc, URTW_CMD, data8); + + error = sc->sc_rf_stop(sc); + if (error != 0) + goto fail; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_read8_m(sc, URTW_CONFIG4, &data8); + urtw_write8_m(sc, URTW_CONFIG4, data8 | URTW_CONFIG4_VCOOFF); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; +fail: + if (error) + device_printf(sc->sc_dev, "failed to stop (%s)\n", + usbd_errstr(error)); + + usb_callout_stop(&sc->sc_led_ch); + callout_stop(&sc->sc_watchdog_ch); + + urtw_abort_xfers(sc); +} + +static void +urtw_abort_xfers(struct urtw_softc *sc) +{ + int i, max; + + URTW_ASSERT_LOCKED(sc); + + max = (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS : + URTW_8187L_N_XFERS; + + /* abort any pending transfers */ + for (i = 0; i < max; i++) + usbd_transfer_stop(sc->sc_xfer[i]); +} + +static void +urtw_parent(struct ieee80211com *ic) +{ + struct urtw_softc *sc = ic->ic_softc; + int startall = 0; + + URTW_LOCK(sc); + if (sc->sc_flags & URTW_DETACHED) { + URTW_UNLOCK(sc); + return; + } + + if (ic->ic_nrunning > 0) { + if (sc->sc_flags & URTW_RUNNING) { + if (ic->ic_promisc > 0 || ic->ic_allmulti > 0) + urtw_set_multi(sc); + } else { + urtw_init(sc); + startall = 1; + } + } else if (sc->sc_flags & URTW_RUNNING) + urtw_stop(sc); + URTW_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static int +urtw_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct urtw_softc *sc = ic->ic_softc; + int error; + + URTW_LOCK(sc); + if ((sc->sc_flags & URTW_RUNNING) == 0) { + URTW_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + URTW_UNLOCK(sc); + return (error); + } + urtw_start(sc); + URTW_UNLOCK(sc); + + return (0); +} + +static void +urtw_start(struct urtw_softc *sc) +{ + struct urtw_data *bf; + struct ieee80211_node *ni; + struct mbuf *m; + + URTW_ASSERT_LOCKED(sc); + + if ((sc->sc_flags & URTW_RUNNING) == 0) + return; + + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + bf = urtw_getbuf(sc); + if (bf == NULL) { + mbufq_prepend(&sc->sc_snd, m); + break; + } + + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + + if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_NORMAL) != 0) { + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); + ieee80211_free_node(ni); + break; + } + + sc->sc_txtimer = 5; + callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); + } +} + +static int +urtw_alloc_data_list(struct urtw_softc *sc, struct urtw_data data[], + int ndata, int maxsz, void *dma_buf) +{ + int i, error; + + for (i = 0; i < ndata; i++) { + struct urtw_data *dp = &data[i]; + + dp->sc = sc; + if (dma_buf == NULL) { + dp->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (dp->m == NULL) { + device_printf(sc->sc_dev, + "could not allocate rx mbuf\n"); + error = ENOMEM; + goto fail; + } + dp->buf = mtod(dp->m, uint8_t *); + } else { + dp->m = NULL; + dp->buf = ((uint8_t *)dma_buf) + + (i * maxsz); + } + dp->ni = NULL; + } + return (0); + +fail: urtw_free_data_list(sc, data, ndata, 1); + return (error); +} + +static int +urtw_alloc_rx_data_list(struct urtw_softc *sc) +{ + int error, i; + + error = urtw_alloc_data_list(sc, + sc->sc_rx, URTW_RX_DATA_LIST_COUNT, + MCLBYTES, NULL /* mbufs */); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + + for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); + + return (0); +} + +static int +urtw_alloc_tx_data_list(struct urtw_softc *sc) +{ + int error, i; + + error = urtw_alloc_data_list(sc, + sc->sc_tx, URTW_TX_DATA_LIST_COUNT, URTW_TX_MAXSIZE, + sc->sc_tx_dma_buf /* no mbufs */); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_tx_active); + STAILQ_INIT(&sc->sc_tx_inactive); + STAILQ_INIT(&sc->sc_tx_pending); + + for (i = 0; i < URTW_TX_DATA_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], + next); + + return (0); +} + +static int +urtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct urtw_softc *sc = ic->ic_softc; + struct urtw_data *bf; + + /* prevent management frames from being sent if we're not ready */ + if (!(sc->sc_flags & URTW_RUNNING)) { + m_freem(m); + return ENETDOWN; + } + URTW_LOCK(sc); + bf = urtw_getbuf(sc); + if (bf == NULL) { + m_freem(m); + URTW_UNLOCK(sc); + return (ENOBUFS); /* XXX */ + } + + if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_LOW) != 0) { + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); + URTW_UNLOCK(sc); + return (EIO); + } + URTW_UNLOCK(sc); + + sc->sc_txtimer = 5; + return (0); +} + +static void +urtw_scan_start(struct ieee80211com *ic) +{ + + /* XXX do nothing? */ +} + +static void +urtw_scan_end(struct ieee80211com *ic) +{ + + /* XXX do nothing? */ +} + +static void +urtw_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + urtw_chan_2ghz, nitems(urtw_chan_2ghz), bands, 0); +} + +static void +urtw_set_channel(struct ieee80211com *ic) +{ + struct urtw_softc *sc = ic->ic_softc; + uint32_t data, orig; + usb_error_t error; + + /* + * if the user set a channel explicitly using ifconfig(8) this function + * can be called earlier than we're expected that in some cases the + * initialization would be failed if setting a channel is called before + * the init have done. + */ + if (!(sc->sc_flags & URTW_RUNNING)) + return; + + if (sc->sc_curchan != NULL && sc->sc_curchan == ic->ic_curchan) + return; + + URTW_LOCK(sc); + + /* + * during changing th channel we need to temporarily be disable + * TX. + */ + urtw_read32_m(sc, URTW_TX_CONF, &orig); + data = orig & ~URTW_TX_LOOPBACK_MASK; + urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_MAC); + + error = sc->sc_rf_set_chan(sc, ieee80211_chan2ieee(ic, ic->ic_curchan)); + if (error != 0) + goto fail; + usb_pause_mtx(&sc->sc_mtx, 10); + urtw_write32_m(sc, URTW_TX_CONF, orig); + + urtw_write16_m(sc, URTW_ATIM_WND, 2); + urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); + urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); + urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); + +fail: + URTW_UNLOCK(sc); + + sc->sc_curchan = ic->ic_curchan; + + if (error != 0) + device_printf(sc->sc_dev, "could not change the channel\n"); +} + +static void +urtw_update_mcast(struct ieee80211com *ic) +{ + + /* XXX do nothing? */ +} + +static int +urtw_tx_start(struct urtw_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, + struct urtw_data *data, int prior) +{ + struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); + struct ieee80211_key *k; + const struct ieee80211_txparam *tp; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct usb_xfer *rtl8187b_pipes[URTW_8187B_TXPIPE_MAX] = { + sc->sc_xfer[URTW_8187B_BULK_TX_BE], + sc->sc_xfer[URTW_8187B_BULK_TX_BK], + sc->sc_xfer[URTW_8187B_BULK_TX_VI], + sc->sc_xfer[URTW_8187B_BULK_TX_VO] + }; + struct usb_xfer *xfer; + int dur = 0, rtsdur = 0, rtsenable = 0, ctsenable = 0, rate, + pkttime = 0, txdur = 0, isshort = 0, xferlen; + uint16_t acktime, rtstime, ctstime; + uint32_t flags; + usb_error_t error; + + URTW_ASSERT_LOCKED(sc); + + /* + * Software crypto. + */ + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + device_printf(sc->sc_dev, + "ieee80211_crypto_encap returns NULL.\n"); + /* XXX we don't expect the fragmented frames */ + m_freem(m0); + return (ENOBUFS); + } + + /* in case packet header moved, reset pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (ieee80211_radiotap_active_vap(vap)) { + struct urtw_tx_radiotap_header *tap = &sc->sc_txtap; + + /* XXX Are variables correct? */ + tap->wt_flags = 0; + tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); + + ieee80211_radiotap_tx(vap, m0); + } + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT || + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) { + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + rate = tp->mgmtrate; + } else { + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + /* for data frames */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else + rate = urtw_rtl2rate(sc->sc_currate); + } + + sc->sc_stats.txrates[sc->sc_currate]++; + + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + txdur = pkttime = urtw_compute_txtime(m0->m_pkthdr.len + + IEEE80211_CRC_LEN, rate, 0, 0); + else { + acktime = urtw_compute_txtime(14, 2,0, 0); + if ((m0->m_pkthdr.len + 4) > vap->iv_rtsthreshold) { + rtsenable = 1; + ctsenable = 0; + rtstime = urtw_compute_txtime(URTW_ACKCTS_LEN, 2, 0, 0); + ctstime = urtw_compute_txtime(14, 2, 0, 0); + pkttime = urtw_compute_txtime(m0->m_pkthdr.len + + IEEE80211_CRC_LEN, rate, 0, isshort); + rtsdur = ctstime + pkttime + acktime + + 3 * URTW_ASIFS_TIME; + txdur = rtstime + rtsdur; + } else { + rtsenable = ctsenable = rtsdur = 0; + pkttime = urtw_compute_txtime(m0->m_pkthdr.len + + IEEE80211_CRC_LEN, rate, 0, isshort); + txdur = pkttime + URTW_ASIFS_TIME + acktime; + } + + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + dur = urtw_compute_txtime(m0->m_pkthdr.len + + IEEE80211_CRC_LEN, rate, 0, isshort) + + 3 * URTW_ASIFS_TIME + + 2 * acktime; + else + dur = URTW_ASIFS_TIME + acktime; + } + USETW(wh->i_dur, dur); + + xferlen = m0->m_pkthdr.len; + xferlen += (sc->sc_flags & URTW_RTL8187B) ? (4 * 8) : (4 * 3); + if ((0 == xferlen % 64) || (0 == xferlen % 512)) + xferlen += 1; + + memset(data->buf, 0, URTW_TX_MAXSIZE); + flags = m0->m_pkthdr.len & 0xfff; + flags |= URTW_TX_FLAG_NO_ENC; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) && + (sc->sc_preamble_mode == URTW_PREAMBLE_MODE_SHORT) && + (sc->sc_currate != 0)) + flags |= URTW_TX_FLAG_SPLCP; + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + flags |= URTW_TX_FLAG_MOREFRAG; + + flags |= (sc->sc_currate & 0xf) << URTW_TX_FLAG_TXRATE_SHIFT; + + if (sc->sc_flags & URTW_RTL8187B) { + struct urtw_8187b_txhdr *tx; + + tx = (struct urtw_8187b_txhdr *)data->buf; + if (ctsenable) + flags |= URTW_TX_FLAG_CTS; + if (rtsenable) { + flags |= URTW_TX_FLAG_RTS; + flags |= (urtw_rate2rtl(11) & 0xf) << + URTW_TX_FLAG_RTSRATE_SHIFT; + tx->rtsdur = rtsdur; + } + tx->flag = htole32(flags); + tx->txdur = txdur; + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_MGT && + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + tx->retry = 1; + else + tx->retry = URTW_TX_MAXRETRY; + m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1)); + } else { + struct urtw_8187l_txhdr *tx; + + tx = (struct urtw_8187l_txhdr *)data->buf; + if (rtsenable) { + flags |= URTW_TX_FLAG_RTS; + tx->rtsdur = rtsdur; + } + flags |= (urtw_rate2rtl(11) & 0xf) << URTW_TX_FLAG_RTSRATE_SHIFT; + tx->flag = htole32(flags); + tx->retry = 3; /* CW minimum */ + tx->retry |= 7 << 4; /* CW maximum */ + tx->retry |= URTW_TX_MAXRETRY << 8; /* retry limitation */ + m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1)); + } + + data->buflen = xferlen; + data->ni = ni; + data->m = m0; + + if (sc->sc_flags & URTW_RTL8187B) { + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_CTL: + case IEEE80211_FC0_TYPE_MGT: + xfer = sc->sc_xfer[URTW_8187B_BULK_TX_EP12]; + break; + default: + KASSERT(M_WME_GETAC(m0) < URTW_8187B_TXPIPE_MAX, + ("unsupported WME pipe %d", M_WME_GETAC(m0))); + xfer = rtl8187b_pipes[M_WME_GETAC(m0)]; + break; + } + } else + xfer = (prior == URTW_PRIORITY_LOW) ? + sc->sc_xfer[URTW_8187L_BULK_TX_LOW] : + sc->sc_xfer[URTW_8187L_BULK_TX_NORMAL]; + + STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); + usbd_transfer_start(xfer); + + error = urtw_led_ctl(sc, URTW_LED_CTL_TX); + if (error != 0) + device_printf(sc->sc_dev, "could not control LED (%d)\n", + error); + return (0); +} + +static int +urtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic = vap->iv_ic; + struct urtw_softc *sc = ic->ic_softc; + struct urtw_vap *uvp = URTW_VAP(vap); + struct ieee80211_node *ni; + usb_error_t error = 0; + + DPRINTF(sc, URTW_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + sc->sc_state = nstate; + + IEEE80211_UNLOCK(ic); + URTW_LOCK(sc); + usb_callout_stop(&sc->sc_led_ch); + callout_stop(&sc->sc_watchdog_ch); + + switch (nstate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + break; + case IEEE80211_S_RUN: + ni = ieee80211_ref_node(vap->iv_bss); + /* setting bssid. */ + urtw_write32_m(sc, URTW_BSSID, ((uint32_t *)ni->ni_bssid)[0]); + urtw_write16_m(sc, URTW_BSSID + 4, + ((uint16_t *)ni->ni_bssid)[2]); + urtw_update_msr(sc); + /* XXX maybe the below would be incorrect. */ + urtw_write16_m(sc, URTW_ATIM_WND, 2); + urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); + urtw_write16_m(sc, URTW_BEACON_INTERVAL, 0x64); + urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); + error = urtw_led_ctl(sc, URTW_LED_CTL_LINK); + if (error != 0) + device_printf(sc->sc_dev, + "could not control LED (%d)\n", error); + ieee80211_free_node(ni); + break; + default: + break; + } +fail: + URTW_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); +} + +static void +urtw_watchdog(void *arg) +{ + struct urtw_softc *sc = arg; + + if (sc->sc_txtimer > 0) { + if (--sc->sc_txtimer == 0) { + device_printf(sc->sc_dev, "device timeout\n"); + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + return; + } + callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); + } +} + +static void +urtw_set_multi(void *arg) +{ + /* XXX don't know how to set a device. Lack of docs. */ +} + +static usb_error_t +urtw_set_rate(struct urtw_softc *sc) +{ + int i, basic_rate, min_rr_rate, max_rr_rate; + uint16_t data; + usb_error_t error; + + basic_rate = urtw_rate2rtl(48); + min_rr_rate = urtw_rate2rtl(12); + max_rr_rate = urtw_rate2rtl(48); + + urtw_write8_m(sc, URTW_RESP_RATE, + max_rr_rate << URTW_RESP_MAX_RATE_SHIFT | + min_rr_rate << URTW_RESP_MIN_RATE_SHIFT); + + urtw_read16_m(sc, URTW_BRSR, &data); + data &= ~URTW_BRSR_MBR_8185; + + for (i = 0; i <= basic_rate; i++) + data |= (1 << i); + + urtw_write16_m(sc, URTW_BRSR, data); +fail: + return (error); +} + +static uint16_t +urtw_rate2rtl(uint32_t rate) +{ + unsigned int i; + + for (i = 0; i < nitems(urtw_ratetable); i++) { + if (rate == urtw_ratetable[i].reg) + return urtw_ratetable[i].val; + } + + return (3); +} + +static uint16_t +urtw_rtl2rate(uint32_t rate) +{ + unsigned int i; + + for (i = 0; i < nitems(urtw_ratetable); i++) { + if (rate == urtw_ratetable[i].val) + return urtw_ratetable[i].reg; + } + + return (0); +} + +static usb_error_t +urtw_update_msr(struct urtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint8_t data; + usb_error_t error; + + urtw_read8_m(sc, URTW_MSR, &data); + data &= ~URTW_MSR_LINK_MASK; + + if (sc->sc_state == IEEE80211_S_RUN) { + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + case IEEE80211_M_MONITOR: + data |= URTW_MSR_LINK_STA; + if (sc->sc_flags & URTW_RTL8187B) + data |= URTW_MSR_LINK_ENEDCA; + break; + case IEEE80211_M_IBSS: + data |= URTW_MSR_LINK_ADHOC; + break; + case IEEE80211_M_HOSTAP: + data |= URTW_MSR_LINK_HOSTAP; + break; + default: + DPRINTF(sc, URTW_DEBUG_STATE, + "unsupported operation mode 0x%x\n", + ic->ic_opmode); + error = USB_ERR_INVAL; + goto fail; + } + } else + data |= URTW_MSR_LINK_NONE; + + urtw_write8_m(sc, URTW_MSR, data); +fail: + return (error); +} + +static usb_error_t +urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data) +{ + struct usb_device_request req; + usb_error_t error; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = URTW_8187_GETREGS_REQ; + USETW(req.wValue, (val & 0xff) | 0xff00); + USETW(req.wIndex, (val >> 8) & 0x3); + USETW(req.wLength, sizeof(uint8_t)); + + error = urtw_do_request(sc, &req, data); + return (error); +} + +static usb_error_t +urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data) +{ + struct usb_device_request req; + usb_error_t error; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = URTW_8187_GETREGS_REQ; + USETW(req.wValue, (val & 0xff) | 0xff00); + USETW(req.wIndex, (val >> 8) & 0x3); + USETW(req.wLength, sizeof(uint16_t)); + + error = urtw_do_request(sc, &req, data); + return (error); +} + +static usb_error_t +urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data) +{ + struct usb_device_request req; + usb_error_t error; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = URTW_8187_GETREGS_REQ; + USETW(req.wValue, (val & 0xff) | 0xff00); + USETW(req.wIndex, (val >> 8) & 0x3); + USETW(req.wLength, sizeof(uint32_t)); + + error = urtw_do_request(sc, &req, data); + return (error); +} + +static usb_error_t +urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data) +{ + struct usb_device_request req; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, (val & 0xff) | 0xff00); + USETW(req.wIndex, (val >> 8) & 0x3); + USETW(req.wLength, sizeof(uint8_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb_error_t +urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data) +{ + struct usb_device_request req; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, (val & 0xff) | 0xff00); + USETW(req.wIndex, (val >> 8) & 0x3); + USETW(req.wLength, sizeof(uint16_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb_error_t +urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data) +{ + struct usb_device_request req; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, (val & 0xff) | 0xff00); + USETW(req.wIndex, (val >> 8) & 0x3); + USETW(req.wLength, sizeof(uint32_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb_error_t +urtw_get_macaddr(struct urtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t data; + usb_error_t error; + + error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR, &data); + if (error != 0) + goto fail; + ic->ic_macaddr[0] = data & 0xff; + ic->ic_macaddr[1] = (data & 0xff00) >> 8; + error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 1, &data); + if (error != 0) + goto fail; + ic->ic_macaddr[2] = data & 0xff; + ic->ic_macaddr[3] = (data & 0xff00) >> 8; + error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 2, &data); + if (error != 0) + goto fail; + ic->ic_macaddr[4] = data & 0xff; + ic->ic_macaddr[5] = (data & 0xff00) >> 8; +fail: + return (error); +} + +static usb_error_t +urtw_eprom_read32(struct urtw_softc *sc, uint32_t addr, uint32_t *data) +{ +#define URTW_READCMD_LEN 3 + int addrlen, i; + int16_t addrstr[8], data16, readcmd[] = { 1, 1, 0 }; + usb_error_t error; + + /* NB: make sure the buffer is initialized */ + *data = 0; + + /* enable EPROM programming */ + urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_PROGRAM_MODE); + DELAY(URTW_EPROM_DELAY); + + error = urtw_eprom_cs(sc, URTW_EPROM_ENABLE); + if (error != 0) + goto fail; + error = urtw_eprom_ck(sc); + if (error != 0) + goto fail; + error = urtw_eprom_sendbits(sc, readcmd, URTW_READCMD_LEN); + if (error != 0) + goto fail; + if (sc->sc_epromtype == URTW_EEPROM_93C56) { + addrlen = 8; + addrstr[0] = addr & (1 << 7); + addrstr[1] = addr & (1 << 6); + addrstr[2] = addr & (1 << 5); + addrstr[3] = addr & (1 << 4); + addrstr[4] = addr & (1 << 3); + addrstr[5] = addr & (1 << 2); + addrstr[6] = addr & (1 << 1); + addrstr[7] = addr & (1 << 0); + } else { + addrlen=6; + addrstr[0] = addr & (1 << 5); + addrstr[1] = addr & (1 << 4); + addrstr[2] = addr & (1 << 3); + addrstr[3] = addr & (1 << 2); + addrstr[4] = addr & (1 << 1); + addrstr[5] = addr & (1 << 0); + } + error = urtw_eprom_sendbits(sc, addrstr, addrlen); + if (error != 0) + goto fail; + + error = urtw_eprom_writebit(sc, 0); + if (error != 0) + goto fail; + + for (i = 0; i < 16; i++) { + error = urtw_eprom_ck(sc); + if (error != 0) + goto fail; + error = urtw_eprom_readbit(sc, &data16); + if (error != 0) + goto fail; + + (*data) |= (data16 << (15 - i)); + } + + error = urtw_eprom_cs(sc, URTW_EPROM_DISABLE); + if (error != 0) + goto fail; + error = urtw_eprom_ck(sc); + if (error != 0) + goto fail; + + /* now disable EPROM programming */ + urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_NORMAL_MODE); +fail: + return (error); +#undef URTW_READCMD_LEN +} + +static usb_error_t +urtw_eprom_cs(struct urtw_softc *sc, int able) +{ + uint8_t data; + usb_error_t error; + + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + if (able == URTW_EPROM_ENABLE) + urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CS); + else + urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CS); + DELAY(URTW_EPROM_DELAY); +fail: + return (error); +} + +static usb_error_t +urtw_eprom_ck(struct urtw_softc *sc) +{ + uint8_t data; + usb_error_t error; + + /* masking */ + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CK); + DELAY(URTW_EPROM_DELAY); + /* unmasking */ + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CK); + DELAY(URTW_EPROM_DELAY); +fail: + return (error); +} + +static usb_error_t +urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data) +{ + uint8_t data8; + usb_error_t error; + + urtw_read8_m(sc, URTW_EPROM_CMD, &data8); + *data = (data8 & URTW_EPROM_READBIT) ? 1 : 0; + DELAY(URTW_EPROM_DELAY); + +fail: + return (error); +} + +static usb_error_t +urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit) +{ + uint8_t data; + usb_error_t error; + + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + if (bit != 0) + urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_WRITEBIT); + else + urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_WRITEBIT); + DELAY(URTW_EPROM_DELAY); +fail: + return (error); +} + +static usb_error_t +urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen) +{ + int i = 0; + usb_error_t error = 0; + + for (i = 0; i < buflen; i++) { + error = urtw_eprom_writebit(sc, buf[i]); + if (error != 0) + goto fail; + error = urtw_eprom_ck(sc); + if (error != 0) + goto fail; + } +fail: + return (error); +} + + +static usb_error_t +urtw_get_txpwr(struct urtw_softc *sc) +{ + int i, j; + uint32_t data; + usb_error_t error; + + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW_BASE, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck_base = data & 0xf; + sc->sc_txpwr_ofdm_base = (data >> 4) & 0xf; + + for (i = 1, j = 0; i < 6; i += 2, j++) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW0 + j, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[i] = data & 0xf; + sc->sc_txpwr_cck[i + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[i] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[i + 1] = (data & 0xf000) >> 12; + } + for (i = 1, j = 0; i < 4; i += 2, j++) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW1 + j, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[i + 6] = data & 0xf; + sc->sc_txpwr_cck[i + 6 + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[i + 6] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[i + 6 + 1] = (data & 0xf000) >> 12; + } + if (sc->sc_flags & URTW_RTL8187B) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[1 + 6 + 4] = data & 0xf; + sc->sc_txpwr_ofdm[1 + 6 + 4] = (data & 0xf0) >> 4; + error = urtw_eprom_read32(sc, 0x0a, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[2 + 6 + 4] = data & 0xf; + sc->sc_txpwr_ofdm[2 + 6 + 4] = (data & 0xf0) >> 4; + error = urtw_eprom_read32(sc, 0x1c, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[3 + 6 + 4] = data & 0xf; + sc->sc_txpwr_cck[3 + 6 + 4 + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[3 + 6 + 4] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[3 + 6 + 4 + 1] = (data & 0xf000) >> 12; + } else { + for (i = 1, j = 0; i < 4; i += 2, j++) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2 + j, + &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[i + 6 + 4] = data & 0xf; + sc->sc_txpwr_cck[i + 6 + 4 + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[i + 6 + 4] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[i + 6 + 4 + 1] = (data & 0xf000) >> 12; + } + } +fail: + return (error); +} + + +static usb_error_t +urtw_get_rfchip(struct urtw_softc *sc) +{ + int ret; + uint8_t data8; + uint32_t data; + usb_error_t error; + + if (sc->sc_flags & URTW_RTL8187B) { + urtw_read8_m(sc, 0xe1, &data8); + switch (data8) { + case 0: + sc->sc_flags |= URTW_RTL8187B_REV_B; + break; + case 1: + sc->sc_flags |= URTW_RTL8187B_REV_D; + break; + case 2: + sc->sc_flags |= URTW_RTL8187B_REV_E; + break; + default: + device_printf(sc->sc_dev, "unknown type: %#x\n", data8); + sc->sc_flags |= URTW_RTL8187B_REV_B; + break; + } + } else { + urtw_read32_m(sc, URTW_TX_CONF, &data); + switch (data & URTW_TX_HWMASK) { + case URTW_TX_R8187vD_B: + sc->sc_flags |= URTW_RTL8187B; + break; + case URTW_TX_R8187vD: + break; + default: + device_printf(sc->sc_dev, "unknown RTL8187L type: %#x\n", + data & URTW_TX_HWMASK); + break; + } + } + + error = urtw_eprom_read32(sc, URTW_EPROM_RFCHIPID, &data); + if (error != 0) + goto fail; + switch (data & 0xff) { + case URTW_EPROM_RFCHIPID_RTL8225U: + error = urtw_8225_isv2(sc, &ret); + if (error != 0) + goto fail; + if (ret == 0) { + sc->sc_rf_init = urtw_8225_rf_init; + sc->sc_rf_set_sens = urtw_8225_rf_set_sens; + sc->sc_rf_set_chan = urtw_8225_rf_set_chan; + sc->sc_rf_stop = urtw_8225_rf_stop; + } else { + sc->sc_rf_init = urtw_8225v2_rf_init; + sc->sc_rf_set_chan = urtw_8225v2_rf_set_chan; + sc->sc_rf_stop = urtw_8225_rf_stop; + } + sc->sc_max_sens = URTW_8225_RF_MAX_SENS; + sc->sc_sens = URTW_8225_RF_DEF_SENS; + break; + case URTW_EPROM_RFCHIPID_RTL8225Z2: + sc->sc_rf_init = urtw_8225v2b_rf_init; + sc->sc_rf_set_chan = urtw_8225v2b_rf_set_chan; + sc->sc_max_sens = URTW_8225_RF_MAX_SENS; + sc->sc_sens = URTW_8225_RF_DEF_SENS; + sc->sc_rf_stop = urtw_8225_rf_stop; + break; + default: + DPRINTF(sc, URTW_DEBUG_STATE, + "unsupported RF chip %d\n", data & 0xff); + error = USB_ERR_INVAL; + goto fail; + } + + device_printf(sc->sc_dev, "%s rf %s hwrev %s\n", + (sc->sc_flags & URTW_RTL8187B) ? "rtl8187b" : "rtl8187l", + ((data & 0xff) == URTW_EPROM_RFCHIPID_RTL8225U) ? "rtl8225u" : + "rtl8225z2", + (sc->sc_flags & URTW_RTL8187B) ? ((data8 == 0) ? "b" : + (data8 == 1) ? "d" : "e") : "none"); + +fail: + return (error); +} + + +static usb_error_t +urtw_led_init(struct urtw_softc *sc) +{ + uint32_t rev; + usb_error_t error; + + urtw_read8_m(sc, URTW_PSR, &sc->sc_psr); + error = urtw_eprom_read32(sc, URTW_EPROM_SWREV, &rev); + if (error != 0) + goto fail; + + switch (rev & URTW_EPROM_CID_MASK) { + case URTW_EPROM_CID_ALPHA0: + sc->sc_strategy = URTW_SW_LED_MODE1; + break; + case URTW_EPROM_CID_SERCOMM_PS: + sc->sc_strategy = URTW_SW_LED_MODE3; + break; + case URTW_EPROM_CID_HW_LED: + sc->sc_strategy = URTW_HW_LED; + break; + case URTW_EPROM_CID_RSVD0: + case URTW_EPROM_CID_RSVD1: + default: + sc->sc_strategy = URTW_SW_LED_MODE0; + break; + } + + sc->sc_gpio_ledpin = URTW_LED_PIN_GPIO0; + +fail: + return (error); +} + + +static usb_error_t +urtw_8225_rf_init(struct urtw_softc *sc) +{ + unsigned int i; + uint16_t data; + usb_error_t error; + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + + error = urtw_8225_usb_init(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); + urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ + urtw_write16_m(sc, URTW_BRSR, 0xffff); + urtw_write32_m(sc, URTW_RF_PARA, 0x100044); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_write8_m(sc, URTW_CONFIG3, 0x44); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_8185_rf_pins_enable(sc); + if (error) + goto fail; + usb_pause_mtx(&sc->sc_mtx, 1000); + + for (i = 0; i < nitems(urtw_8225_rf_part1); i++) { + urtw_8225_write(sc, urtw_8225_rf_part1[i].reg, + urtw_8225_rf_part1[i].val); + usb_pause_mtx(&sc->sc_mtx, 1); + } + usb_pause_mtx(&sc->sc_mtx, 100); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); + usb_pause_mtx(&sc->sc_mtx, 200); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); + usb_pause_mtx(&sc->sc_mtx, 200); + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC3); + + for (i = 0; i < 95; i++) { + urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225_rxgain[i]); + } + + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC4); + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC5); + + for (i = 0; i < 128; i++) { + urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); + usb_pause_mtx(&sc->sc_mtx, 1); + } + + for (i = 0; i < nitems(urtw_8225_rf_part2); i++) { + urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg, + urtw_8225_rf_part2[i].val); + usb_pause_mtx(&sc->sc_mtx, 1); + } + + error = urtw_8225_setgain(sc, 4); + if (error) + goto fail; + + for (i = 0; i < nitems(urtw_8225_rf_part3); i++) { + urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg, + urtw_8225_rf_part3[i].val); + usb_pause_mtx(&sc->sc_mtx, 1); + } + + urtw_write8_m(sc, URTW_TESTR, 0x0d); + + error = urtw_8225_set_txpwrlvl(sc, 1); + if (error) + goto fail; + + urtw_8187_write_phy_cck(sc, 0x10, 0x9b); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); + usb_pause_mtx(&sc->sc_mtx, 1); + + /* TX ant A, 0x0 for B */ + error = urtw_8185_tx_antenna(sc, 0x3); + if (error) + goto fail; + urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002); + + error = urtw_8225_rf_set_chan(sc, 1); +fail: + return (error); +} + +static usb_error_t +urtw_8185_rf_pins_enable(struct urtw_softc *sc) +{ + usb_error_t error = 0; + + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7); +fail: + return (error); +} + +static usb_error_t +urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant) +{ + usb_error_t error; + + urtw_write8_m(sc, URTW_TX_ANTENNA, ant); + usb_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb_error_t +urtw_8187_write_phy_ofdm_c(struct urtw_softc *sc, uint8_t addr, uint32_t data) +{ + + data = data & 0xff; + return urtw_8187_write_phy(sc, addr, data); +} + +static usb_error_t +urtw_8187_write_phy_cck_c(struct urtw_softc *sc, uint8_t addr, uint32_t data) +{ + + data = data & 0xff; + return urtw_8187_write_phy(sc, addr, data | 0x10000); +} + +static usb_error_t +urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data) +{ + uint32_t phyw; + usb_error_t error; + + phyw = ((data << 8) | (addr | 0x80)); + urtw_write8_m(sc, URTW_PHY_MAGIC4, ((phyw & 0xff000000) >> 24)); + urtw_write8_m(sc, URTW_PHY_MAGIC3, ((phyw & 0x00ff0000) >> 16)); + urtw_write8_m(sc, URTW_PHY_MAGIC2, ((phyw & 0x0000ff00) >> 8)); + urtw_write8_m(sc, URTW_PHY_MAGIC1, ((phyw & 0x000000ff))); + usb_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb_error_t +urtw_8225_setgain(struct urtw_softc *sc, int16_t gain) +{ + usb_error_t error; + + urtw_8187_write_phy_ofdm(sc, 0x0d, urtw_8225_gain[gain * 4]); + urtw_8187_write_phy_ofdm(sc, 0x1b, urtw_8225_gain[gain * 4 + 2]); + urtw_8187_write_phy_ofdm(sc, 0x1d, urtw_8225_gain[gain * 4 + 3]); + urtw_8187_write_phy_ofdm(sc, 0x23, urtw_8225_gain[gain * 4 + 1]); +fail: + return (error); +} + +static usb_error_t +urtw_8225_usb_init(struct urtw_softc *sc) +{ + uint8_t data; + usb_error_t error; + + urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 0); + urtw_write8_m(sc, URTW_GPIO, 0); + error = urtw_read8e(sc, 0x53, &data); + if (error) + goto fail; + error = urtw_write8e(sc, 0x53, data | (1 << 7)); + if (error) + goto fail; + urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 4); + urtw_write8_m(sc, URTW_GPIO, 0x20); + urtw_write8_m(sc, URTW_GP_ENABLE, 0); + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x80); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x80); + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x80); + + usb_pause_mtx(&sc->sc_mtx, 500); +fail: + return (error); +} + +static usb_error_t +urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data) +{ + uint16_t d80, d82, d84; + usb_error_t error; + + urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &d80); + d80 &= URTW_RF_PINS_MAGIC1; + urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &d82); + urtw_read16_m(sc, URTW_RF_PINS_SELECT, &d84); + d84 &= URTW_RF_PINS_MAGIC2; + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, d82 | URTW_RF_PINS_MAGIC3); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84 | URTW_RF_PINS_MAGIC3); + DELAY(10); + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80); + DELAY(10); + + error = urtw_8225_write_s16(sc, addr, 0x8225, &data); + if (error != 0) + goto fail; + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); + DELAY(10); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84); + usb_pause_mtx(&sc->sc_mtx, 2); +fail: + return (error); +} + +static usb_error_t +urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index, + uint16_t *data) +{ + uint8_t buf[2]; + uint16_t data16; + struct usb_device_request req; + usb_error_t error = 0; + + data16 = *data; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, addr); + USETW(req.wIndex, index); + USETW(req.wLength, sizeof(uint16_t)); + buf[0] = (data16 & 0x00ff); + buf[1] = (data16 & 0xff00) >> 8; + + error = urtw_do_request(sc, &req, buf); + + return (error); +} + +static usb_error_t +urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan) +{ + usb_error_t error; + + error = urtw_8225_set_txpwrlvl(sc, chan); + if (error) + goto fail; + urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); + usb_pause_mtx(&sc->sc_mtx, 10); +fail: + return (error); +} + +static usb_error_t +urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens) +{ + usb_error_t error; + + if (sens < 0 || sens > 6) + return -1; + + if (sens > 4) + urtw_8225_write(sc, + URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC1); + else + urtw_8225_write(sc, + URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC2); + + sens = 6 - sens; + error = urtw_8225_setgain(sc, sens); + if (error) + goto fail; + + urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[sens]); + +fail: + return (error); +} + +static usb_error_t +urtw_8225_set_txpwrlvl(struct urtw_softc *sc, int chan) +{ + int i, idx, set; + uint8_t *cck_pwltable; + uint8_t cck_pwrlvl_max, ofdm_pwrlvl_min, ofdm_pwrlvl_max; + uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; + uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; + usb_error_t error; + + cck_pwrlvl_max = 11; + ofdm_pwrlvl_max = 25; /* 12 -> 25 */ + ofdm_pwrlvl_min = 10; + + /* CCK power setting */ + cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl; + idx = cck_pwrlvl % 6; + set = cck_pwrlvl / 6; + cck_pwltable = (chan == 14) ? urtw_8225_txpwr_cck_ch14 : + urtw_8225_txpwr_cck; + + urtw_write8_m(sc, URTW_TX_GAIN_CCK, + urtw_8225_tx_gain_cck_ofdm[set] >> 1); + for (i = 0; i < 8; i++) { + urtw_8187_write_phy_cck(sc, 0x44 + i, + cck_pwltable[idx * 8 + i]); + } + usb_pause_mtx(&sc->sc_mtx, 1); + + /* OFDM power setting */ + ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ? + ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min; + ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; + + idx = ofdm_pwrlvl % 6; + set = ofdm_pwrlvl / 6; + + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; + urtw_8187_write_phy_ofdm(sc, 2, 0x42); + urtw_8187_write_phy_ofdm(sc, 6, 0); + urtw_8187_write_phy_ofdm(sc, 8, 0); + + urtw_write8_m(sc, URTW_TX_GAIN_OFDM, + urtw_8225_tx_gain_cck_ofdm[set] >> 1); + urtw_8187_write_phy_ofdm(sc, 0x5, urtw_8225_txpwr_ofdm[idx]); + urtw_8187_write_phy_ofdm(sc, 0x7, urtw_8225_txpwr_ofdm[idx]); + usb_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + + +static usb_error_t +urtw_8225_rf_stop(struct urtw_softc *sc) +{ + uint8_t data; + usb_error_t error; + + urtw_8225_write(sc, 0x4, 0x1f); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); + if (sc->sc_flags & URTW_RTL8187B) { + urtw_write32_m(sc, URTW_ANAPARAM2, + URTW_8187B_8225_ANAPARAM2_OFF); + urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_OFF); + urtw_write32_m(sc, URTW_ANAPARAM3, + URTW_8187B_8225_ANAPARAM3_OFF); + } else { + urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8225_ANAPARAM2_OFF); + urtw_write32_m(sc, URTW_ANAPARAM, URTW_8225_ANAPARAM_OFF); + } + + urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + +fail: + return (error); +} + +static usb_error_t +urtw_8225v2_rf_init(struct urtw_softc *sc) +{ + unsigned int i; + uint16_t data; + uint32_t data32; + usb_error_t error; + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + + error = urtw_8225_usb_init(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); + urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ + urtw_write16_m(sc, URTW_BRSR, 0xffff); + urtw_write32_m(sc, URTW_RF_PARA, 0x100044); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_write8_m(sc, URTW_CONFIG3, 0x44); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_8185_rf_pins_enable(sc); + if (error) + goto fail; + + usb_pause_mtx(&sc->sc_mtx, 500); + + for (i = 0; i < nitems(urtw_8225v2_rf_part1); i++) { + urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg, + urtw_8225v2_rf_part1[i].val); + } + usb_pause_mtx(&sc->sc_mtx, 50); + + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1); + + for (i = 0; i < 95; i++) { + urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, + urtw_8225v2_rxgain[i]); + } + + urtw_8225_write(sc, + URTW_8225_ADDR_3_MAGIC, URTW_8225_ADDR_3_DATA_MAGIC1); + urtw_8225_write(sc, + URTW_8225_ADDR_5_MAGIC, URTW_8225_ADDR_5_DATA_MAGIC1); + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); + usb_pause_mtx(&sc->sc_mtx, 100); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); + usb_pause_mtx(&sc->sc_mtx, 100); + + error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32); + if (error != 0) + goto fail; + if (data32 != URTW_8225_ADDR_6_DATA_MAGIC1) + device_printf(sc->sc_dev, "expect 0xe6!! (0x%x)\n", data32); + if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) { + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); + usb_pause_mtx(&sc->sc_mtx, 100); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); + usb_pause_mtx(&sc->sc_mtx, 50); + error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32); + if (error != 0) + goto fail; + if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) + device_printf(sc->sc_dev, "RF calibration failed\n"); + } + usb_pause_mtx(&sc->sc_mtx, 100); + + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC6); + for (i = 0; i < 128; i++) { + urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); + urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); + } + + for (i = 0; i < nitems(urtw_8225v2_rf_part2); i++) { + urtw_8187_write_phy_ofdm(sc, urtw_8225v2_rf_part2[i].reg, + urtw_8225v2_rf_part2[i].val); + } + + error = urtw_8225v2_setgain(sc, 4); + if (error) + goto fail; + + for (i = 0; i < nitems(urtw_8225v2_rf_part3); i++) { + urtw_8187_write_phy_cck(sc, urtw_8225v2_rf_part3[i].reg, + urtw_8225v2_rf_part3[i].val); + } + + urtw_write8_m(sc, URTW_TESTR, 0x0d); + + error = urtw_8225v2_set_txpwrlvl(sc, 1); + if (error) + goto fail; + + urtw_8187_write_phy_cck(sc, 0x10, 0x9b); + urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); + + /* TX ant A, 0x0 for B */ + error = urtw_8185_tx_antenna(sc, 0x3); + if (error) + goto fail; + urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002); + + error = urtw_8225_rf_set_chan(sc, 1); +fail: + return (error); +} + +static usb_error_t +urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan) +{ + usb_error_t error; + + error = urtw_8225v2_set_txpwrlvl(sc, chan); + if (error) + goto fail; + + urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); + usb_pause_mtx(&sc->sc_mtx, 10); +fail: + return (error); +} + +static usb_error_t +urtw_8225_read(struct urtw_softc *sc, uint8_t addr, uint32_t *data) +{ + int i; + int16_t bit; + uint8_t rlen = 12, wlen = 6; + uint16_t o1, o2, o3, tmp; + uint32_t d2w = ((uint32_t)(addr & 0x1f)) << 27; + uint32_t mask = 0x80000000, value = 0; + usb_error_t error; + + urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &o1); + urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &o2); + urtw_read16_m(sc, URTW_RF_PINS_SELECT, &o3); + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2 | URTW_RF_PINS_MAGIC4); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3 | URTW_RF_PINS_MAGIC4); + o1 &= ~URTW_RF_PINS_MAGIC4; + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN); + DELAY(5); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1); + DELAY(5); + + for (i = 0; i < (wlen / 2); i++, mask = mask >> 1) { + bit = ((d2w & mask) != 0) ? 1 : 0; + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + mask = mask >> 1; + if (i == 2) + break; + bit = ((d2w & mask) != 0) ? 1 : 0; + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1); + DELAY(1); + } + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW); + DELAY(2); + + mask = 0x800; + for (i = 0; i < rlen; i++, mask = mask >> 1) { + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); + DELAY(2); + + urtw_read16_m(sc, URTW_RF_PINS_INPUT, &tmp); + value |= ((tmp & URTW_BB_HOST_BANG_CLK) ? mask : 0); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW); + DELAY(2); + } + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN | + URTW_BB_HOST_BANG_RW); + DELAY(2); + + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_OUTPUT_MAGIC1); + + if (data != NULL) + *data = value; +fail: + return (error); +} + + +static usb_error_t +urtw_8225v2_set_txpwrlvl(struct urtw_softc *sc, int chan) +{ + int i; + uint8_t *cck_pwrtable; + uint8_t cck_pwrlvl_max = 15, ofdm_pwrlvl_max = 25, ofdm_pwrlvl_min = 10; + uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; + uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; + usb_error_t error; + + /* CCK power setting */ + cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl; + cck_pwrlvl += sc->sc_txpwr_cck_base; + cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; + cck_pwrtable = (chan == 14) ? urtw_8225v2_txpwr_cck_ch14 : + urtw_8225v2_txpwr_cck; + + for (i = 0; i < 8; i++) + urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]); + + urtw_write8_m(sc, URTW_TX_GAIN_CCK, + urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl]); + usb_pause_mtx(&sc->sc_mtx, 1); + + /* OFDM power setting */ + ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ? + ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min; + ofdm_pwrlvl += sc->sc_txpwr_ofdm_base; + ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; + + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; + + urtw_8187_write_phy_ofdm(sc, 2, 0x42); + urtw_8187_write_phy_ofdm(sc, 5, 0x0); + urtw_8187_write_phy_ofdm(sc, 6, 0x40); + urtw_8187_write_phy_ofdm(sc, 7, 0x0); + urtw_8187_write_phy_ofdm(sc, 8, 0x40); + + urtw_write8_m(sc, URTW_TX_GAIN_OFDM, + urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl]); + usb_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb_error_t +urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain) +{ + uint8_t *gainp; + usb_error_t error; + + /* XXX for A? */ + gainp = urtw_8225v2_gain_bg; + urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0x21, 0x17); + usb_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb_error_t +urtw_8225_isv2(struct urtw_softc *sc, int *ret) +{ + uint32_t data; + usb_error_t error; + + *ret = 1; + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_MAGIC5); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, URTW_RF_PINS_MAGIC5); + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, URTW_RF_PINS_MAGIC5); + usb_pause_mtx(&sc->sc_mtx, 500); + + urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, + URTW_8225_ADDR_0_DATA_MAGIC1); + + error = urtw_8225_read(sc, URTW_8225_ADDR_8_MAGIC, &data); + if (error != 0) + goto fail; + if (data != URTW_8225_ADDR_8_DATA_MAGIC1) + *ret = 0; + else { + error = urtw_8225_read(sc, URTW_8225_ADDR_9_MAGIC, &data); + if (error != 0) + goto fail; + if (data != URTW_8225_ADDR_9_DATA_MAGIC1) + *ret = 0; + } + + urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, + URTW_8225_ADDR_0_DATA_MAGIC2); +fail: + return (error); +} + +static usb_error_t +urtw_8225v2b_rf_init(struct urtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + unsigned int i; + uint8_t data8; + usb_error_t error; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + /* + * initialize extra registers on 8187 + */ + urtw_write16_m(sc, URTW_BRSR_8187B, 0xfff); + + /* retry limit */ + urtw_read8_m(sc, URTW_CW_CONF, &data8); + data8 |= URTW_CW_CONF_PERPACKET_RETRY; + urtw_write8_m(sc, URTW_CW_CONF, data8); + + /* TX AGC */ + urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); + data8 |= URTW_TX_AGC_CTL_PERPACKET_GAIN; + urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); + + /* Auto Rate Fallback Control */ +#define URTW_ARFR 0x1e0 + urtw_write16_m(sc, URTW_ARFR, 0xfff); + urtw_read8_m(sc, URTW_RATE_FALLBACK, &data8); + urtw_write8_m(sc, URTW_RATE_FALLBACK, + data8 | URTW_RATE_FALLBACK_ENABLE); + + urtw_read8_m(sc, URTW_MSR, &data8); + urtw_write8_m(sc, URTW_MSR, data8 & 0xf3); + urtw_read8_m(sc, URTW_MSR, &data8); + urtw_write8_m(sc, URTW_MSR, data8 | URTW_MSR_LINK_ENEDCA); + urtw_write8_m(sc, URTW_ACM_CONTROL, sc->sc_acmctl); + + urtw_write16_m(sc, URTW_ATIM_WND, 2); + urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); +#define URTW_FEMR_FOR_8187B 0x1d4 + urtw_write16_m(sc, URTW_FEMR_FOR_8187B, 0xffff); + + /* led type */ + urtw_read8_m(sc, URTW_CONFIG1, &data8); + data8 = (data8 & 0x3f) | 0x80; + urtw_write8_m(sc, URTW_CONFIG1, data8); + + /* applying MAC address again. */ + urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)ic->ic_macaddr)[0]); + urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)ic->ic_macaddr)[1] & 0xffff); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + urtw_write8_m(sc, URTW_WPA_CONFIG, 0); + + /* + * MAC configuration + */ + for (i = 0; i < nitems(urtw_8225v2b_rf_part1); i++) + urtw_write8_m(sc, urtw_8225v2b_rf_part1[i].reg, + urtw_8225v2b_rf_part1[i].val); + urtw_write16_m(sc, URTW_TID_AC_MAP, 0xfa50); + urtw_write16_m(sc, URTW_INT_MIG, 0x0000); + urtw_write32_m(sc, 0x1f0, 0); + urtw_write32_m(sc, 0x1f4, 0); + urtw_write8_m(sc, 0x1f8, 0); + urtw_write32_m(sc, URTW_RF_TIMING, 0x4001); + +#define URTW_RFSW_CTRL 0x272 + urtw_write16_m(sc, URTW_RFSW_CTRL, 0x569a); + + /* + * initialize PHY + */ + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_read8_m(sc, URTW_CONFIG3, &data8); + urtw_write8_m(sc, URTW_CONFIG3, + data8 | URTW_CONFIG3_ANAPARAM_WRITE); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + /* setup RFE initial timing */ + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x0480); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x2488); + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1fff); + usb_pause_mtx(&sc->sc_mtx, 1100); + + for (i = 0; i < nitems(urtw_8225v2b_rf_part0); i++) { + urtw_8225_write(sc, urtw_8225v2b_rf_part0[i].reg, + urtw_8225v2b_rf_part0[i].val); + usb_pause_mtx(&sc->sc_mtx, 1); + } + urtw_8225_write(sc, 0x00, 0x01b7); + + for (i = 0; i < 95; i++) { + urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, + urtw_8225v2b_rxgain[i]); + usb_pause_mtx(&sc->sc_mtx, 1); + } + + urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, 0x080); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, 0x004); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x0b7); + usb_pause_mtx(&sc->sc_mtx, 1); + usb_pause_mtx(&sc->sc_mtx, 3000); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0xc4d); + usb_pause_mtx(&sc->sc_mtx, 2000); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0x44d); + usb_pause_mtx(&sc->sc_mtx, 1); + urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x2bf); + usb_pause_mtx(&sc->sc_mtx, 1); + + urtw_write8_m(sc, URTW_TX_GAIN_CCK, 0x03); + urtw_write8_m(sc, URTW_TX_GAIN_OFDM, 0x07); + urtw_write8_m(sc, URTW_TX_ANTENNA, 0x03); + + urtw_8187_write_phy_ofdm(sc, 0x80, 0x12); + for (i = 0; i < 128; i++) { + uint32_t addr, data; + + data = (urtw_8225z2_agc[i] << 8) | 0x0000008f; + addr = ((i + 0x80) << 8) | 0x0000008e; + + urtw_8187_write_phy_ofdm(sc, data & 0x7f, (data >> 8) & 0xff); + urtw_8187_write_phy_ofdm(sc, addr & 0x7f, (addr >> 8) & 0xff); + urtw_8187_write_phy_ofdm(sc, 0x0e, 0x00); + } + urtw_8187_write_phy_ofdm(sc, 0x80, 0x10); + + for (i = 0; i < nitems(urtw_8225v2b_rf_part2); i++) + urtw_8187_write_phy_ofdm(sc, i, urtw_8225v2b_rf_part2[i].val); + + urtw_write32_m(sc, URTW_8187B_AC_VO, (7 << 12) | (3 << 8) | 0x1c); + urtw_write32_m(sc, URTW_8187B_AC_VI, (7 << 12) | (3 << 8) | 0x1c); + urtw_write32_m(sc, URTW_8187B_AC_BE, (7 << 12) | (3 << 8) | 0x1c); + urtw_write32_m(sc, URTW_8187B_AC_BK, (7 << 12) | (3 << 8) | 0x1c); + + urtw_8187_write_phy_ofdm(sc, 0x97, 0x46); + urtw_8187_write_phy_ofdm(sc, 0xa4, 0xb6); + urtw_8187_write_phy_ofdm(sc, 0x85, 0xfc); + urtw_8187_write_phy_cck(sc, 0xc1, 0x88); + +fail: + return (error); +} + +static usb_error_t +urtw_8225v2b_rf_set_chan(struct urtw_softc *sc, int chan) +{ + usb_error_t error; + + error = urtw_8225v2b_set_txpwrlvl(sc, chan); + if (error) + goto fail; + + urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); + usb_pause_mtx(&sc->sc_mtx, 10); +fail: + return (error); +} + +static usb_error_t +urtw_8225v2b_set_txpwrlvl(struct urtw_softc *sc, int chan) +{ + int i; + uint8_t *cck_pwrtable; + uint8_t cck_pwrlvl_max = 15; + uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; + uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; + usb_error_t error; + + /* CCK power setting */ + cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? cck_pwrlvl_max : 22) : + (cck_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 0 : 7)); + cck_pwrlvl += sc->sc_txpwr_cck_base; + cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; + cck_pwrtable = (chan == 14) ? urtw_8225v2b_txpwr_cck_ch14 : + urtw_8225v2b_txpwr_cck; + + if (sc->sc_flags & URTW_RTL8187B_REV_B) + cck_pwrtable += (cck_pwrlvl <= 6) ? 0 : + ((cck_pwrlvl <= 11) ? 8 : 16); + else + cck_pwrtable += (cck_pwrlvl <= 5) ? 0 : + ((cck_pwrlvl <= 11) ? 8 : ((cck_pwrlvl <= 17) ? 16 : 24)); + + for (i = 0; i < 8; i++) + urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]); + + urtw_write8_m(sc, URTW_TX_GAIN_CCK, + urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl] << 1); + usb_pause_mtx(&sc->sc_mtx, 1); + + /* OFDM power setting */ + ofdm_pwrlvl = (ofdm_pwrlvl > 15) ? + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 17 : 25) : + (ofdm_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 2 : 10)); + ofdm_pwrlvl += sc->sc_txpwr_ofdm_base; + ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; + + urtw_write8_m(sc, URTW_TX_GAIN_OFDM, + urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl] << 1); + + if (sc->sc_flags & URTW_RTL8187B_REV_B) { + if (ofdm_pwrlvl <= 11) { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x60); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x60); + } else { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c); + } + } else { + if (ofdm_pwrlvl <= 11) { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c); + } else if (ofdm_pwrlvl <= 17) { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x54); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x54); + } else { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x50); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x50); + } + } + usb_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb_error_t +urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = URTW_8187_GETREGS_REQ; + USETW(req.wValue, val | 0xfe00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint8_t)); + + error = urtw_do_request(sc, &req, data); + return (error); +} + +static usb_error_t +urtw_write8e(struct urtw_softc *sc, int val, uint8_t data) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, val | 0xfe00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint8_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb_error_t +urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val) +{ + uint8_t data; + usb_error_t error; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); + urtw_write32_m(sc, URTW_ANAPARAM, val); + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; +fail: + return (error); +} + +static usb_error_t +urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val) +{ + uint8_t data; + usb_error_t error; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); + urtw_write32_m(sc, URTW_ANAPARAM2, val); + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; +fail: + return (error); +} + +static usb_error_t +urtw_intr_enable(struct urtw_softc *sc) +{ + usb_error_t error; + + urtw_write16_m(sc, URTW_INTR_MASK, 0xffff); +fail: + return (error); +} + +static usb_error_t +urtw_intr_disable(struct urtw_softc *sc) +{ + usb_error_t error; + + urtw_write16_m(sc, URTW_INTR_MASK, 0); +fail: + return (error); +} + +static usb_error_t +urtw_reset(struct urtw_softc *sc) +{ + uint8_t data; + usb_error_t error; + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; + + error = urtw_intr_disable(sc); + if (error) + goto fail; + usb_pause_mtx(&sc->sc_mtx, 100); + + error = urtw_write8e(sc, 0x18, 0x10); + if (error != 0) + goto fail; + error = urtw_write8e(sc, 0x18, 0x11); + if (error != 0) + goto fail; + error = urtw_write8e(sc, 0x18, 0x00); + if (error != 0) + goto fail; + usb_pause_mtx(&sc->sc_mtx, 100); + + urtw_read8_m(sc, URTW_CMD, &data); + data = (data & 0x2) | URTW_CMD_RST; + urtw_write8_m(sc, URTW_CMD, data); + usb_pause_mtx(&sc->sc_mtx, 100); + + urtw_read8_m(sc, URTW_CMD, &data); + if (data & URTW_CMD_RST) { + device_printf(sc->sc_dev, "reset timeout\n"); + goto fail; + } + + error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD); + if (error) + goto fail; + usb_pause_mtx(&sc->sc_mtx, 100); + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; +fail: + return (error); +} + +static usb_error_t +urtw_led_ctl(struct urtw_softc *sc, int mode) +{ + usb_error_t error = 0; + + switch (sc->sc_strategy) { + case URTW_SW_LED_MODE0: + error = urtw_led_mode0(sc, mode); + break; + case URTW_SW_LED_MODE1: + error = urtw_led_mode1(sc, mode); + break; + case URTW_SW_LED_MODE2: + error = urtw_led_mode2(sc, mode); + break; + case URTW_SW_LED_MODE3: + error = urtw_led_mode3(sc, mode); + break; + default: + DPRINTF(sc, URTW_DEBUG_STATE, + "unsupported LED mode %d\n", sc->sc_strategy); + error = USB_ERR_INVAL; + break; + } + + return (error); +} + +static usb_error_t +urtw_led_mode0(struct urtw_softc *sc, int mode) +{ + + switch (mode) { + case URTW_LED_CTL_POWER_ON: + sc->sc_gpio_ledstate = URTW_LED_POWER_ON_BLINK; + break; + case URTW_LED_CTL_TX: + if (sc->sc_gpio_ledinprogress == 1) + return (0); + + sc->sc_gpio_ledstate = URTW_LED_BLINK_NORMAL; + sc->sc_gpio_blinktime = 2; + break; + case URTW_LED_CTL_LINK: + sc->sc_gpio_ledstate = URTW_LED_ON; + break; + default: + DPRINTF(sc, URTW_DEBUG_STATE, + "unsupported LED mode 0x%x", mode); + return (USB_ERR_INVAL); + } + + switch (sc->sc_gpio_ledstate) { + case URTW_LED_ON: + if (sc->sc_gpio_ledinprogress != 0) + break; + urtw_led_on(sc, URTW_LED_GPIO); + break; + case URTW_LED_BLINK_NORMAL: + if (sc->sc_gpio_ledinprogress != 0) + break; + sc->sc_gpio_ledinprogress = 1; + sc->sc_gpio_blinkstate = (sc->sc_gpio_ledon != 0) ? + URTW_LED_OFF : URTW_LED_ON; + usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc); + break; + case URTW_LED_POWER_ON_BLINK: + urtw_led_on(sc, URTW_LED_GPIO); + usb_pause_mtx(&sc->sc_mtx, 100); + urtw_led_off(sc, URTW_LED_GPIO); + break; + default: + DPRINTF(sc, URTW_DEBUG_STATE, + "unknown LED status 0x%x", sc->sc_gpio_ledstate); + return (USB_ERR_INVAL); + } + return (0); +} + +static usb_error_t +urtw_led_mode1(struct urtw_softc *sc, int mode) +{ + return (USB_ERR_INVAL); +} + +static usb_error_t +urtw_led_mode2(struct urtw_softc *sc, int mode) +{ + return (USB_ERR_INVAL); +} + +static usb_error_t +urtw_led_mode3(struct urtw_softc *sc, int mode) +{ + return (USB_ERR_INVAL); +} + +static usb_error_t +urtw_led_on(struct urtw_softc *sc, int type) +{ + usb_error_t error; + + if (type == URTW_LED_GPIO) { + switch (sc->sc_gpio_ledpin) { + case URTW_LED_PIN_GPIO0: + urtw_write8_m(sc, URTW_GPIO, 0x01); + urtw_write8_m(sc, URTW_GP_ENABLE, 0x00); + break; + default: + DPRINTF(sc, URTW_DEBUG_STATE, + "unsupported LED PIN type 0x%x", + sc->sc_gpio_ledpin); + error = USB_ERR_INVAL; + goto fail; + } + } else { + DPRINTF(sc, URTW_DEBUG_STATE, + "unsupported LED type 0x%x", type); + error = USB_ERR_INVAL; + goto fail; + } + + sc->sc_gpio_ledon = 1; +fail: + return (error); +} + +static usb_error_t +urtw_led_off(struct urtw_softc *sc, int type) +{ + usb_error_t error; + + if (type == URTW_LED_GPIO) { + switch (sc->sc_gpio_ledpin) { + case URTW_LED_PIN_GPIO0: + urtw_write8_m(sc, URTW_GPIO, URTW_GPIO_DATA_MAGIC1); + urtw_write8_m(sc, + URTW_GP_ENABLE, URTW_GP_ENABLE_DATA_MAGIC1); + break; + default: + DPRINTF(sc, URTW_DEBUG_STATE, + "unsupported LED PIN type 0x%x", + sc->sc_gpio_ledpin); + error = USB_ERR_INVAL; + goto fail; + } + } else { + DPRINTF(sc, URTW_DEBUG_STATE, + "unsupported LED type 0x%x", type); + error = USB_ERR_INVAL; + goto fail; + } + + sc->sc_gpio_ledon = 0; + +fail: + return (error); +} + +static void +urtw_led_ch(void *arg) +{ + struct urtw_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + + ieee80211_runtask(ic, &sc->sc_led_task); +} + +static void +urtw_ledtask(void *arg, int pending) +{ + struct urtw_softc *sc = arg; + + if (sc->sc_strategy != URTW_SW_LED_MODE0) { + DPRINTF(sc, URTW_DEBUG_STATE, + "could not process a LED strategy 0x%x", + sc->sc_strategy); + return; + } + + URTW_LOCK(sc); + urtw_led_blink(sc); + URTW_UNLOCK(sc); +} + +static usb_error_t +urtw_led_blink(struct urtw_softc *sc) +{ + uint8_t ing = 0; + usb_error_t error; + + if (sc->sc_gpio_blinkstate == URTW_LED_ON) + error = urtw_led_on(sc, URTW_LED_GPIO); + else + error = urtw_led_off(sc, URTW_LED_GPIO); + sc->sc_gpio_blinktime--; + if (sc->sc_gpio_blinktime == 0) + ing = 1; + else { + if (sc->sc_gpio_ledstate != URTW_LED_BLINK_NORMAL && + sc->sc_gpio_ledstate != URTW_LED_BLINK_SLOWLY && + sc->sc_gpio_ledstate != URTW_LED_BLINK_CM3) + ing = 1; + } + if (ing == 1) { + if (sc->sc_gpio_ledstate == URTW_LED_ON && + sc->sc_gpio_ledon == 0) + error = urtw_led_on(sc, URTW_LED_GPIO); + else if (sc->sc_gpio_ledstate == URTW_LED_OFF && + sc->sc_gpio_ledon == 1) + error = urtw_led_off(sc, URTW_LED_GPIO); + + sc->sc_gpio_blinktime = 0; + sc->sc_gpio_ledinprogress = 0; + return (0); + } + + sc->sc_gpio_blinkstate = (sc->sc_gpio_blinkstate != URTW_LED_ON) ? + URTW_LED_ON : URTW_LED_OFF; + + switch (sc->sc_gpio_ledstate) { + case URTW_LED_BLINK_NORMAL: + usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc); + break; + default: + DPRINTF(sc, URTW_DEBUG_STATE, + "unknown LED status 0x%x", + sc->sc_gpio_ledstate); + return (USB_ERR_INVAL); + } + return (0); +} + +static usb_error_t +urtw_rx_enable(struct urtw_softc *sc) +{ + uint8_t data; + usb_error_t error; + + usbd_transfer_start((sc->sc_flags & URTW_RTL8187B) ? + sc->sc_xfer[URTW_8187B_BULK_RX] : sc->sc_xfer[URTW_8187L_BULK_RX]); + + error = urtw_rx_setconf(sc); + if (error != 0) + goto fail; + + if ((sc->sc_flags & URTW_RTL8187B) == 0) { + urtw_read8_m(sc, URTW_CMD, &data); + urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE); + } +fail: + return (error); +} + +static usb_error_t +urtw_tx_enable(struct urtw_softc *sc) +{ + uint8_t data8; + uint32_t data; + usb_error_t error; + + if (sc->sc_flags & URTW_RTL8187B) { + urtw_read32_m(sc, URTW_TX_CONF, &data); + data &= ~URTW_TX_LOOPBACK_MASK; + data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); + data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); + data &= ~URTW_TX_SWPLCPLEN; + data |= URTW_TX_HW_SEQNUM | URTW_TX_DISREQQSIZE | + (7 << 8) | /* short retry limit */ + (7 << 0) | /* long retry limit */ + (7 << 21); /* MAX TX DMA */ + urtw_write32_m(sc, URTW_TX_CONF, data); + + urtw_read8_m(sc, URTW_MSR, &data8); + data8 |= URTW_MSR_LINK_ENEDCA; + urtw_write8_m(sc, URTW_MSR, data8); + return (error); + } + + urtw_read8_m(sc, URTW_CW_CONF, &data8); + data8 &= ~(URTW_CW_CONF_PERPACKET_CW | URTW_CW_CONF_PERPACKET_RETRY); + urtw_write8_m(sc, URTW_CW_CONF, data8); + + urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); + data8 &= ~URTW_TX_AGC_CTL_PERPACKET_GAIN; + data8 &= ~URTW_TX_AGC_CTL_PERPACKET_ANTSEL; + data8 &= ~URTW_TX_AGC_CTL_FEEDBACK_ANT; + urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); + + urtw_read32_m(sc, URTW_TX_CONF, &data); + data &= ~URTW_TX_LOOPBACK_MASK; + data |= URTW_TX_LOOPBACK_NONE; + data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); + data |= sc->sc_tx_retry << URTW_TX_DPRETRY_SHIFT; + data |= sc->sc_rts_retry << URTW_TX_RTSRETRY_SHIFT; + data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); + data |= URTW_TX_MXDMA_2048 | URTW_TX_CWMIN | URTW_TX_DISCW; + data &= ~URTW_TX_SWPLCPLEN; + data |= URTW_TX_NOICV; + urtw_write32_m(sc, URTW_TX_CONF, data); + + urtw_read8_m(sc, URTW_CMD, &data8); + urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE); +fail: + return (error); +} + +static usb_error_t +urtw_rx_setconf(struct urtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t data; + usb_error_t error; + + urtw_read32_m(sc, URTW_RX, &data); + data = data &~ URTW_RX_FILTER_MASK; + if (sc->sc_flags & URTW_RTL8187B) { + data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA | + URTW_RX_FILTER_MCAST | URTW_RX_FILTER_BCAST | + URTW_RX_FILTER_NICMAC | URTW_RX_CHECK_BSSID | + URTW_RX_FIFO_THRESHOLD_NONE | + URTW_MAX_RX_DMA_2048 | + URTW_RX_AUTORESETPHY | URTW_RCR_ONLYERLPKT; + } else { + data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA; + data = data | URTW_RX_FILTER_BCAST | URTW_RX_FILTER_MCAST; + + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + data = data | URTW_RX_FILTER_ICVERR; + data = data | URTW_RX_FILTER_PWR; + } + if (sc->sc_crcmon == 1 && ic->ic_opmode == IEEE80211_M_MONITOR) + data = data | URTW_RX_FILTER_CRCERR; + + if (ic->ic_opmode == IEEE80211_M_MONITOR || + ic->ic_promisc > 0 || ic->ic_allmulti > 0) { + data = data | URTW_RX_FILTER_ALLMAC; + } else { + data = data | URTW_RX_FILTER_NICMAC; + data = data | URTW_RX_CHECK_BSSID; + } + + data = data &~ URTW_RX_FIFO_THRESHOLD_MASK; + data = data | URTW_RX_FIFO_THRESHOLD_NONE | + URTW_RX_AUTORESETPHY; + data = data &~ URTW_MAX_RX_DMA_MASK; + data = data | URTW_MAX_RX_DMA_2048 | URTW_RCR_ONLYERLPKT; + } + + urtw_write32_m(sc, URTW_RX, data); +fail: + return (error); +} + +static struct mbuf * +urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p, + int8_t *nf_p) +{ + int actlen, flen, rssi; + struct ieee80211_frame *wh; + struct mbuf *m, *mnew; + struct urtw_softc *sc = data->sc; + struct ieee80211com *ic = &sc->sc_ic; + uint8_t noise = 0, rate; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + if (actlen < (int)URTW_MIN_RXBUFSZ) { + counter_u64_add(ic->ic_ierrors, 1); + return (NULL); + } + + if (sc->sc_flags & URTW_RTL8187B) { + struct urtw_8187b_rxhdr *rx; + + rx = (struct urtw_8187b_rxhdr *)(data->buf + + (actlen - (sizeof(struct urtw_8187b_rxhdr)))); + flen = le32toh(rx->flag) & 0xfff; + if (flen > actlen) { + counter_u64_add(ic->ic_ierrors, 1); + return (NULL); + } + rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; + /* XXX correct? */ + rssi = rx->rssi & URTW_RX_RSSI_MASK; + noise = rx->noise; + } else { + struct urtw_8187l_rxhdr *rx; + + rx = (struct urtw_8187l_rxhdr *)(data->buf + + (actlen - (sizeof(struct urtw_8187l_rxhdr)))); + flen = le32toh(rx->flag) & 0xfff; + if (flen > actlen) { + counter_u64_add(ic->ic_ierrors, 1); + return (NULL); + } + + rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; + /* XXX correct? */ + rssi = rx->rssi & URTW_RX_8187L_RSSI_MASK; + noise = rx->noise; + } + + mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (mnew == NULL) { + counter_u64_add(ic->ic_ierrors, 1); + return (NULL); + } + + m = data->m; + data->m = mnew; + data->buf = mtod(mnew, uint8_t *); + + /* finalize mbuf */ + m->m_pkthdr.len = m->m_len = flen - IEEE80211_CRC_LEN; + + if (ieee80211_radiotap_active(ic)) { + struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap; + + /* XXX Are variables correct? */ + tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + tap->wr_dbm_antsignal = (int8_t)rssi; + } + + wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) + sc->sc_currate = (rate > 0) ? rate : sc->sc_currate; + + *rssi_p = rssi; + *nf_p = noise; /* XXX correct? */ + + return (m); +} + +static void +urtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct urtw_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m = NULL; + struct urtw_data *data; + int8_t nf = -95; + int rssi = 1; + + URTW_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + m = urtw_rxeof(xfer, data, &rssi, &nf); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + data = STAILQ_FIRST(&sc->sc_rx_inactive); + if (data == NULL) { + KASSERT(m == NULL, ("mbuf isn't NULL")); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); + usbd_xfer_set_frame_data(xfer, 0, data->buf, + usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + + /* + * To avoid LOR we should unlock our private mutex here to call + * ieee80211_input() because here is at the end of a USB + * callback and safe to unlock. + */ + URTW_UNLOCK(sc); + if (m != NULL) { + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, + (struct ieee80211_frame_min *)wh); + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi, nf); + /* node is no longer needed */ + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi, nf); + m = NULL; + } + URTW_LOCK(sc); + break; + default: + /* needs it to the inactive queue due to a error. */ + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + counter_u64_add(ic->ic_ierrors, 1); + goto setup; + } + break; + } +} + +#define URTW_STATUS_TYPE_TXCLOSE 1 +#define URTW_STATUS_TYPE_BEACON_INTR 0 + +static void +urtw_txstatus_eof(struct usb_xfer *xfer) +{ + struct urtw_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + int actlen, type, pktretry, seq; + uint64_t val; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + if (actlen != sizeof(uint64_t)) + return; + + val = le64toh(sc->sc_txstatus); + type = (val >> 30) & 0x3; + if (type == URTW_STATUS_TYPE_TXCLOSE) { + pktretry = val & 0xff; + seq = (val >> 16) & 0xff; + if (pktretry == URTW_TX_MAXRETRY) + counter_u64_add(ic->ic_oerrors, 1); + DPRINTF(sc, URTW_DEBUG_TXSTATUS, "pktretry %d seq %#x\n", + pktretry, seq); + } +} + +static void +urtw_bulk_tx_status_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct urtw_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + void *dma_buf = usbd_xfer_get_frame_buffer(xfer, 0); + + URTW_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + urtw_txstatus_eof(xfer); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + memcpy(dma_buf, &sc->sc_txstatus, sizeof(uint64_t)); + usbd_xfer_set_frame_len(xfer, 0, sizeof(uint64_t)); + usbd_transfer_submit(xfer); + break; + default: + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + counter_u64_add(ic->ic_ierrors, 1); + goto setup; + } + break; + } +} + +static void +urtw_txeof(struct usb_xfer *xfer, struct urtw_data *data) +{ + struct urtw_softc *sc = usbd_xfer_softc(xfer); + + URTW_ASSERT_LOCKED(sc); + + if (data->m) { + /* XXX status? */ + ieee80211_tx_complete(data->ni, data->m, 0); + data->m = NULL; + data->ni = NULL; + } + sc->sc_txtimer = 0; +} + +static void +urtw_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct urtw_softc *sc = usbd_xfer_softc(xfer); + struct urtw_data *data; + + URTW_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_tx_active); + if (data == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); + urtw_txeof(xfer, data); + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + data = STAILQ_FIRST(&sc->sc_tx_pending); + if (data == NULL) { + DPRINTF(sc, URTW_DEBUG_XMIT, + "%s: empty pending queue\n", __func__); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); + STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); + + usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); + usbd_transfer_submit(xfer); + + urtw_start(sc); + break; + default: + data = STAILQ_FIRST(&sc->sc_tx_active); + if (data == NULL) + goto setup; + if (data->ni != NULL) { + if_inc_counter(data->ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + ieee80211_free_node(data->ni); + data->ni = NULL; + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto setup; + } + break; + } +} + +static struct urtw_data * +_urtw_getbuf(struct urtw_softc *sc) +{ + struct urtw_data *bf; + + bf = STAILQ_FIRST(&sc->sc_tx_inactive); + if (bf != NULL) + STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); + else + bf = NULL; + if (bf == NULL) + DPRINTF(sc, URTW_DEBUG_XMIT, "%s: %s\n", __func__, + "out of xmit buffers"); + return (bf); +} + +static struct urtw_data * +urtw_getbuf(struct urtw_softc *sc) +{ + struct urtw_data *bf; + + URTW_ASSERT_LOCKED(sc); + + bf = _urtw_getbuf(sc); + if (bf == NULL) + DPRINTF(sc, URTW_DEBUG_XMIT, "%s: stop queue\n", __func__); + return (bf); +} + +static int +urtw_isbmode(uint16_t rate) +{ + + return ((rate <= 22 && rate != 12 && rate != 18) || + rate == 44) ? (1) : (0); +} + +static uint16_t +urtw_rate2dbps(uint16_t rate) +{ + + switch(rate) { + case 12: + case 18: + case 24: + case 36: + case 48: + case 72: + case 96: + case 108: + return (rate * 2); + default: + break; + } + return (24); +} + +static int +urtw_compute_txtime(uint16_t framelen, uint16_t rate, + uint8_t ismgt, uint8_t isshort) +{ + uint16_t ceiling, frametime, n_dbps; + + if (urtw_isbmode(rate)) { + if (ismgt || !isshort || rate == 2) + frametime = (uint16_t)(144 + 48 + + (framelen * 8 / (rate / 2))); + else + frametime = (uint16_t)(72 + 24 + + (framelen * 8 / (rate / 2))); + if ((framelen * 8 % (rate / 2)) != 0) + frametime++; + } else { + n_dbps = urtw_rate2dbps(rate); + ceiling = (16 + 8 * framelen + 6) / n_dbps + + (((16 + 8 * framelen + 6) % n_dbps) ? 1 : 0); + frametime = (uint16_t)(16 + 4 + 4 * ceiling + 6); + } + return (frametime); +} + +/* + * Callback from the 802.11 layer to update the + * slot time based on the current setting. + */ +static void +urtw_updateslot(struct ieee80211com *ic) +{ + struct urtw_softc *sc = ic->ic_softc; + + ieee80211_runtask(ic, &sc->sc_updateslot_task); +} + +static void +urtw_updateslottask(void *arg, int pending) +{ + struct urtw_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + int error; + + URTW_LOCK(sc); + if ((sc->sc_flags & URTW_RUNNING) == 0) { + URTW_UNLOCK(sc); + return; + } + if (sc->sc_flags & URTW_RTL8187B) { + urtw_write8_m(sc, URTW_SIFS, 0x22); + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) + urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SHSLOT); + else + urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SLOT); + urtw_write8_m(sc, URTW_8187B_EIFS, 0x5b); + urtw_write8_m(sc, URTW_CARRIER_SCOUNT, 0x5b); + } else { + urtw_write8_m(sc, URTW_SIFS, 0x22); + if (sc->sc_state == IEEE80211_S_ASSOC && + ic->ic_flags & IEEE80211_F_SHSLOT) + urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SHSLOT); + else + urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SLOT); + if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { + urtw_write8_m(sc, URTW_DIFS, 0x14); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14); + urtw_write8_m(sc, URTW_CW_VAL, 0x73); + } else { + urtw_write8_m(sc, URTW_DIFS, 0x24); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24); + urtw_write8_m(sc, URTW_CW_VAL, 0xa5); + } + } +fail: + URTW_UNLOCK(sc); +} + +static void +urtw_sysctl_node(struct urtw_softc *sc) +{ +#define URTW_SYSCTL_STAT_ADD32(c, h, n, p, d) \ + SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *child, *parent; + struct sysctl_oid *tree; + struct urtw_stats *stats = &sc->sc_stats; + + ctx = device_get_sysctl_ctx(sc->sc_dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); + + tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, + NULL, "URTW statistics"); + parent = SYSCTL_CHILDREN(tree); + + /* Tx statistics. */ + tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, + NULL, "Tx MAC statistics"); + child = SYSCTL_CHILDREN(tree); + URTW_SYSCTL_STAT_ADD32(ctx, child, "1m", &stats->txrates[0], + "1 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "2m", &stats->txrates[1], + "2 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "5.5m", &stats->txrates[2], + "5.5 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "6m", &stats->txrates[4], + "6 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "9m", &stats->txrates[5], + "9 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "11m", &stats->txrates[3], + "11 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "12m", &stats->txrates[6], + "12 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "18m", &stats->txrates[7], + "18 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "24m", &stats->txrates[8], + "24 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "36m", &stats->txrates[9], + "36 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "48m", &stats->txrates[10], + "48 Mbit/s"); + URTW_SYSCTL_STAT_ADD32(ctx, child, "54m", &stats->txrates[11], + "54 Mbit/s"); +#undef URTW_SYSCTL_STAT_ADD32 +} + +static device_method_t urtw_methods[] = { + DEVMETHOD(device_probe, urtw_match), + DEVMETHOD(device_attach, urtw_attach), + DEVMETHOD(device_detach, urtw_detach), + DEVMETHOD_END +}; +static driver_t urtw_driver = { + .name = "urtw", + .methods = urtw_methods, + .size = sizeof(struct urtw_softc) +}; +static devclass_t urtw_devclass; + +DRIVER_MODULE(urtw, uhub, urtw_driver, urtw_devclass, NULL, 0); +MODULE_DEPEND(urtw, wlan, 1, 1, 1); +MODULE_DEPEND(urtw, usb, 1, 1, 1); +MODULE_VERSION(urtw, 1); +USB_PNP_HOST_INFO(urtw_devs); diff --git a/freebsd/sys/dev/usb/wlan/if_urtwreg.h b/freebsd/sys/dev/usb/wlan/if_urtwreg.h new file mode 100644 index 00000000..5021e5ac --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_urtwreg.h @@ -0,0 +1,433 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define URTW_CONFIG_INDEX 0 +#define URTW_IFACE_INDEX 0 + +/* for 8187 */ +#define URTW_MAC0 0x0000 /* 1 byte */ +#define URTW_MAC1 0x0001 /* 1 byte */ +#define URTW_MAC2 0x0002 /* 1 byte */ +#define URTW_MAC3 0x0003 /* 1 byte */ +#define URTW_MAC4 0x0004 /* 1 byte */ +#define URTW_MAC5 0x0005 /* 1 byte */ +#define URTW_MAR 0x0008 /* 6 byte */ +#define URTW_RXFIFO_CNT 0x0010 /* 1 byte */ +#define URTW_TXFIFO_CNT 0x0012 /* 1 byte */ +#define URTW_BQREQ 0x0013 /* 1 byte */ +#define URTW_TSFT 0x0018 /* 6 byte */ +#define URTW_TLPDA 0x0020 /* 4 byte */ +#define URTW_TNPDA 0x0024 /* 4 byte */ +#define URTW_THPDA 0x0028 /* 4 byte */ +#define URTW_BRSR 0x002c /* 2 byte */ +#define URTW_BRSR_MBR_8185 (0x0fff) +#define URTW_8187B_EIFS 0x002d /* 1 byte for 8187B */ +#define URTW_BSSID 0x002e /* 6 byte */ +#define URTW_BRSR_8187B 0x0034 /* 2 byte for 8187B */ +#define URTW_RESP_RATE 0x0034 /* 1 byte for 8187L */ +#define URTW_RESP_MAX_RATE_SHIFT (4) +#define URTW_RESP_MIN_RATE_SHIFT (0) +#define URTW_EIFS 0x0035 /* 1 byte */ +#define URTW_CMD 0x0037 /* 1 byte */ +#define URTW_CMD_TX_ENABLE (0x4) +#define URTW_CMD_RX_ENABLE (0x8) +#define URTW_CMD_RST (0x10) +#define URTW_INTR_MASK 0x003c /* 2 byte */ +#define URTW_INTR_STATUS 0x003e /* 2 byte */ +#define URTW_TX_CONF 0x0040 /* 4 byte */ +#define URTW_TX_LOOPBACK_SHIFT (17) +#define URTW_TX_LOOPBACK_NONE (0 << URTW_TX_LOOPBACK_SHIFT) +#define URTW_TX_LOOPBACK_MAC (1 << URTW_TX_LOOPBACK_SHIFT) +#define URTW_TX_LOOPBACK_BASEBAND (2 << URTW_TX_LOOPBACK_SHIFT) +#define URTW_TX_LOOPBACK_CONTINUE (3 << URTW_TX_LOOPBACK_SHIFT) +#define URTW_TX_LOOPBACK_MASK (0x60000) +#define URTW_TX_DPRETRY_MASK (0xff00) +#define URTW_TX_RTSRETRY_MASK (0xff) +#define URTW_TX_DPRETRY_SHIFT (0) +#define URTW_TX_RTSRETRY_SHIFT (8) +#define URTW_TX_NOCRC (0x10000) +#define URTW_TX_MXDMA_MASK (0xe00000) +#define URTW_TX_MXDMA_1024 (6 << URTW_TX_MXDMA_SHIFT) +#define URTW_TX_MXDMA_2048 (7 << URTW_TX_MXDMA_SHIFT) +#define URTW_TX_MXDMA_SHIFT (21) +#define URTW_TX_DISCW (1 << 20) +#define URTW_TX_SWPLCPLEN (1 << 24) +#define URTW_TX_R8187vD (5 << 25) +#define URTW_TX_R8187vD_B (6 << 25) +#define URTW_TX_HWMASK (7 << 25) +#define URTW_TX_DISREQQSIZE (1 << 28) +#define URTW_TX_HW_SEQNUM (1 << 30) +#define URTW_TX_CWMIN (1U << 31) +#define URTW_TX_NOICV (0x80000) +#define URTW_RX 0x0044 /* 4 byte */ +#define URTW_RX_9356SEL (1 << 6) +#define URTW_RX_FILTER_MASK \ + (URTW_RX_FILTER_ALLMAC | URTW_RX_FILTER_NICMAC | URTW_RX_FILTER_MCAST | \ + URTW_RX_FILTER_BCAST | URTW_RX_FILTER_CRCERR | URTW_RX_FILTER_ICVERR | \ + URTW_RX_FILTER_DATA | URTW_RX_FILTER_CTL | URTW_RX_FILTER_MNG | \ + (1 << 21) | \ + URTW_RX_FILTER_PWR | URTW_RX_CHECK_BSSID) +#define URTW_RX_FILTER_ALLMAC (0x00000001) +#define URTW_RX_FILTER_NICMAC (0x00000002) +#define URTW_RX_FILTER_MCAST (0x00000004) +#define URTW_RX_FILTER_BCAST (0x00000008) +#define URTW_RX_FILTER_CRCERR (0x00000020) +#define URTW_RX_FILTER_ICVERR (0x00001000) +#define URTW_RX_FILTER_DATA (0x00040000) +#define URTW_RX_FILTER_CTL (0x00080000) +#define URTW_RX_FILTER_MNG (0x00100000) +#define URTW_RX_FILTER_PWR (0x00400000) +#define URTW_RX_CHECK_BSSID (0x00800000) +#define URTW_RX_FIFO_THRESHOLD_MASK ((1 << 13) | (1 << 14) | (1 << 15)) +#define URTW_RX_FIFO_THRESHOLD_SHIFT (13) +#define URTW_RX_FIFO_THRESHOLD_128 (3) +#define URTW_RX_FIFO_THRESHOLD_256 (4) +#define URTW_RX_FIFO_THRESHOLD_512 (5) +#define URTW_RX_FIFO_THRESHOLD_1024 (6) +#define URTW_RX_FIFO_THRESHOLD_NONE (7 << URTW_RX_FIFO_THRESHOLD_SHIFT) +#define URTW_RX_AUTORESETPHY (1 << URTW_RX_AUTORESETPHY_SHIFT) +#define URTW_RX_AUTORESETPHY_SHIFT (28) +#define URTW_MAX_RX_DMA_MASK ((1<<8) | (1<<9) | (1<<10)) +#define URTW_MAX_RX_DMA_2048 (7 << URTW_MAX_RX_DMA_SHIFT) +#define URTW_MAX_RX_DMA_1024 (6) +#define URTW_MAX_RX_DMA_SHIFT (10) +#define URTW_RCR_ONLYERLPKT (1U << 31) +#define URTW_INT_TIMEOUT 0x0048 /* 4 byte */ +#define URTW_INT_TBDA 0x004c /* 4 byte */ +#define URTW_EPROM_CMD 0x0050 /* 1 byte */ +#define URTW_EPROM_CMD_NORMAL (0x0) +#define URTW_EPROM_CMD_NORMAL_MODE \ + (URTW_EPROM_CMD_NORMAL << URTW_EPROM_CMD_SHIFT) +#define URTW_EPROM_CMD_LOAD (0x1) +#define URTW_EPROM_CMD_PROGRAM (0x2) +#define URTW_EPROM_CMD_PROGRAM_MODE \ + (URTW_EPROM_CMD_PROGRAM << URTW_EPROM_CMD_SHIFT) +#define URTW_EPROM_CMD_CONFIG (0x3) +#define URTW_EPROM_CMD_SHIFT (6) +#define URTW_EPROM_CMD_MASK ((1 << 7) | (1 << 6)) +#define URTW_EPROM_READBIT (0x1) +#define URTW_EPROM_WRITEBIT (0x2) +#define URTW_EPROM_CK (0x4) +#define URTW_EPROM_CS (0x8) +#define URTW_CONFIG0 0x0051 /* 1 byte */ +#define URTW_CONFIG1 0x0052 /* 1 byte */ +#define URTW_CONFIG2 0x0053 /* 1 byte */ +#define URTW_ANAPARAM 0x0054 /* 4 byte */ +#define URTW_8225_ANAPARAM_ON (0xa0000a59) +#define URTW_8225_ANAPARAM_OFF (0xa00beb59) +#define URTW_8187B_8225_ANAPARAM_ON (0x45090658) +#define URTW_8187B_8225_ANAPARAM_OFF (0x55480658) +#define URTW_MSR 0x0058 /* 1 byte */ +#define URTW_MSR_LINK_MASK ((1 << 2) | (1 << 3)) +#define URTW_MSR_LINK_SHIFT (2) +#define URTW_MSR_LINK_NONE (0 << URTW_MSR_LINK_SHIFT) +#define URTW_MSR_LINK_ADHOC (1 << URTW_MSR_LINK_SHIFT) +#define URTW_MSR_LINK_STA (2 << URTW_MSR_LINK_SHIFT) +#define URTW_MSR_LINK_HOSTAP (3 << URTW_MSR_LINK_SHIFT) +#define URTW_MSR_LINK_ENEDCA (1 << 4) +#define URTW_CONFIG3 0x0059 /* 1 byte */ +#define URTW_CONFIG3_ANAPARAM_WRITE (0x40) +#define URTW_CONFIG3_GNT_SELECT (0x80) +#define URTW_CONFIG3_ANAPARAM_W_SHIFT (6) +#define URTW_CONFIG4 0x005a /* 1 byte */ +#define URTW_CONFIG4_VCOOFF (1 << 7) +#define URTW_TESTR 0x005b /* 1 byte */ +#define URTW_PSR 0x005e /* 1 byte */ +#define URTW_SECURITY 0x005f /* 1 byte */ +#define URTW_ANAPARAM2 0x0060 /* 4 byte */ +#define URTW_8225_ANAPARAM2_ON (0x860c7312) +#define URTW_8225_ANAPARAM2_OFF (0x840dec11) +#define URTW_8187B_8225_ANAPARAM2_ON (0x727f3f52) +#define URTW_8187B_8225_ANAPARAM2_OFF (0x72003f50) +#define URTW_BEACON_INTERVAL 0x0070 /* 2 byte */ +#define URTW_ATIM_WND 0x0072 /* 2 byte */ +#define URTW_BEACON_INTERVAL_TIME 0x0074 /* 2 byte */ +#define URTW_ATIM_TR_ITV 0x0076 /* 2 byte */ +#define URTW_PHY_DELAY 0x0078 /* 1 byte */ +#define URTW_CARRIER_SCOUNT 0x0079 /* 1 byte */ +#define URTW_PHY_MAGIC1 0x007c /* 1 byte */ +#define URTW_PHY_MAGIC2 0x007d /* 1 byte */ +#define URTW_PHY_MAGIC3 0x007e /* 1 byte */ +#define URTW_PHY_MAGIC4 0x007f /* 1 byte */ +#define URTW_RF_PINS_OUTPUT 0x0080 /* 2 byte */ +#define URTW_RF_PINS_OUTPUT_MAGIC1 (0x3a0) +#define URTW_BB_HOST_BANG_CLK (1 << 1) +#define URTW_BB_HOST_BANG_EN (1 << 2) +#define URTW_BB_HOST_BANG_RW (1 << 3) +#define URTW_RF_PINS_ENABLE 0x0082 /* 2 byte */ +#define URTW_RF_PINS_SELECT 0x0084 /* 2 byte */ +#define URTW_ADDR_MAGIC1 0x0085 /* broken? */ +#define URTW_RF_PINS_INPUT 0x0086 /* 2 byte */ +#define URTW_RF_PINS_MAGIC1 (0xfff3) +#define URTW_RF_PINS_MAGIC2 (0xfff0) +#define URTW_RF_PINS_MAGIC3 (0x0007) +#define URTW_RF_PINS_MAGIC4 (0xf) +#define URTW_RF_PINS_MAGIC5 (0x0080) +#define URTW_RF_PARA 0x0088 /* 4 byte */ +#define URTW_RF_TIMING 0x008c /* 4 byte */ +#define URTW_GP_ENABLE 0x0090 /* 1 byte */ +#define URTW_GP_ENABLE_DATA_MAGIC1 (0x1) +#define URTW_GPIO 0x0091 /* 1 byte */ +#define URTW_GPIO_DATA_MAGIC1 (0x1) +#define URTW_HSSI_PARA 0x0094 /* 4 byte */ +#define URTW_TX_AGC_CTL 0x009c /* 1 byte */ +#define URTW_TX_AGC_CTL_PERPACKET_GAIN (0x1) +#define URTW_TX_AGC_CTL_PERPACKET_ANTSEL (0x2) +#define URTW_TX_AGC_CTL_FEEDBACK_ANT (0x4) +#define URTW_TX_GAIN_CCK 0x009d /* 1 byte */ +#define URTW_TX_GAIN_OFDM 0x009e /* 1 byte */ +#define URTW_TX_ANTENNA 0x009f /* 1 byte */ +#define URTW_WPA_CONFIG 0x00b0 /* 1 byte */ +#define URTW_SIFS 0x00b4 /* 1 byte */ +#define URTW_DIFS 0x00b5 /* 1 byte */ +#define URTW_SLOT 0x00b6 /* 1 byte */ +#define URTW_CW_CONF 0x00bc /* 1 byte */ +#define URTW_CW_CONF_PERPACKET_RETRY (0x2) +#define URTW_CW_CONF_PERPACKET_CW (0x1) +#define URTW_CW_VAL 0x00bd /* 1 byte */ +#define URTW_RATE_FALLBACK 0x00be /* 1 byte */ +#define URTW_RATE_FALLBACK_ENABLE (0x80) +#define URTW_ACM_CONTROL 0x00bf /* 1 byte */ +#define URTW_CONFIG5 0x00d8 /* 1 byte */ +#define URTW_TXDMA_POLLING 0x00d9 /* 1 byte */ +#define URTW_CWR 0x00dc /* 2 byte */ +#define URTW_RETRY_CTR 0x00de /* 1 byte */ +#define URTW_INT_MIG 0x00e2 /* 2 byte */ +#define URTW_RDSAR 0x00e4 /* 4 byte */ +#define URTW_TID_AC_MAP 0x00e8 /* 2 byte */ +#define URTW_ANAPARAM3 0x00ee /* 1 byte */ +#define URTW_8187B_8225_ANAPARAM3_ON (0x0) +#define URTW_8187B_8225_ANAPARAM3_OFF (0x0) +#define URTW_8187B_AC_VO 0x00f0 /* 4 byte for 8187B */ +#define URTW_FEMR 0x00f4 /* 2 byte */ +#define URTW_8187B_AC_VI 0x00f4 /* 4 byte for 8187B */ +#define URTW_8187B_AC_BE 0x00f8 /* 4 byte for 8187B */ +#define URTW_TALLY_CNT 0x00fa /* 2 byte */ +#define URTW_TALLY_SEL 0x00fc /* 1 byte */ +#define URTW_8187B_AC_BK 0x00fc /* 4 byte for 8187B */ +#define URTW_ADDR_MAGIC2 0x00fe /* 2 byte */ +#define URTW_ADDR_MAGIC3 0x00ff /* 1 byte */ + +/* for 8225 */ +#define URTW_8225_ADDR_0_MAGIC 0x0 +#define URTW_8225_ADDR_0_DATA_MAGIC1 (0x1b7) +#define URTW_8225_ADDR_0_DATA_MAGIC2 (0x0b7) +#define URTW_8225_ADDR_0_DATA_MAGIC3 (0x127) +#define URTW_8225_ADDR_0_DATA_MAGIC4 (0x027) +#define URTW_8225_ADDR_0_DATA_MAGIC5 (0x22f) +#define URTW_8225_ADDR_0_DATA_MAGIC6 (0x2bf) +#define URTW_8225_ADDR_1_MAGIC 0x1 +#define URTW_8225_ADDR_2_MAGIC 0x2 +#define URTW_8225_ADDR_2_DATA_MAGIC1 (0xc4d) +#define URTW_8225_ADDR_2_DATA_MAGIC2 (0x44d) +#define URTW_8225_ADDR_3_MAGIC 0x3 +#define URTW_8225_ADDR_3_DATA_MAGIC1 (0x2) +#define URTW_8225_ADDR_5_MAGIC 0x5 +#define URTW_8225_ADDR_5_DATA_MAGIC1 (0x4) +#define URTW_8225_ADDR_6_MAGIC 0x6 +#define URTW_8225_ADDR_6_DATA_MAGIC1 (0xe6) +#define URTW_8225_ADDR_6_DATA_MAGIC2 (0x80) +#define URTW_8225_ADDR_7_MAGIC 0x7 +#define URTW_8225_ADDR_8_MAGIC 0x8 +#define URTW_8225_ADDR_8_DATA_MAGIC1 (0x588) +#define URTW_8225_ADDR_9_MAGIC 0x9 +#define URTW_8225_ADDR_9_DATA_MAGIC1 (0x700) +#define URTW_8225_ADDR_C_MAGIC 0xc +#define URTW_8225_ADDR_C_DATA_MAGIC1 (0x850) +#define URTW_8225_ADDR_C_DATA_MAGIC2 (0x050) + +/* for EEPROM */ +#define URTW_EPROM_CHANPLAN 0x03 +#define URTW_EPROM_CHANPLAN_BY_HW (0x80) +#define URTW_EPROM_TXPW_BASE 0x05 +#define URTW_EPROM_RFCHIPID 0x06 +#define URTW_EPROM_RFCHIPID_RTL8225U (5) +#define URTW_EPROM_RFCHIPID_RTL8225Z2 (6) +#define URTW_EPROM_MACADDR 0x07 +#define URTW_EPROM_TXPW0 0x16 +#define URTW_EPROM_TXPW2 0x1b +#define URTW_EPROM_TXPW1 0x3d +#define URTW_EPROM_SWREV 0x3f +#define URTW_EPROM_CID_MASK (0xff) +#define URTW_EPROM_CID_RSVD0 (0x00) +#define URTW_EPROM_CID_RSVD1 (0xff) +#define URTW_EPROM_CID_ALPHA0 (0x01) +#define URTW_EPROM_CID_SERCOMM_PS (0x02) +#define URTW_EPROM_CID_HW_LED (0x03) + +/* LED */ +#define URTW_CID_DEFAULT 0 +#define URTW_CID_8187_ALPHA0 1 +#define URTW_CID_8187_SERCOMM_PS 2 +#define URTW_CID_8187_HW_LED 3 +#define URTW_SW_LED_MODE0 0 +#define URTW_SW_LED_MODE1 1 +#define URTW_SW_LED_MODE2 2 +#define URTW_SW_LED_MODE3 3 +#define URTW_HW_LED 4 +#define URTW_LED_CTL_POWER_ON 0 +#define URTW_LED_CTL_LINK 2 +#define URTW_LED_CTL_TX 4 +#define URTW_LED_PIN_GPIO0 0 +#define URTW_LED_PIN_LED0 1 +#define URTW_LED_PIN_LED1 2 +#define URTW_LED_UNKNOWN 0 +#define URTW_LED_ON 1 +#define URTW_LED_OFF 2 +#define URTW_LED_BLINK_NORMAL 3 +#define URTW_LED_BLINK_SLOWLY 4 +#define URTW_LED_POWER_ON_BLINK 5 +#define URTW_LED_SCAN_BLINK 6 +#define URTW_LED_NO_LINK_BLINK 7 +#define URTW_LED_BLINK_CM3 8 + +/* for extra area */ +#define URTW_EPROM_DISABLE 0 +#define URTW_EPROM_ENABLE 1 +#define URTW_EPROM_DELAY 10 +#define URTW_8187_GETREGS_REQ 5 +#define URTW_8187_SETREGS_REQ 5 +#define URTW_8225_RF_MAX_SENS 6 +#define URTW_8225_RF_DEF_SENS 4 +#define URTW_DEFAULT_RTS_RETRY 7 +#define URTW_DEFAULT_TX_RETRY 7 +#define URTW_DEFAULT_RTS_THRESHOLD 2342U + +#define URTW_ASIFS_TIME 10 +#define URTW_ACKCTS_LEN 14 /* len for ACK and CTS */ + +struct urtw_8187b_rxhdr { + uint32_t flag; +#define URTW_RX_FLAG_LEN /* 0 ~ 11 bits */ +#define URTW_RX_FLAG_ICV_ERR (1 << 12) +#define URTW_RX_FLAG_CRC32_ERR (1 << 13) +#define URTW_RX_FLAG_PM (1 << 14) +#define URTW_RX_FLAG_RX_ERR (1 << 15) +#define URTW_RX_FLAG_BCAST (1 << 16) +#define URTW_RX_FLAG_PAM (1 << 17) +#define URTW_RX_FLAG_MCAST (1 << 18) +#define URTW_RX_FLAG_QOS (1 << 19) /* only for RTL8187B */ +#define URTW_RX_FLAG_RXRATE /* 20 ~ 23 bits */ +#define URTW_RX_FLAG_RXRATE_SHIFT 20 +#define URTW_RX_FLAG_TRSW (1 << 24) /* only for RTL8187B */ +#define URTW_RX_FLAG_SPLCP (1 << 25) +#define URTW_RX_FLAG_FOF (1 << 26) +#define URTW_RX_FLAG_DMA_FAIL (1 << 27) +#define URTW_RX_FLAG_LAST (1 << 28) +#define URTW_RX_FLAG_FIRST (1 << 29) +#define URTW_RX_FLAG_EOR (1 << 30) +#define URTW_RX_FLAG_OWN (1U << 31) + uint64_t mactime; + uint8_t noise; + uint8_t rssi; +#define URTW_RX_RSSI /* 0 ~ 6 bits */ +#define URTW_RX_RSSI_MASK 0x3f +#define URTW_RX_ANTENNA (1 << 7) + uint8_t agc; + uint8_t flag2; +#define URTW_RX_FLAG2_DECRYPTED (1 << 0) +#define URTW_RX_FLAG2_WAKUP (1 << 1) +#define URTW_RX_FLAG2_SHIFT (1 << 2) +#define URTW_RX_FLAG2_RSVD0 /* 3 ~ 7 bits */ + uint16_t flag3; +#define URTW_RX_FLAG3_NUMMCSI /* 0 ~ 3 bits */ +#define URTW_RX_FLAG3_SNR_L2E /* 4 ~ 9 bits */ +#define URTW_RX_FLAG3_CFO_BIAS /* 10 ~ 15 bits */ + int8_t pwdb; + uint8_t fot; +} __packed; + +struct urtw_8187b_txhdr { + uint32_t flag; +#define URTW_TX_FLAG_PKTLEN /* 0 ~ 11 bits */ +#define URTW_TX_FLAG_RSVD0 /* 12 ~ 14 bits */ +#define URTW_TX_FLAG_NO_ENC (1 << 15) +#define URTW_TX_FLAG_SPLCP (1 << 16) +#define URTW_TX_FLAG_MOREFRAG (1 << 17) +#define URTW_TX_FLAG_CTS (1 << 18) +#define URTW_TX_FLAG_RTSRATE /* 19 ~ 22 bits */ +#define URTW_TX_FLAG_RTSRATE_SHIFT 19 +#define URTW_TX_FLAG_RTS (1 << 23) +#define URTW_TX_FLAG_TXRATE /* 24 ~ 27 bits */ +#define URTW_TX_FLAG_TXRATE_SHIFT 24 +#define URTW_TX_FLAG_LAST (1 << 28) +#define URTW_TX_FLAG_FIRST (1 << 29) +#define URTW_TX_FLAG_DMA (1 << 30) +#define URTW_TX_FLAG_OWN (1U << 31) + uint16_t rtsdur; + uint16_t len; +#define URTW_TX_LEN /* 0 ~ 14 bits */ +#define URTW_TX_LEN_EXT (1 << 15) + uint32_t bufaddr; + uint16_t flag1; +#define URTW_TX_FLAG1_RXLEN /* 0 ~ 11 bits */ +#define URTW_TX_FLAG1_RSVD0 /* 12 ~ 14 bits */ +#define URTW_TX_FLAG1_MICCAL (1 << 15) + uint16_t txdur; + uint32_t nextdescaddr; + uint8_t rtsagc; + uint8_t retry; + uint16_t flag2; +#define URTW_TX_FLAG2_RTDB (1 << 0) +#define URTW_TX_FLAG2_NOACM (1 << 1) +#define URTW_TX_FLAG2_PIFS (1 << 2) +#define URTW_TX_FLAG2_RSVD0 /* 3 ~ 6 bits */ +#define URTW_TX_FLAG2_RTSRATEFALLBACK /* 7 ~ 10 bits */ +#define URTW_TX_FLAG2_RATEFALLBACK /* 11 ~ 15 bits */ + uint16_t delaybound; + uint16_t flag3; +#define URTW_TX_FLAG3_RSVD0 /* 0 ~ 3 bits */ +#define URTW_TX_FLAG3_AGC /* 4 ~ 11 bits */ +#define URTW_TX_FLAG3_ANTENNA (1 << 12) +#define URTW_TX_FLAG3_SPC /* 13 ~ 14 bits */ +#define URTW_TX_FLAG3_RSVD1 (1 << 15) + uint32_t flag4; +#define URTW_TX_FLAG4_LENADJUST /* 0 ~ 1 bits */ +#define URTW_TX_FLAG4_RSVD0 (1 << 2) +#define URTW_TX_FLAG4_TPCDESEN (1 << 3) +#define URTW_TX_FLAG4_TPCPOLARITY /* 4 ~ 5 bits */ +#define URTW_TX_FLAG4_TPCEN (1 << 6) +#define URTW_TX_FLAG4_PTEN (1 << 7) +#define URTW_TX_FLAG4_BCKEY /* 8 ~ 13 bits */ +#define URTW_TX_FLAG4_ENBCKEY (1 << 14) +#define URTW_TX_FLAG4_ENPMPD (1 << 15) +#define URTW_TX_FLAG4_FRAGQSZ /* 16 ~ 31 bits */ +} __packed; + +struct urtw_8187l_rxhdr { + uint32_t flag; + uint8_t noise; + uint8_t rssi; +#define URTW_RX_8187L_RSSI /* 0 ~ 6 bits */ +#define URTW_RX_8187L_RSSI_MASK 0x3f +#define URTW_RX_8187L_ANTENNA (1 << 7) + uint8_t agc; + uint8_t flag2; +#define URTW_RX_8187L_DECRYPTED (1 << 0) +#define URTW_RX_8187L_WAKEUP (1 << 1) +#define URTW_RX_8187L_SHIFT (1 << 2) +#define URTW_RX_8187L_RSVD0 /* 3 ~ 7 bits */ + uint64_t mactime; +} __packed; + +struct urtw_8187l_txhdr { + uint32_t flag; + uint16_t rtsdur; + uint16_t len; + uint32_t retry; +} __packed; diff --git a/freebsd/sys/dev/usb/wlan/if_urtwvar.h b/freebsd/sys/dev/usb/wlan/if_urtwvar.h new file mode 100644 index 00000000..08ffc8f3 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_urtwvar.h @@ -0,0 +1,186 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +enum { + URTW_8187B_BULK_RX, + URTW_8187B_BULK_TX_STATUS, + URTW_8187B_BULK_TX_BE, + URTW_8187B_BULK_TX_BK, + URTW_8187B_BULK_TX_VI, + URTW_8187B_BULK_TX_VO, + URTW_8187B_BULK_TX_EP12, + URTW_8187B_N_XFERS = 7 +}; + +enum { + URTW_8187L_BULK_RX, + URTW_8187L_BULK_TX_LOW, + URTW_8187L_BULK_TX_NORMAL, + URTW_8187L_N_XFERS = 3 +}; + +/* XXX no definition at net80211? */ +#define URTW_MAX_CHANNELS 15 + +struct urtw_data { + struct urtw_softc *sc; + uint8_t *buf; + uint16_t buflen; + struct mbuf *m; + struct ieee80211_node *ni; /* NB: tx only */ + STAILQ_ENTRY(urtw_data) next; +}; +typedef STAILQ_HEAD(, urtw_data) urtw_datahead; + +/* XXX not correct.. */ +#define URTW_MIN_RXBUFSZ \ + (sizeof(struct ieee80211_frame_min)) + +#define URTW_RX_DATA_LIST_COUNT 4 +#define URTW_TX_DATA_LIST_COUNT 16 +#define URTW_RX_MAXSIZE 0x9c4 +#define URTW_TX_MAXSIZE 0x9c4 +#define URTW_TX_MAXRETRY 11 + +struct urtw_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_dbm_antsignal; +} __packed __aligned(8); + +#define URTW_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL)) + +struct urtw_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed __aligned(8); + +#define URTW_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct urtw_stats { + unsigned int txrates[12]; +}; + +struct urtw_vap { + struct ieee80211vap vap; + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define URTW_VAP(vap) ((struct urtw_vap *)(vap)) + +struct urtw_softc { + struct ieee80211com sc_ic; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + struct mtx sc_mtx; + void *sc_tx_dma_buf; + + int sc_debug; + int sc_flags; +#define URTW_INIT_ONCE (1 << 1) +#define URTW_RTL8187B (1 << 2) +#define URTW_RTL8187B_REV_B (1 << 3) +#define URTW_RTL8187B_REV_D (1 << 4) +#define URTW_RTL8187B_REV_E (1 << 5) +#define URTW_DETACHED (1 << 6) +#define URTW_RUNNING (1 << 7) + enum ieee80211_state sc_state; + + int sc_epromtype; +#define URTW_EEPROM_93C46 0 +#define URTW_EEPROM_93C56 1 + uint8_t sc_crcmon; + + struct ieee80211_channel *sc_curchan; + + /* for RF */ + usb_error_t (*sc_rf_init)(struct urtw_softc *); + usb_error_t (*sc_rf_set_chan)(struct urtw_softc *, + int); + usb_error_t (*sc_rf_set_sens)(struct urtw_softc *, + int); + usb_error_t (*sc_rf_stop)(struct urtw_softc *); + uint8_t sc_rfchip; + uint32_t sc_max_sens; + uint32_t sc_sens; + /* for LED */ + struct usb_callout sc_led_ch; + struct task sc_led_task; + uint8_t sc_psr; + uint8_t sc_strategy; +#define URTW_LED_GPIO 1 + uint8_t sc_gpio_ledon; + uint8_t sc_gpio_ledinprogress; + uint8_t sc_gpio_ledstate; + uint8_t sc_gpio_ledpin; + uint8_t sc_gpio_blinktime; + uint8_t sc_gpio_blinkstate; + /* RX/TX */ + struct usb_xfer *sc_xfer[URTW_8187B_N_XFERS]; +#define URTW_PRIORITY_LOW 0 +#define URTW_PRIORITY_NORMAL 1 +#define URTW_DATA_TIMEOUT 10000 /* 10 sec */ +#define URTW_8187B_TXPIPE_BE 0x6 /* best effort */ +#define URTW_8187B_TXPIPE_BK 0x7 /* background */ +#define URTW_8187B_TXPIPE_VI 0x5 /* video */ +#define URTW_8187B_TXPIPE_VO 0x4 /* voice */ +#define URTW_8187B_TXPIPE_MAX 4 + struct urtw_data sc_rx[URTW_RX_DATA_LIST_COUNT]; + urtw_datahead sc_rx_active; + urtw_datahead sc_rx_inactive; + struct urtw_data sc_tx[URTW_TX_DATA_LIST_COUNT]; + urtw_datahead sc_tx_active; + urtw_datahead sc_tx_inactive; + urtw_datahead sc_tx_pending; + uint8_t sc_rts_retry; + uint8_t sc_tx_retry; + uint8_t sc_preamble_mode; +#define URTW_PREAMBLE_MODE_SHORT 1 +#define URTW_PREAMBLE_MODE_LONG 2 + struct callout sc_watchdog_ch; + int sc_txtimer; + int sc_currate; + /* TX power */ + uint8_t sc_txpwr_cck[URTW_MAX_CHANNELS]; + uint8_t sc_txpwr_cck_base; + uint8_t sc_txpwr_ofdm[URTW_MAX_CHANNELS]; + uint8_t sc_txpwr_ofdm_base; + + uint8_t sc_acmctl; + uint64_t sc_txstatus; /* only for 8187B */ + struct task sc_updateslot_task; + + struct urtw_stats sc_stats; + + struct urtw_rx_radiotap_header sc_rxtap; + struct urtw_tx_radiotap_header sc_txtap; +}; + +#define URTW_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define URTW_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define URTW_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) diff --git a/freebsd/sys/dev/usb/wlan/if_zyd.c b/freebsd/sys/dev/usb/wlan/if_zyd.c new file mode 100644 index 00000000..f935bfc9 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_zyd.c @@ -0,0 +1,2924 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $OpenBSD: if_zyd.c,v 1.52 2007/02/11 00:08:04 jsg Exp $ */ +/* $NetBSD: if_zyd.c,v 1.7 2007/06/21 04:04:29 kiyohara Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * ZyDAS ZD1211/ZD1211B USB WLAN driver. + */ + +#include <rtems/bsd/sys/param.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/kdb.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#endif + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_ratectl.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <rtems/bsd/local/usbdevs.h> + +#include <dev/usb/wlan/if_zydreg.h> +#include <dev/usb/wlan/if_zydfw.h> + +#ifdef USB_DEBUG +static int zyd_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd"); +SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RWTUN, &zyd_debug, 0, + "zyd debug level"); + +enum { + ZYD_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + ZYD_DEBUG_RECV = 0x00000002, /* basic recv operation */ + ZYD_DEBUG_RESET = 0x00000004, /* reset processing */ + ZYD_DEBUG_INIT = 0x00000008, /* device init */ + ZYD_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */ + ZYD_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */ + ZYD_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */ + ZYD_DEBUG_STAT = 0x00000080, /* statistic */ + ZYD_DEBUG_FW = 0x00000100, /* firmware */ + ZYD_DEBUG_CMD = 0x00000200, /* fw commands */ + ZYD_DEBUG_ANY = 0xffffffff +}; +#define DPRINTF(sc, m, fmt, ...) do { \ + if (zyd_debug & (m)) \ + printf("%s: " fmt, __func__, ## __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(sc, m, fmt, ...) do { \ + (void) sc; \ +} while (0) +#endif + +#define zyd_do_request(sc,req,data) \ + usbd_do_request_flags((sc)->sc_udev, &(sc)->sc_mtx, req, data, 0, NULL, 5000) + +static device_probe_t zyd_match; +static device_attach_t zyd_attach; +static device_detach_t zyd_detach; + +static usb_callback_t zyd_intr_read_callback; +static usb_callback_t zyd_intr_write_callback; +static usb_callback_t zyd_bulk_read_callback; +static usb_callback_t zyd_bulk_write_callback; + +static struct ieee80211vap *zyd_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, int, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void zyd_vap_delete(struct ieee80211vap *); +static void zyd_tx_free(struct zyd_tx_data *, int); +static void zyd_setup_tx_list(struct zyd_softc *); +static void zyd_unsetup_tx_list(struct zyd_softc *); +static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int, + void *, int, int); +static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *); +static int zyd_read32(struct zyd_softc *, uint16_t, uint32_t *); +static int zyd_write16(struct zyd_softc *, uint16_t, uint16_t); +static int zyd_write32(struct zyd_softc *, uint16_t, uint32_t); +static int zyd_rfwrite(struct zyd_softc *, uint32_t); +static int zyd_lock_phy(struct zyd_softc *); +static int zyd_unlock_phy(struct zyd_softc *); +static int zyd_rf_attach(struct zyd_softc *, uint8_t); +static const char *zyd_rf_name(uint8_t); +static int zyd_hw_init(struct zyd_softc *); +static int zyd_read_pod(struct zyd_softc *); +static int zyd_read_eeprom(struct zyd_softc *); +static int zyd_get_macaddr(struct zyd_softc *); +static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *); +static int zyd_set_bssid(struct zyd_softc *, const uint8_t *); +static int zyd_switch_radio(struct zyd_softc *, int); +static int zyd_set_led(struct zyd_softc *, int, int); +static void zyd_set_multi(struct zyd_softc *); +static void zyd_update_mcast(struct ieee80211com *); +static int zyd_set_rxfilter(struct zyd_softc *); +static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *); +static int zyd_set_beacon_interval(struct zyd_softc *, int); +static void zyd_rx_data(struct usb_xfer *, int, uint16_t); +static int zyd_tx_start(struct zyd_softc *, struct mbuf *, + struct ieee80211_node *); +static int zyd_transmit(struct ieee80211com *, struct mbuf *); +static void zyd_start(struct zyd_softc *); +static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void zyd_parent(struct ieee80211com *); +static void zyd_init_locked(struct zyd_softc *); +static void zyd_stop(struct zyd_softc *); +static int zyd_loadfirmware(struct zyd_softc *); +static void zyd_scan_start(struct ieee80211com *); +static void zyd_scan_end(struct ieee80211com *); +static void zyd_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static void zyd_set_channel(struct ieee80211com *); +static int zyd_rfmd_init(struct zyd_rf *); +static int zyd_rfmd_switch_radio(struct zyd_rf *, int); +static int zyd_rfmd_set_channel(struct zyd_rf *, uint8_t); +static int zyd_al2230_init(struct zyd_rf *); +static int zyd_al2230_switch_radio(struct zyd_rf *, int); +static int zyd_al2230_set_channel(struct zyd_rf *, uint8_t); +static int zyd_al2230_set_channel_b(struct zyd_rf *, uint8_t); +static int zyd_al2230_init_b(struct zyd_rf *); +static int zyd_al7230B_init(struct zyd_rf *); +static int zyd_al7230B_switch_radio(struct zyd_rf *, int); +static int zyd_al7230B_set_channel(struct zyd_rf *, uint8_t); +static int zyd_al2210_init(struct zyd_rf *); +static int zyd_al2210_switch_radio(struct zyd_rf *, int); +static int zyd_al2210_set_channel(struct zyd_rf *, uint8_t); +static int zyd_gct_init(struct zyd_rf *); +static int zyd_gct_switch_radio(struct zyd_rf *, int); +static int zyd_gct_set_channel(struct zyd_rf *, uint8_t); +static int zyd_gct_mode(struct zyd_rf *); +static int zyd_gct_set_channel_synth(struct zyd_rf *, int, int); +static int zyd_gct_write(struct zyd_rf *, uint16_t); +static int zyd_gct_txgain(struct zyd_rf *, uint8_t); +static int zyd_maxim2_init(struct zyd_rf *); +static int zyd_maxim2_switch_radio(struct zyd_rf *, int); +static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t); + +static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY; +static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB; + +/* various supported device vendors/products */ +#define ZYD_ZD1211 0 +#define ZYD_ZD1211B 1 + +#define ZYD_ZD1211_DEV(v,p) \ + { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, ZYD_ZD1211) } +#define ZYD_ZD1211B_DEV(v,p) \ + { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, ZYD_ZD1211B) } +static const STRUCT_USB_HOST_ID zyd_devs[] = { + /* ZYD_ZD1211 */ + ZYD_ZD1211_DEV(3COM2, 3CRUSB10075), + ZYD_ZD1211_DEV(ABOCOM, WL54), + ZYD_ZD1211_DEV(ASUS, WL159G), + ZYD_ZD1211_DEV(CYBERTAN, TG54USB), + ZYD_ZD1211_DEV(DRAYTEK, VIGOR550), + ZYD_ZD1211_DEV(PLANEX2, GWUS54GD), + ZYD_ZD1211_DEV(PLANEX2, GWUS54GZL), + ZYD_ZD1211_DEV(PLANEX3, GWUS54GZ), + ZYD_ZD1211_DEV(PLANEX3, GWUS54MINI), + ZYD_ZD1211_DEV(SAGEM, XG760A), + ZYD_ZD1211_DEV(SENAO, NUB8301), + ZYD_ZD1211_DEV(SITECOMEU, WL113), + ZYD_ZD1211_DEV(SWEEX, ZD1211), + ZYD_ZD1211_DEV(TEKRAM, QUICKWLAN), + ZYD_ZD1211_DEV(TEKRAM, ZD1211_1), + ZYD_ZD1211_DEV(TEKRAM, ZD1211_2), + ZYD_ZD1211_DEV(TWINMOS, G240), + ZYD_ZD1211_DEV(UMEDIA, ALL0298V2), + ZYD_ZD1211_DEV(UMEDIA, TEW429UB_A), + ZYD_ZD1211_DEV(UMEDIA, TEW429UB), + ZYD_ZD1211_DEV(WISTRONNEWEB, UR055G), + ZYD_ZD1211_DEV(ZCOM, ZD1211), + ZYD_ZD1211_DEV(ZYDAS, ZD1211), + ZYD_ZD1211_DEV(ZYXEL, AG225H), + ZYD_ZD1211_DEV(ZYXEL, ZYAIRG220), + ZYD_ZD1211_DEV(ZYXEL, G200V2), + /* ZYD_ZD1211B */ + ZYD_ZD1211B_DEV(ACCTON, SMCWUSBG_NF), + ZYD_ZD1211B_DEV(ACCTON, SMCWUSBG), + ZYD_ZD1211B_DEV(ACCTON, ZD1211B), + ZYD_ZD1211B_DEV(ASUS, A9T_WIFI), + ZYD_ZD1211B_DEV(BELKIN, F5D7050_V4000), + ZYD_ZD1211B_DEV(BELKIN, ZD1211B), + ZYD_ZD1211B_DEV(CISCOLINKSYS, WUSBF54G), + ZYD_ZD1211B_DEV(FIBERLINE, WL430U), + ZYD_ZD1211B_DEV(MELCO, KG54L), + ZYD_ZD1211B_DEV(PHILIPS, SNU5600), + ZYD_ZD1211B_DEV(PLANEX2, GW_US54GXS), + ZYD_ZD1211B_DEV(SAGEM, XG76NA), + ZYD_ZD1211B_DEV(SITECOMEU, ZD1211B), + ZYD_ZD1211B_DEV(UMEDIA, TEW429UBC1), + ZYD_ZD1211B_DEV(USR, USR5423), + ZYD_ZD1211B_DEV(VTECH, ZD1211B), + ZYD_ZD1211B_DEV(ZCOM, ZD1211B), + ZYD_ZD1211B_DEV(ZYDAS, ZD1211B), + ZYD_ZD1211B_DEV(ZYXEL, M202), + ZYD_ZD1211B_DEV(ZYXEL, G202), + ZYD_ZD1211B_DEV(ZYXEL, G220V2) +}; + +static const struct usb_config zyd_config[ZYD_N_TRANSFER] = { + [ZYD_BULK_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = ZYD_MAX_TXBUFSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = zyd_bulk_write_callback, + .ep_index = 0, + .timeout = 10000, /* 10 seconds */ + }, + [ZYD_BULK_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = ZYX_MAX_RXBUFSZ, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = zyd_bulk_read_callback, + .ep_index = 0, + }, + [ZYD_INTR_WR] = { + .type = UE_BULK_INTR, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = sizeof(struct zyd_cmd), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = zyd_intr_write_callback, + .timeout = 1000, /* 1 second */ + .ep_index = 1, + }, + [ZYD_INTR_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = sizeof(struct zyd_cmd), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = zyd_intr_read_callback, + }, +}; +#define zyd_read16_m(sc, val, data) do { \ + error = zyd_read16(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define zyd_write16_m(sc, val, data) do { \ + error = zyd_write16(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define zyd_read32_m(sc, val, data) do { \ + error = zyd_read32(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define zyd_write32_m(sc, val, data) do { \ + error = zyd_write32(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) + +static int +zyd_match(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != ZYD_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa)); +} + +static int +zyd_attach(device_t dev) +{ + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct zyd_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + uint8_t iface_index; + int error; + + if (uaa->info.bcdDevice < 0x4330) { + device_printf(dev, "device version mismatch: 0x%X " + "(only >= 43.30 supported)\n", + uaa->info.bcdDevice); + return (EINVAL); + } + + device_set_usb_desc(dev); + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + sc->sc_macrev = USB_GET_DRIVER_INFO(uaa); + + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), + MTX_NETWORK_LOCK, MTX_DEF); + STAILQ_INIT(&sc->sc_rqh); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + iface_index = ZYD_IFACE_INDEX; + error = usbd_transfer_setup(uaa->device, + &iface_index, sc->sc_xfer, zyd_config, + ZYD_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto detach; + } + + ZYD_LOCK(sc); + if ((error = zyd_get_macaddr(sc)) != 0) { + device_printf(sc->sc_dev, "could not read EEPROM\n"); + ZYD_UNLOCK(sc); + goto detach; + } + ZYD_UNLOCK(sc); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(dev); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode */ + | IEEE80211_C_MONITOR /* monitor mode */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ + | IEEE80211_C_WPA /* 802.11i */ + ; + + zyd_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + ic->ic_raw_xmit = zyd_raw_xmit; + ic->ic_scan_start = zyd_scan_start; + ic->ic_scan_end = zyd_scan_end; + ic->ic_getradiocaps = zyd_getradiocaps; + ic->ic_set_channel = zyd_set_channel; + ic->ic_vap_create = zyd_vap_create; + ic->ic_vap_delete = zyd_vap_delete; + ic->ic_update_mcast = zyd_update_mcast; + ic->ic_update_promisc = zyd_update_mcast; + ic->ic_parent = zyd_parent; + ic->ic_transmit = zyd_transmit; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + ZYD_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + ZYD_RX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +detach: + zyd_detach(dev); + return (ENXIO); /* failure */ +} + +static void +zyd_drain_mbufq(struct zyd_softc *sc) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + ZYD_LOCK_ASSERT(sc, MA_OWNED); + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + ieee80211_free_node(ni); + m_freem(m); + } +} + + +static int +zyd_detach(device_t dev) +{ + struct zyd_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + unsigned int x; + + /* + * Prevent further allocations from RX/TX data + * lists and ioctls: + */ + ZYD_LOCK(sc); + sc->sc_flags |= ZYD_FLAG_DETACHED; + zyd_drain_mbufq(sc); + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + ZYD_UNLOCK(sc); + + /* drain USB transfers */ + for (x = 0; x != ZYD_N_TRANSFER; x++) + usbd_transfer_drain(sc->sc_xfer[x]); + + /* free TX list, if any */ + ZYD_LOCK(sc); + zyd_unsetup_tx_list(sc); + ZYD_UNLOCK(sc); + + /* free USB transfers and some data buffers */ + usbd_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER); + + if (ic->ic_softc == sc) + ieee80211_ifdetach(ic); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static struct ieee80211vap * +zyd_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct zyd_vap *zvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return (NULL); + zvp = malloc(sizeof(struct zyd_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &zvp->vap; + + /* enable s/w bmiss handling for sta mode */ + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(zvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + zvp->newstate = vap->iv_newstate; + vap->iv_newstate = zyd_newstate; + + ieee80211_ratectl_init(vap); + ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return (vap); +} + +static void +zyd_vap_delete(struct ieee80211vap *vap) +{ + struct zyd_vap *zvp = ZYD_VAP(vap); + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + free(zvp, M_80211_VAP); +} + +static void +zyd_tx_free(struct zyd_tx_data *data, int txerr) +{ + struct zyd_softc *sc = data->sc; + + if (data->m != NULL) { + ieee80211_tx_complete(data->ni, data->m, txerr); + data->m = NULL; + data->ni = NULL; + } + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; +} + +static void +zyd_setup_tx_list(struct zyd_softc *sc) +{ + struct zyd_tx_data *data; + int i; + + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + for (i = 0; i < ZYD_TX_LIST_CNT; i++) { + data = &sc->tx_data[i]; + + data->sc = sc; + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } +} + +static void +zyd_unsetup_tx_list(struct zyd_softc *sc) +{ + struct zyd_tx_data *data; + int i; + + /* make sure any subsequent use of the queues will fail */ + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + /* free up all node references and mbufs */ + for (i = 0; i < ZYD_TX_LIST_CNT; i++) { + data = &sc->tx_data[i]; + + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +static int +zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct zyd_vap *zvp = ZYD_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct zyd_softc *sc = ic->ic_softc; + int error; + + DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + ZYD_LOCK(sc); + switch (nstate) { + case IEEE80211_S_AUTH: + zyd_set_chan(sc, ic->ic_curchan); + break; + case IEEE80211_S_RUN: + if (vap->iv_opmode == IEEE80211_M_MONITOR) + break; + + /* turn link LED on */ + error = zyd_set_led(sc, ZYD_LED1, 1); + if (error != 0) + break; + + /* make data LED blink upon Tx */ + zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1); + + IEEE80211_ADDR_COPY(sc->sc_bssid, vap->iv_bss->ni_bssid); + zyd_set_bssid(sc, sc->sc_bssid); + break; + default: + break; + } +fail: + ZYD_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (zvp->newstate(vap, nstate, arg)); +} + +/* + * Callback handler for interrupt transfer + */ +static void +zyd_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct zyd_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_node *ni; + struct zyd_cmd *cmd = &sc->sc_ibuf; + struct usb_page_cache *pc; + int datalen; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, cmd, sizeof(*cmd)); + + switch (le16toh(cmd->code)) { + case ZYD_NOTIF_RETRYSTATUS: + { + struct zyd_notif_retry *retry = + (struct zyd_notif_retry *)cmd->data; + + DPRINTF(sc, ZYD_DEBUG_TX_PROC, + "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n", + le16toh(retry->rate), ether_sprintf(retry->macaddr), + le16toh(retry->count)&0xff, le16toh(retry->count)); + + /* + * Find the node to which the packet was sent and + * update its retry statistics. In BSS mode, this node + * is the AP we're associated to so no lookup is + * actually needed. + */ + ni = ieee80211_find_txnode(vap, retry->macaddr); + if (ni != NULL) { + struct ieee80211_ratectl_tx_status *txs = + &sc->sc_txs; + int retrycnt = + (int)(le16toh(retry->count) & 0xff); + + txs->flags = + IEEE80211_RATECTL_STATUS_LONG_RETRY; + txs->long_retries = retrycnt; + if (le16toh(retry->count) & 0x100) { + txs->status = + IEEE80211_RATECTL_TX_FAIL_LONG; + } else { + txs->status = + IEEE80211_RATECTL_TX_SUCCESS; + } + + + ieee80211_ratectl_tx_complete(ni, txs); + ieee80211_free_node(ni); + } + if (le16toh(retry->count) & 0x100) + /* too many retries */ + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, + 1); + break; + } + case ZYD_NOTIF_IORD: + { + struct zyd_rq *rqp; + + if (le16toh(*(uint16_t *)cmd->data) == ZYD_CR_INTERRUPT) + break; /* HMAC interrupt */ + + datalen = actlen - sizeof(cmd->code); + datalen -= 2; /* XXX: padding? */ + + STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { + int i; + int count; + + if (rqp->olen != datalen) + continue; + count = rqp->olen / sizeof(struct zyd_pair); + for (i = 0; i < count; i++) { + if (*(((const uint16_t *)rqp->idata) + i) != + (((struct zyd_pair *)cmd->data) + i)->reg) + break; + } + if (i != count) + continue; + /* copy answer into caller-supplied buffer */ + memcpy(rqp->odata, cmd->data, rqp->olen); + DPRINTF(sc, ZYD_DEBUG_CMD, + "command %p complete, data = %*D \n", + rqp, rqp->olen, (char *)rqp->odata, ":"); + wakeup(rqp); /* wakeup caller */ + break; + } + if (rqp == NULL) { + device_printf(sc->sc_dev, + "unexpected IORD notification %*D\n", + datalen, cmd->data, ":"); + } + break; + } + default: + device_printf(sc->sc_dev, "unknown notification %x\n", + le16toh(cmd->code)); + } + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + + default: /* Error */ + DPRINTF(sc, ZYD_DEBUG_CMD, "error = %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +zyd_intr_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct zyd_softc *sc = usbd_xfer_softc(xfer); + struct zyd_rq *rqp, *cmd; + struct usb_page_cache *pc; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + cmd = usbd_xfer_get_priv(xfer); + DPRINTF(sc, ZYD_DEBUG_CMD, "command %p transferred\n", cmd); + STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { + /* Ensure the cached rq pointer is still valid */ + if (rqp == cmd && + (rqp->flags & ZYD_CMD_FLAG_READ) == 0) + wakeup(rqp); /* wakeup caller */ + } + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { + if (rqp->flags & ZYD_CMD_FLAG_SENT) + continue; + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, rqp->cmd, rqp->ilen); + + usbd_xfer_set_frame_len(xfer, 0, rqp->ilen); + usbd_xfer_set_priv(xfer, rqp); + rqp->flags |= ZYD_CMD_FLAG_SENT; + usbd_transfer_submit(xfer); + break; + } + break; + + default: /* Error */ + DPRINTF(sc, ZYD_DEBUG_ANY, "error = %s\n", + usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static int +zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen, + void *odata, int olen, int flags) +{ + struct zyd_cmd cmd; + struct zyd_rq rq; + int error; + + if (ilen > (int)sizeof(cmd.data)) + return (EINVAL); + + cmd.code = htole16(code); + memcpy(cmd.data, idata, ilen); + DPRINTF(sc, ZYD_DEBUG_CMD, "sending cmd %p = %*D\n", + &rq, ilen, idata, ":"); + + rq.cmd = &cmd; + rq.idata = idata; + rq.odata = odata; + rq.ilen = sizeof(uint16_t) + ilen; + rq.olen = olen; + rq.flags = flags; + STAILQ_INSERT_TAIL(&sc->sc_rqh, &rq, rq); + usbd_transfer_start(sc->sc_xfer[ZYD_INTR_RD]); + usbd_transfer_start(sc->sc_xfer[ZYD_INTR_WR]); + + /* wait at most one second for command reply */ + error = mtx_sleep(&rq, &sc->sc_mtx, 0 , "zydcmd", hz); + if (error) + device_printf(sc->sc_dev, "command timeout\n"); + STAILQ_REMOVE(&sc->sc_rqh, &rq, zyd_rq, rq); + DPRINTF(sc, ZYD_DEBUG_CMD, "finsihed cmd %p, error = %d \n", + &rq, error); + + return (error); +} + +static int +zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val) +{ + struct zyd_pair tmp; + int error; + + reg = htole16(reg); + error = zyd_cmd(sc, ZYD_CMD_IORD, ®, sizeof(reg), &tmp, sizeof(tmp), + ZYD_CMD_FLAG_READ); + if (error == 0) + *val = le16toh(tmp.val); + return (error); +} + +static int +zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val) +{ + struct zyd_pair tmp[2]; + uint16_t regs[2]; + int error; + + regs[0] = htole16(ZYD_REG32_HI(reg)); + regs[1] = htole16(ZYD_REG32_LO(reg)); + error = zyd_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp), + ZYD_CMD_FLAG_READ); + if (error == 0) + *val = le16toh(tmp[0].val) << 16 | le16toh(tmp[1].val); + return (error); +} + +static int +zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) +{ + struct zyd_pair pair; + + pair.reg = htole16(reg); + pair.val = htole16(val); + + return zyd_cmd(sc, ZYD_CMD_IOWR, &pair, sizeof(pair), NULL, 0, 0); +} + +static int +zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val) +{ + struct zyd_pair pair[2]; + + pair[0].reg = htole16(ZYD_REG32_HI(reg)); + pair[0].val = htole16(val >> 16); + pair[1].reg = htole16(ZYD_REG32_LO(reg)); + pair[1].val = htole16(val & 0xffff); + + return zyd_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); +} + +static int +zyd_rfwrite(struct zyd_softc *sc, uint32_t val) +{ + struct zyd_rf *rf = &sc->sc_rf; + struct zyd_rfwrite_cmd req; + uint16_t cr203; + int error, i; + + zyd_read16_m(sc, ZYD_CR203, &cr203); + cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); + + req.code = htole16(2); + req.width = htole16(rf->width); + for (i = 0; i < rf->width; i++) { + req.bit[i] = htole16(cr203); + if (val & (1 << (rf->width - 1 - i))) + req.bit[i] |= htole16(ZYD_RF_DATA); + } + error = zyd_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + 2 * rf->width, NULL, 0, 0); +fail: + return (error); +} + +static int +zyd_rfwrite_cr(struct zyd_softc *sc, uint32_t val) +{ + int error; + + zyd_write16_m(sc, ZYD_CR244, (val >> 16) & 0xff); + zyd_write16_m(sc, ZYD_CR243, (val >> 8) & 0xff); + zyd_write16_m(sc, ZYD_CR242, (val >> 0) & 0xff); +fail: + return (error); +} + +static int +zyd_lock_phy(struct zyd_softc *sc) +{ + int error; + uint32_t tmp; + + zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); + tmp &= ~ZYD_UNLOCK_PHY_REGS; + zyd_write32_m(sc, ZYD_MAC_MISC, tmp); +fail: + return (error); +} + +static int +zyd_unlock_phy(struct zyd_softc *sc) +{ + int error; + uint32_t tmp; + + zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); + tmp |= ZYD_UNLOCK_PHY_REGS; + zyd_write32_m(sc, ZYD_MAC_MISC, tmp); +fail: + return (error); +} + +/* + * RFMD RF methods. + */ +static int +zyd_rfmd_init(struct zyd_rf *rf) +{ + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY; + static const uint32_t rfini[] = ZYD_RFMD_RF; + int i, error; + + /* init RF-dependent PHY registers */ + for (i = 0; i < nitems(phyini); i++) { + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + } + + /* init RFMD radio */ + for (i = 0; i < nitems(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } +fail: + return (error); +} + +static int +zyd_rfmd_switch_radio(struct zyd_rf *rf, int on) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + + zyd_write16_m(sc, ZYD_CR10, on ? 0x89 : 0x15); + zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x81); +fail: + return (error); +} + +static int +zyd_rfmd_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_RFMD_CHANTABLE; + + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + +fail: + return (error); +} + +/* + * AL2230 RF methods. + */ +static int +zyd_al2230_init(struct zyd_rf *rf) +{ + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; + static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; + static const struct zyd_phy_pair phypll[] = { + { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, + { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } + }; + static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1; + static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2; + static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3; + int i, error; + + /* init RF-dependent PHY registers */ + for (i = 0; i < nitems(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { + for (i = 0; i < nitems(phy2230s); i++) + zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); + } + + /* init AL2230 radio */ + for (i = 0; i < nitems(rfini1); i++) { + error = zyd_rfwrite(sc, rfini1[i]); + if (error != 0) + goto fail; + } + + if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) + error = zyd_rfwrite(sc, 0x000824); + else + error = zyd_rfwrite(sc, 0x0005a4); + if (error != 0) + goto fail; + + for (i = 0; i < nitems(rfini2); i++) { + error = zyd_rfwrite(sc, rfini2[i]); + if (error != 0) + goto fail; + } + + for (i = 0; i < nitems(phypll); i++) + zyd_write16_m(sc, phypll[i].reg, phypll[i].val); + + for (i = 0; i < nitems(rfini3); i++) { + error = zyd_rfwrite(sc, rfini3[i]); + if (error != 0) + goto fail; + } +fail: + return (error); +} + +static int +zyd_al2230_fini(struct zyd_rf *rf) +{ + int error, i; + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1; + + for (i = 0; i < nitems(phy); i++) + zyd_write16_m(sc, phy[i].reg, phy[i].val); + + if (sc->sc_newphy != 0) + zyd_write16_m(sc, ZYD_CR9, 0xe1); + + zyd_write16_m(sc, ZYD_CR203, 0x6); +fail: + return (error); +} + +static int +zyd_al2230_init_b(struct zyd_rf *rf) +{ + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; + static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2; + static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3; + static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; + static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; + static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1; + static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2; + static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3; + static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE; + int i, error; + + for (i = 0; i < nitems(phy1); i++) + zyd_write16_m(sc, phy1[i].reg, phy1[i].val); + + /* init RF-dependent PHY registers */ + for (i = 0; i < nitems(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { + for (i = 0; i < nitems(phy2230s); i++) + zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); + } + + for (i = 0; i < 3; i++) { + error = zyd_rfwrite_cr(sc, zyd_al2230_chtable[0][i]); + if (error != 0) + return (error); + } + + for (i = 0; i < nitems(rfini_part1); i++) { + error = zyd_rfwrite_cr(sc, rfini_part1[i]); + if (error != 0) + return (error); + } + + if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) + error = zyd_rfwrite(sc, 0x241000); + else + error = zyd_rfwrite(sc, 0x25a000); + if (error != 0) + goto fail; + + for (i = 0; i < nitems(rfini_part2); i++) { + error = zyd_rfwrite_cr(sc, rfini_part2[i]); + if (error != 0) + return (error); + } + + for (i = 0; i < nitems(phy2); i++) + zyd_write16_m(sc, phy2[i].reg, phy2[i].val); + + for (i = 0; i < nitems(rfini_part3); i++) { + error = zyd_rfwrite_cr(sc, rfini_part3[i]); + if (error != 0) + return (error); + } + + for (i = 0; i < nitems(phy3); i++) + zyd_write16_m(sc, phy3[i].reg, phy3[i].val); + + error = zyd_al2230_fini(rf); +fail: + return (error); +} + +static int +zyd_al2230_switch_radio(struct zyd_rf *rf, int on) +{ + struct zyd_softc *sc = rf->rf_sc; + int error, on251 = (sc->sc_macrev == ZYD_ZD1211) ? 0x3f : 0x7f; + + zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); + zyd_write16_m(sc, ZYD_CR251, on ? on251 : 0x2f); +fail: + return (error); +} + +static int +zyd_al2230_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + int error, i; + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phy1[] = { + { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 }, + }; + static const struct { + uint32_t r1, r2, r3; + } rfprog[] = ZYD_AL2230_CHANTABLE; + + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r3); + if (error != 0) + goto fail; + + for (i = 0; i < nitems(phy1); i++) + zyd_write16_m(sc, phy1[i].reg, phy1[i].val); +fail: + return (error); +} + +static int +zyd_al2230_set_channel_b(struct zyd_rf *rf, uint8_t chan) +{ + int error, i; + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; + static const struct { + uint32_t r1, r2, r3; + } rfprog[] = ZYD_AL2230_CHANTABLE_B; + + for (i = 0; i < nitems(phy1); i++) + zyd_write16_m(sc, phy1[i].reg, phy1[i].val); + + error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r3); + if (error != 0) + goto fail; + error = zyd_al2230_fini(rf); +fail: + return (error); +} + +#define ZYD_AL2230_PHY_BANDEDGE6 \ +{ \ + { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ + { ZYD_CR47, 0x1e } \ +} + +static int +zyd_al2230_bandedge6(struct zyd_rf *rf, struct ieee80211_channel *c) +{ + int error = 0, i; + struct zyd_softc *sc = rf->rf_sc; + struct ieee80211com *ic = &sc->sc_ic; + struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6; + int chan = ieee80211_chan2ieee(ic, c); + + if (chan == 1 || chan == 11) + r[0].val = 0x12; + + for (i = 0; i < nitems(r); i++) + zyd_write16_m(sc, r[i].reg, r[i].val); +fail: + return (error); +} + +/* + * AL7230B RF methods. + */ +static int +zyd_al7230B_init(struct zyd_rf *rf) +{ + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1; + static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2; + static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3; + static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1; + static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2; + int i, error; + + /* for AL7230B, PHY and RF need to be initialized in "phases" */ + + /* init RF-dependent PHY registers, part one */ + for (i = 0; i < nitems(phyini_1); i++) + zyd_write16_m(sc, phyini_1[i].reg, phyini_1[i].val); + + /* init AL7230B radio, part one */ + for (i = 0; i < nitems(rfini_1); i++) { + if ((error = zyd_rfwrite(sc, rfini_1[i])) != 0) + return (error); + } + /* init RF-dependent PHY registers, part two */ + for (i = 0; i < nitems(phyini_2); i++) + zyd_write16_m(sc, phyini_2[i].reg, phyini_2[i].val); + + /* init AL7230B radio, part two */ + for (i = 0; i < nitems(rfini_2); i++) { + if ((error = zyd_rfwrite(sc, rfini_2[i])) != 0) + return (error); + } + /* init RF-dependent PHY registers, part three */ + for (i = 0; i < nitems(phyini_3); i++) + zyd_write16_m(sc, phyini_3[i].reg, phyini_3[i].val); +fail: + return (error); +} + +static int +zyd_al7230B_switch_radio(struct zyd_rf *rf, int on) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + + zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); + zyd_write16_m(sc, ZYD_CR251, on ? 0x3f : 0x2f); +fail: + return (error); +} + +static int +zyd_al7230B_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + struct zyd_softc *sc = rf->rf_sc; + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_AL7230B_CHANTABLE; + static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL; + int i, error; + + zyd_write16_m(sc, ZYD_CR240, 0x57); + zyd_write16_m(sc, ZYD_CR251, 0x2f); + + for (i = 0; i < nitems(rfsc); i++) { + if ((error = zyd_rfwrite(sc, rfsc[i])) != 0) + return (error); + } + + zyd_write16_m(sc, ZYD_CR128, 0x14); + zyd_write16_m(sc, ZYD_CR129, 0x12); + zyd_write16_m(sc, ZYD_CR130, 0x10); + zyd_write16_m(sc, ZYD_CR38, 0x38); + zyd_write16_m(sc, ZYD_CR136, 0xdf); + + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, 0x3c9000); + if (error != 0) + goto fail; + + zyd_write16_m(sc, ZYD_CR251, 0x3f); + zyd_write16_m(sc, ZYD_CR203, 0x06); + zyd_write16_m(sc, ZYD_CR240, 0x08); +fail: + return (error); +} + +/* + * AL2210 RF methods. + */ +static int +zyd_al2210_init(struct zyd_rf *rf) +{ + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY; + static const uint32_t rfini[] = ZYD_AL2210_RF; + uint32_t tmp; + int i, error; + + zyd_write32_m(sc, ZYD_CR18, 2); + + /* init RF-dependent PHY registers */ + for (i = 0; i < nitems(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + /* init AL2210 radio */ + for (i = 0; i < nitems(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + zyd_write16_m(sc, ZYD_CR47, 0x1e); + zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); + zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); + zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); + zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); + zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); + zyd_write16_m(sc, ZYD_CR47, 0x1e); + zyd_write32_m(sc, ZYD_CR18, 3); +fail: + return (error); +} + +static int +zyd_al2210_switch_radio(struct zyd_rf *rf, int on) +{ + /* vendor driver does nothing for this RF chip */ + + return (0); +} + +static int +zyd_al2210_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE; + uint32_t tmp; + + zyd_write32_m(sc, ZYD_CR18, 2); + zyd_write16_m(sc, ZYD_CR47, 0x1e); + zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); + zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); + zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); + zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); + zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); + zyd_write16_m(sc, ZYD_CR47, 0x1e); + + /* actually set the channel */ + error = zyd_rfwrite(sc, rfprog[chan - 1]); + if (error != 0) + goto fail; + + zyd_write32_m(sc, ZYD_CR18, 3); +fail: + return (error); +} + +/* + * GCT RF methods. + */ +static int +zyd_gct_init(struct zyd_rf *rf) +{ +#define ZYD_GCT_INTR_REG 0x85c1 + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY; + static const uint32_t rfini[] = ZYD_GCT_RF; + static const uint16_t vco[11][7] = ZYD_GCT_VCO; + int i, idx = -1, error; + uint16_t data; + + /* init RF-dependent PHY registers */ + for (i = 0; i < nitems(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + /* init cgt radio */ + for (i = 0; i < nitems(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + + error = zyd_gct_mode(rf); + if (error != 0) + return (error); + + for (i = 0; i < (int)(nitems(vco) - 1); i++) { + error = zyd_gct_set_channel_synth(rf, 1, 0); + if (error != 0) + goto fail; + error = zyd_gct_write(rf, vco[i][0]); + if (error != 0) + goto fail; + zyd_write16_m(sc, ZYD_GCT_INTR_REG, 0xf); + zyd_read16_m(sc, ZYD_GCT_INTR_REG, &data); + if ((data & 0xf) == 0) { + idx = i; + break; + } + } + if (idx == -1) { + error = zyd_gct_set_channel_synth(rf, 1, 1); + if (error != 0) + goto fail; + error = zyd_gct_write(rf, 0x6662); + if (error != 0) + goto fail; + } + + rf->idx = idx; + zyd_write16_m(sc, ZYD_CR203, 0x6); +fail: + return (error); +#undef ZYD_GCT_INTR_REG +} + +static int +zyd_gct_mode(struct zyd_rf *rf) +{ + struct zyd_softc *sc = rf->rf_sc; + static const uint32_t mode[] = { + 0x25f98, 0x25f9a, 0x25f94, 0x27fd4 + }; + int i, error; + + for (i = 0; i < nitems(mode); i++) { + if ((error = zyd_rfwrite(sc, mode[i])) != 0) + break; + } + return (error); +} + +static int +zyd_gct_set_channel_synth(struct zyd_rf *rf, int chan, int acal) +{ + int error, idx = chan - 1; + struct zyd_softc *sc = rf->rf_sc; + static uint32_t acal_synth[] = ZYD_GCT_CHANNEL_ACAL; + static uint32_t std_synth[] = ZYD_GCT_CHANNEL_STD; + static uint32_t div_synth[] = ZYD_GCT_CHANNEL_DIV; + + error = zyd_rfwrite(sc, + (acal == 1) ? acal_synth[idx] : std_synth[idx]); + if (error != 0) + return (error); + return zyd_rfwrite(sc, div_synth[idx]); +} + +static int +zyd_gct_write(struct zyd_rf *rf, uint16_t value) +{ + struct zyd_softc *sc = rf->rf_sc; + + return zyd_rfwrite(sc, 0x300000 | 0x40000 | value); +} + +static int +zyd_gct_switch_radio(struct zyd_rf *rf, int on) +{ + int error; + struct zyd_softc *sc = rf->rf_sc; + + error = zyd_rfwrite(sc, on ? 0x25f94 : 0x25f90); + if (error != 0) + return (error); + + zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); + zyd_write16_m(sc, ZYD_CR251, + on ? ((sc->sc_macrev == ZYD_ZD1211B) ? 0x7f : 0x3f) : 0x2f); +fail: + return (error); +} + +static int +zyd_gct_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + int error, i; + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair cmd[] = { + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, + { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, + }; + static const uint16_t vco[11][7] = ZYD_GCT_VCO; + + error = zyd_gct_set_channel_synth(rf, chan, 0); + if (error != 0) + goto fail; + error = zyd_gct_write(rf, (rf->idx == -1) ? 0x6662 : + vco[rf->idx][((chan - 1) / 2)]); + if (error != 0) + goto fail; + error = zyd_gct_mode(rf); + if (error != 0) + return (error); + for (i = 0; i < nitems(cmd); i++) + zyd_write16_m(sc, cmd[i].reg, cmd[i].val); + error = zyd_gct_txgain(rf, chan); + if (error != 0) + return (error); + zyd_write16_m(sc, ZYD_CR203, 0x6); +fail: + return (error); +} + +static int +zyd_gct_txgain(struct zyd_rf *rf, uint8_t chan) +{ + struct zyd_softc *sc = rf->rf_sc; + static uint32_t txgain[] = ZYD_GCT_TXGAIN; + uint8_t idx = sc->sc_pwrint[chan - 1]; + + if (idx >= nitems(txgain)) { + device_printf(sc->sc_dev, "could not set TX gain (%d %#x)\n", + chan, idx); + return 0; + } + + return zyd_rfwrite(sc, 0x700000 | txgain[idx]); +} + +/* + * Maxim2 RF methods. + */ +static int +zyd_maxim2_init(struct zyd_rf *rf) +{ + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; + static const uint32_t rfini[] = ZYD_MAXIM2_RF; + uint16_t tmp; + int i, error; + + /* init RF-dependent PHY registers */ + for (i = 0; i < nitems(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* init maxim2 radio */ + for (i = 0; i < nitems(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); +fail: + return (error); +} + +static int +zyd_maxim2_switch_radio(struct zyd_rf *rf, int on) +{ + + /* vendor driver does nothing for this RF chip */ + return (0); +} + +static int +zyd_maxim2_set_channel(struct zyd_rf *rf, uint8_t chan) +{ + struct zyd_softc *sc = rf->rf_sc; + static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; + static const uint32_t rfini[] = ZYD_MAXIM2_RF; + static const struct { + uint32_t r1, r2; + } rfprog[] = ZYD_MAXIM2_CHANTABLE; + uint16_t tmp; + int i, error; + + /* + * Do the same as we do when initializing it, except for the channel + * values coming from the two channel tables. + */ + + /* init RF-dependent PHY registers */ + for (i = 0; i < nitems(phyini); i++) + zyd_write16_m(sc, phyini[i].reg, phyini[i].val); + + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); + + /* first two values taken from the chantables */ + error = zyd_rfwrite(sc, rfprog[chan - 1].r1); + if (error != 0) + goto fail; + error = zyd_rfwrite(sc, rfprog[chan - 1].r2); + if (error != 0) + goto fail; + + /* init maxim2 radio - skipping the two first values */ + for (i = 2; i < nitems(rfini); i++) { + if ((error = zyd_rfwrite(sc, rfini[i])) != 0) + return (error); + } + zyd_read16_m(sc, ZYD_CR203, &tmp); + zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); +fail: + return (error); +} + +static int +zyd_rf_attach(struct zyd_softc *sc, uint8_t type) +{ + struct zyd_rf *rf = &sc->sc_rf; + + rf->rf_sc = sc; + rf->update_pwr = 1; + + switch (type) { + case ZYD_RF_RFMD: + rf->init = zyd_rfmd_init; + rf->switch_radio = zyd_rfmd_switch_radio; + rf->set_channel = zyd_rfmd_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL2230: + case ZYD_RF_AL2230S: + if (sc->sc_macrev == ZYD_ZD1211B) { + rf->init = zyd_al2230_init_b; + rf->set_channel = zyd_al2230_set_channel_b; + } else { + rf->init = zyd_al2230_init; + rf->set_channel = zyd_al2230_set_channel; + } + rf->switch_radio = zyd_al2230_switch_radio; + rf->bandedge6 = zyd_al2230_bandedge6; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL7230B: + rf->init = zyd_al7230B_init; + rf->switch_radio = zyd_al7230B_switch_radio; + rf->set_channel = zyd_al7230B_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_AL2210: + rf->init = zyd_al2210_init; + rf->switch_radio = zyd_al2210_switch_radio; + rf->set_channel = zyd_al2210_set_channel; + rf->width = 24; /* 24-bit RF values */ + break; + case ZYD_RF_MAXIM_NEW: + case ZYD_RF_GCT: + rf->init = zyd_gct_init; + rf->switch_radio = zyd_gct_switch_radio; + rf->set_channel = zyd_gct_set_channel; + rf->width = 24; /* 24-bit RF values */ + rf->update_pwr = 0; + break; + case ZYD_RF_MAXIM_NEW2: + rf->init = zyd_maxim2_init; + rf->switch_radio = zyd_maxim2_switch_radio; + rf->set_channel = zyd_maxim2_set_channel; + rf->width = 18; /* 18-bit RF values */ + break; + default: + device_printf(sc->sc_dev, + "sorry, radio \"%s\" is not supported yet\n", + zyd_rf_name(type)); + return (EINVAL); + } + return (0); +} + +static const char * +zyd_rf_name(uint8_t type) +{ + static const char * const zyd_rfs[] = { + "unknown", "unknown", "UW2451", "UCHIP", "AL2230", + "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", + "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", + "PHILIPS" + }; + + return zyd_rfs[(type > 15) ? 0 : type]; +} + +static int +zyd_hw_init(struct zyd_softc *sc) +{ + int error; + const struct zyd_phy_pair *phyp; + struct zyd_rf *rf = &sc->sc_rf; + uint16_t val; + + /* specify that the plug and play is finished */ + zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); + zyd_read16_m(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_fwbase); + DPRINTF(sc, ZYD_DEBUG_FW, "firmware base address=0x%04x\n", + sc->sc_fwbase); + + /* retrieve firmware revision number */ + zyd_read16_m(sc, sc->sc_fwbase + ZYD_FW_FIRMWARE_REV, &sc->sc_fwrev); + zyd_write32_m(sc, ZYD_CR_GPI_EN, 0); + zyd_write32_m(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); + /* set mandatory rates - XXX assumes 802.11b/g */ + zyd_write32_m(sc, ZYD_MAC_MAN_RATE, 0x150f); + + /* disable interrupts */ + zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); + + if ((error = zyd_read_pod(sc)) != 0) { + device_printf(sc->sc_dev, "could not read EEPROM\n"); + goto fail; + } + + /* PHY init (resetting) */ + error = zyd_lock_phy(sc); + if (error != 0) + goto fail; + phyp = (sc->sc_macrev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy; + for (; phyp->reg != 0; phyp++) + zyd_write16_m(sc, phyp->reg, phyp->val); + if (sc->sc_macrev == ZYD_ZD1211 && sc->sc_fix_cr157 != 0) { + zyd_read16_m(sc, ZYD_EEPROM_PHY_REG, &val); + zyd_write32_m(sc, ZYD_CR157, val >> 8); + } + error = zyd_unlock_phy(sc); + if (error != 0) + goto fail; + + /* HMAC init */ + zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000020); + zyd_write32_m(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); + zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_GHTBL, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_GHTBH, 0x80000000); + zyd_write32_m(sc, ZYD_MAC_MISC, 0x000000a4); + zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); + zyd_write32_m(sc, ZYD_MAC_BCNCFG, 0x00f00401); + zyd_write32_m(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000080); + zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); + zyd_write32_m(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); + zyd_write32_m(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); + zyd_write32_m(sc, ZYD_CR_PS_CTRL, 0x10000000); + zyd_write32_m(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); + zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); + zyd_write32_m(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); + zyd_write32_m(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032); + zyd_write32_m(sc, ZYD_MAC_CAM_MODE, 0x3); + + if (sc->sc_macrev == ZYD_ZD1211) { + zyd_write32_m(sc, ZYD_MAC_RETRY, 0x00000002); + zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); + } else { + zyd_write32_m(sc, ZYD_MACB_MAX_RETRY, 0x02020202); + zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); + zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); + zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); + zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); + zyd_write32_m(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); + zyd_write32_m(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); + zyd_write32_m(sc, ZYD_MACB_TXOP, 0x01800824); + zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff); + } + + /* init beacon interval to 100ms */ + if ((error = zyd_set_beacon_interval(sc, 100)) != 0) + goto fail; + + if ((error = zyd_rf_attach(sc, sc->sc_rfrev)) != 0) { + device_printf(sc->sc_dev, "could not attach RF, rev 0x%x\n", + sc->sc_rfrev); + goto fail; + } + + /* RF chip init */ + error = zyd_lock_phy(sc); + if (error != 0) + goto fail; + error = (*rf->init)(rf); + if (error != 0) { + device_printf(sc->sc_dev, + "radio initialization failed, error %d\n", error); + goto fail; + } + error = zyd_unlock_phy(sc); + if (error != 0) + goto fail; + + if ((error = zyd_read_eeprom(sc)) != 0) { + device_printf(sc->sc_dev, "could not read EEPROM\n"); + goto fail; + } + +fail: return (error); +} + +static int +zyd_read_pod(struct zyd_softc *sc) +{ + int error; + uint32_t tmp; + + zyd_read32_m(sc, ZYD_EEPROM_POD, &tmp); + sc->sc_rfrev = tmp & 0x0f; + sc->sc_ledtype = (tmp >> 4) & 0x01; + sc->sc_al2230s = (tmp >> 7) & 0x01; + sc->sc_cckgain = (tmp >> 8) & 0x01; + sc->sc_fix_cr157 = (tmp >> 13) & 0x01; + sc->sc_parev = (tmp >> 16) & 0x0f; + sc->sc_bandedge6 = (tmp >> 21) & 0x01; + sc->sc_newphy = (tmp >> 31) & 0x01; + sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1; +fail: + return (error); +} + +static int +zyd_read_eeprom(struct zyd_softc *sc) +{ + uint16_t val; + int error, i; + + /* read Tx power calibration tables */ + for (i = 0; i < 7; i++) { + zyd_read16_m(sc, ZYD_EEPROM_PWR_CAL + i, &val); + sc->sc_pwrcal[i * 2] = val >> 8; + sc->sc_pwrcal[i * 2 + 1] = val & 0xff; + zyd_read16_m(sc, ZYD_EEPROM_PWR_INT + i, &val); + sc->sc_pwrint[i * 2] = val >> 8; + sc->sc_pwrint[i * 2 + 1] = val & 0xff; + zyd_read16_m(sc, ZYD_EEPROM_36M_CAL + i, &val); + sc->sc_ofdm36_cal[i * 2] = val >> 8; + sc->sc_ofdm36_cal[i * 2 + 1] = val & 0xff; + zyd_read16_m(sc, ZYD_EEPROM_48M_CAL + i, &val); + sc->sc_ofdm48_cal[i * 2] = val >> 8; + sc->sc_ofdm48_cal[i * 2 + 1] = val & 0xff; + zyd_read16_m(sc, ZYD_EEPROM_54M_CAL + i, &val); + sc->sc_ofdm54_cal[i * 2] = val >> 8; + sc->sc_ofdm54_cal[i * 2 + 1] = val & 0xff; + } +fail: + return (error); +} + +static int +zyd_get_macaddr(struct zyd_softc *sc) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = ZYD_READFWDATAREQ; + USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1); + USETW(req.wIndex, 0); + USETW(req.wLength, IEEE80211_ADDR_LEN); + + error = zyd_do_request(sc, &req, sc->sc_ic.ic_macaddr); + if (error != 0) { + device_printf(sc->sc_dev, "could not read EEPROM: %s\n", + usbd_errstr(error)); + } + + return (error); +} + +static int +zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr) +{ + int error; + uint32_t tmp; + + tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; + zyd_write32_m(sc, ZYD_MAC_MACADRL, tmp); + tmp = addr[5] << 8 | addr[4]; + zyd_write32_m(sc, ZYD_MAC_MACADRH, tmp); +fail: + return (error); +} + +static int +zyd_set_bssid(struct zyd_softc *sc, const uint8_t *addr) +{ + int error; + uint32_t tmp; + + tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; + zyd_write32_m(sc, ZYD_MAC_BSSADRL, tmp); + tmp = addr[5] << 8 | addr[4]; + zyd_write32_m(sc, ZYD_MAC_BSSADRH, tmp); +fail: + return (error); +} + +static int +zyd_switch_radio(struct zyd_softc *sc, int on) +{ + struct zyd_rf *rf = &sc->sc_rf; + int error; + + error = zyd_lock_phy(sc); + if (error != 0) + goto fail; + error = (*rf->switch_radio)(rf, on); + if (error != 0) + goto fail; + error = zyd_unlock_phy(sc); +fail: + return (error); +} + +static int +zyd_set_led(struct zyd_softc *sc, int which, int on) +{ + int error; + uint32_t tmp; + + zyd_read32_m(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); + tmp &= ~which; + if (on) + tmp |= which; + zyd_write32_m(sc, ZYD_MAC_TX_PE_CONTROL, tmp); +fail: + return (error); +} + +static void +zyd_set_multi(struct zyd_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t low, high; + int error; + + if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0) + return; + + low = 0x00000000; + high = 0x80000000; + + if (ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_allmulti > 0 || + ic->ic_promisc > 0) { + low = 0xffffffff; + high = 0xffffffff; + } else { + struct ieee80211vap *vap; + struct ifnet *ifp; + struct ifmultiaddr *ifma; + uint8_t v; + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + ifp = vap->iv_ifp; + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + v = ((uint8_t *)LLADDR((struct sockaddr_dl *) + ifma->ifma_addr))[5] >> 2; + if (v < 32) + low |= 1 << v; + else + high |= 1 << (v - 32); + } + if_maddr_runlock(ifp); + } + } + + /* reprogram multicast global hash table */ + zyd_write32_m(sc, ZYD_MAC_GHTBL, low); + zyd_write32_m(sc, ZYD_MAC_GHTBH, high); +fail: + if (error != 0) + device_printf(sc->sc_dev, + "could not set multicast hash table\n"); +} + +static void +zyd_update_mcast(struct ieee80211com *ic) +{ + struct zyd_softc *sc = ic->ic_softc; + + ZYD_LOCK(sc); + zyd_set_multi(sc); + ZYD_UNLOCK(sc); +} + +static int +zyd_set_rxfilter(struct zyd_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t rxfilter; + + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + rxfilter = ZYD_FILTER_BSS; + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_HOSTAP: + rxfilter = ZYD_FILTER_HOSTAP; + break; + case IEEE80211_M_MONITOR: + rxfilter = ZYD_FILTER_MONITOR; + break; + default: + /* should not get there */ + return (EINVAL); + } + return zyd_write32(sc, ZYD_MAC_RXFILTER, rxfilter); +} + +static void +zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c) +{ + int error; + struct ieee80211com *ic = &sc->sc_ic; + struct zyd_rf *rf = &sc->sc_rf; + uint32_t tmp; + int chan; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) { + /* XXX should NEVER happen */ + device_printf(sc->sc_dev, + "%s: invalid channel %x\n", __func__, chan); + return; + } + + error = zyd_lock_phy(sc); + if (error != 0) + goto fail; + + error = (*rf->set_channel)(rf, chan); + if (error != 0) + goto fail; + + if (rf->update_pwr) { + /* update Tx power */ + zyd_write16_m(sc, ZYD_CR31, sc->sc_pwrint[chan - 1]); + + if (sc->sc_macrev == ZYD_ZD1211B) { + zyd_write16_m(sc, ZYD_CR67, + sc->sc_ofdm36_cal[chan - 1]); + zyd_write16_m(sc, ZYD_CR66, + sc->sc_ofdm48_cal[chan - 1]); + zyd_write16_m(sc, ZYD_CR65, + sc->sc_ofdm54_cal[chan - 1]); + zyd_write16_m(sc, ZYD_CR68, sc->sc_pwrcal[chan - 1]); + zyd_write16_m(sc, ZYD_CR69, 0x28); + zyd_write16_m(sc, ZYD_CR69, 0x2a); + } + } + if (sc->sc_cckgain) { + /* set CCK baseband gain from EEPROM */ + if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0) + zyd_write16_m(sc, ZYD_CR47, tmp & 0xff); + } + if (sc->sc_bandedge6 && rf->bandedge6 != NULL) { + error = (*rf->bandedge6)(rf, c); + if (error != 0) + goto fail; + } + zyd_write32_m(sc, ZYD_CR_CONFIG_PHILIPS, 0); + + error = zyd_unlock_phy(sc); + if (error != 0) + goto fail; + + sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = + htole16(c->ic_freq); + sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = + htole16(c->ic_flags); +fail: + return; +} + +static int +zyd_set_beacon_interval(struct zyd_softc *sc, int bintval) +{ + int error; + uint32_t val; + + zyd_read32_m(sc, ZYD_CR_ATIM_WND_PERIOD, &val); + sc->sc_atim_wnd = val; + zyd_read32_m(sc, ZYD_CR_PRE_TBTT, &val); + sc->sc_pre_tbtt = val; + sc->sc_bcn_int = bintval; + + if (sc->sc_bcn_int <= 5) + sc->sc_bcn_int = 5; + if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int) + sc->sc_pre_tbtt = sc->sc_bcn_int - 1; + if (sc->sc_atim_wnd >= sc->sc_pre_tbtt) + sc->sc_atim_wnd = sc->sc_pre_tbtt - 1; + + zyd_write32_m(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd); + zyd_write32_m(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt); + zyd_write32_m(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int); +fail: + return (error); +} + +static void +zyd_rx_data(struct usb_xfer *xfer, int offset, uint16_t len) +{ + struct zyd_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct zyd_plcphdr plcp; + struct zyd_rx_stat stat; + struct usb_page_cache *pc; + struct mbuf *m; + int rlen, rssi; + + if (len < ZYD_MIN_FRAGSZ) { + DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n", + device_get_nameunit(sc->sc_dev), len); + counter_u64_add(ic->ic_ierrors, 1); + return; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, offset, &plcp, sizeof(plcp)); + usbd_copy_out(pc, offset + len - sizeof(stat), &stat, sizeof(stat)); + + if (stat.flags & ZYD_RX_ERROR) { + DPRINTF(sc, ZYD_DEBUG_RECV, + "%s: RX status indicated error (%x)\n", + device_get_nameunit(sc->sc_dev), stat.flags); + counter_u64_add(ic->ic_ierrors, 1); + return; + } + + /* compute actual frame length */ + rlen = len - sizeof(struct zyd_plcphdr) - + sizeof(struct zyd_rx_stat) - IEEE80211_CRC_LEN; + + /* allocate a mbuf to store the frame */ + if (rlen > (int)MCLBYTES) { + DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too long (length=%d)\n", + device_get_nameunit(sc->sc_dev), rlen); + counter_u64_add(ic->ic_ierrors, 1); + return; + } else if (rlen > (int)MHLEN) + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + else + m = m_gethdr(M_NOWAIT, MT_DATA); + if (m == NULL) { + DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n", + device_get_nameunit(sc->sc_dev)); + counter_u64_add(ic->ic_ierrors, 1); + return; + } + m->m_pkthdr.len = m->m_len = rlen; + usbd_copy_out(pc, offset + sizeof(plcp), mtod(m, uint8_t *), rlen); + + if (ieee80211_radiotap_active(ic)) { + struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + if (stat.flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32)) + tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; + /* XXX toss, no way to express errors */ + if (stat.flags & ZYD_RX_DECRYPTERR) + tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; + tap->wr_rate = ieee80211_plcp2rate(plcp.signal, + (stat.flags & ZYD_RX_OFDM) ? + IEEE80211_T_OFDM : IEEE80211_T_CCK); + tap->wr_antsignal = stat.rssi + -95; + tap->wr_antnoise = -95; /* XXX */ + } + rssi = (stat.rssi > 63) ? 127 : 2 * stat.rssi; + + sc->sc_rx_data[sc->sc_rx_count].rssi = rssi; + sc->sc_rx_data[sc->sc_rx_count].m = m; + sc->sc_rx_count++; +} + +static void +zyd_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct zyd_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct zyd_rx_desc desc; + struct mbuf *m; + struct usb_page_cache *pc; + uint32_t offset; + uint8_t rssi; + int8_t nf; + int i; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + sc->sc_rx_count = 0; + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, actlen - sizeof(desc), &desc, sizeof(desc)); + + offset = 0; + if (UGETW(desc.tag) == ZYD_TAG_MULTIFRAME) { + DPRINTF(sc, ZYD_DEBUG_RECV, + "%s: received multi-frame transfer\n", __func__); + + for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) { + uint16_t len16 = UGETW(desc.len[i]); + + if (len16 == 0 || len16 > actlen) + break; + + zyd_rx_data(xfer, offset, len16); + + /* next frame is aligned on a 32-bit boundary */ + len16 = (len16 + 3) & ~3; + offset += len16; + if (len16 > actlen) + break; + actlen -= len16; + } + } else { + DPRINTF(sc, ZYD_DEBUG_RECV, + "%s: received single-frame transfer\n", __func__); + + zyd_rx_data(xfer, 0, actlen); + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "ieee80211_input" here, and not some lines up! + */ + ZYD_UNLOCK(sc); + for (i = 0; i < sc->sc_rx_count; i++) { + rssi = sc->sc_rx_data[i].rssi; + m = sc->sc_rx_data[i].m; + sc->sc_rx_data[i].m = NULL; + + nf = -95; /* XXX */ + + ni = ieee80211_find_rxnode(ic, + mtod(m, struct ieee80211_frame_min *)); + if (ni != NULL) { + (void)ieee80211_input(ni, m, rssi, nf); + ieee80211_free_node(ni); + } else + (void)ieee80211_input_all(ic, m, rssi, nf); + } + ZYD_LOCK(sc); + zyd_start(sc); + break; + + default: /* Error */ + DPRINTF(sc, ZYD_DEBUG_ANY, "frame error: %s\n", usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static uint8_t +zyd_plcp_signal(struct zyd_softc *sc, int rate) +{ + switch (rate) { + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: + return (0xb); + case 18: + return (0xf); + case 24: + return (0xa); + case 36: + return (0xe); + case 48: + return (0x9); + case 72: + return (0xd); + case 96: + return (0x8); + case 108: + return (0xc); + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: + return (0x0); + case 4: + return (0x1); + case 11: + return (0x2); + case 22: + return (0x3); + } + + device_printf(sc->sc_dev, "unsupported rate %d\n", rate); + return (0x0); +} + +static void +zyd_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct zyd_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211vap *vap; + struct zyd_tx_data *data; + struct mbuf *m; + struct usb_page_cache *pc; + int actlen; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTF(sc, ZYD_DEBUG_ANY, "transfer complete, %u bytes\n", + actlen); + + /* free resources */ + data = usbd_xfer_get_priv(xfer); + zyd_tx_free(data, 0); + usbd_xfer_set_priv(xfer, NULL); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->tx_q); + if (data) { + STAILQ_REMOVE_HEAD(&sc->tx_q, next); + m = data->m; + + if (m->m_pkthdr.len > (int)ZYD_MAX_TXBUFSZ) { + DPRINTF(sc, ZYD_DEBUG_ANY, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = ZYD_MAX_TXBUFSZ; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &data->desc, ZYD_TX_DESC_SIZE); + usbd_m_copy_in(pc, ZYD_TX_DESC_SIZE, m, 0, + m->m_pkthdr.len); + + vap = data->ni->ni_vap; + if (ieee80211_radiotap_active_vap(vap)) { + struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = data->rate; + + ieee80211_radiotap_tx(vap, m); + } + + usbd_xfer_set_frame_len(xfer, 0, ZYD_TX_DESC_SIZE + m->m_pkthdr.len); + usbd_xfer_set_priv(xfer, data); + usbd_transfer_submit(xfer); + } + zyd_start(sc); + break; + + default: /* Error */ + DPRINTF(sc, ZYD_DEBUG_ANY, "transfer error, %s\n", + usbd_errstr(error)); + + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + data = usbd_xfer_get_priv(xfer); + usbd_xfer_set_priv(xfer, NULL); + if (data != NULL) + zyd_tx_free(data, error); + + if (error != USB_ERR_CANCELLED) { + if (error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + + /* + * Try to clear stall first, also if other + * errors occur, hence clearing stall + * introduces a 50 ms delay: + */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static int +zyd_tx_start(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct zyd_tx_desc *desc; + struct zyd_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k; + int rate, totlen; + static const uint8_t ratediv[] = ZYD_TX_RATEDIV; + uint8_t phy; + uint16_t pktlen; + uint32_t bits; + + wh = mtod(m0, struct ieee80211_frame *); + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT || + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) { + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + rate = tp->mgmtrate; + } else { + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + /* for data frames */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else { + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = ni->ni_txrate; + } + } + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + return (ENOBUFS); + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + data->ni = ni; + data->m = m0; + data->rate = rate; + + /* fill Tx descriptor */ + desc = &data->desc; + phy = zyd_plcp_signal(sc, rate); + desc->phy = phy; + if (ZYD_RATE_IS_OFDM(rate)) { + desc->phy |= ZYD_TX_PHY_OFDM; + if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) + desc->phy |= ZYD_TX_PHY_5GHZ; + } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->phy |= ZYD_TX_PHY_SHPREAMBLE; + + totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; + desc->len = htole16(totlen); + + desc->flags = ZYD_TX_FLAG_BACKOFF; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* multicast frames are not sent at OFDM rates in 802.11b/g */ + if (totlen > vap->iv_rtsthreshold) { + desc->flags |= ZYD_TX_FLAG_RTS; + } else if (ZYD_RATE_IS_OFDM(rate) && + (ic->ic_flags & IEEE80211_F_USEPROT)) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + desc->flags |= ZYD_TX_FLAG_RTS; + } + } else + desc->flags |= ZYD_TX_FLAG_MULTICAST; + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) + desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); + + /* actual transmit length (XXX why +10?) */ + pktlen = ZYD_TX_DESC_SIZE + 10; + if (sc->sc_macrev == ZYD_ZD1211) + pktlen += totlen; + desc->pktlen = htole16(pktlen); + + bits = (rate == 11) ? (totlen * 16) + 10 : + ((rate == 22) ? (totlen * 8) + 10 : (totlen * 8)); + desc->plcp_length = htole16(bits / ratediv[phy]); + desc->plcp_service = 0; + if (rate == 22 && (bits % 11) > 0 && (bits % 11) <= 3) + desc->plcp_service |= ZYD_PLCP_LENGEXT; + desc->nextlen = 0; + + if (ieee80211_radiotap_active_vap(vap)) { + struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = rate; + + ieee80211_radiotap_tx(vap, m0); + } + + DPRINTF(sc, ZYD_DEBUG_XMIT, + "%s: sending data frame len=%zu rate=%u\n", + device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len, + rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[ZYD_BULK_WR]); + + return (0); +} + +static int +zyd_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct zyd_softc *sc = ic->ic_softc; + int error; + + ZYD_LOCK(sc); + if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0) { + ZYD_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + ZYD_UNLOCK(sc); + return (error); + } + zyd_start(sc); + ZYD_UNLOCK(sc); + + return (0); +} + +static void +zyd_start(struct zyd_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + ZYD_LOCK_ASSERT(sc, MA_OWNED); + + while (sc->tx_nfree > 0 && (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (zyd_tx_start(sc, m, ni) != 0) { + ieee80211_free_node(ni); + m_freem(m); + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + break; + } + } +} + +static int +zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct zyd_softc *sc = ic->ic_softc; + + ZYD_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!(sc->sc_flags & ZYD_FLAG_RUNNING)) { + ZYD_UNLOCK(sc); + m_freem(m); + return (ENETDOWN); + } + if (sc->tx_nfree == 0) { + ZYD_UNLOCK(sc); + m_freem(m); + return (ENOBUFS); /* XXX */ + } + + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + * XXX raw path + */ + if (zyd_tx_start(sc, m, ni) != 0) { + ZYD_UNLOCK(sc); + m_freem(m); + return (EIO); + } + ZYD_UNLOCK(sc); + return (0); +} + +static void +zyd_parent(struct ieee80211com *ic) +{ + struct zyd_softc *sc = ic->ic_softc; + int startall = 0; + + ZYD_LOCK(sc); + if (sc->sc_flags & ZYD_FLAG_DETACHED) { + ZYD_UNLOCK(sc); + return; + } + if (ic->ic_nrunning > 0) { + if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0) { + zyd_init_locked(sc); + startall = 1; + } else + zyd_set_multi(sc); + } else if (sc->sc_flags & ZYD_FLAG_RUNNING) + zyd_stop(sc); + ZYD_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static void +zyd_init_locked(struct zyd_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct usb_config_descriptor *cd; + int error; + uint32_t val; + + ZYD_LOCK_ASSERT(sc, MA_OWNED); + + if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) { + error = zyd_loadfirmware(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load firmware (error=%d)\n", error); + goto fail; + } + + /* reset device */ + cd = usbd_get_config_descriptor(sc->sc_udev); + error = usbd_req_set_config(sc->sc_udev, &sc->sc_mtx, + cd->bConfigurationValue); + if (error) + device_printf(sc->sc_dev, "reset failed, continuing\n"); + + error = zyd_hw_init(sc); + if (error) { + device_printf(sc->sc_dev, + "hardware initialization failed\n"); + goto fail; + } + + device_printf(sc->sc_dev, + "HMAC ZD1211%s, FW %02x.%02x, RF %s S%x, PA%x LED %x " + "BE%x NP%x Gain%x F%x\n", + (sc->sc_macrev == ZYD_ZD1211) ? "": "B", + sc->sc_fwrev >> 8, sc->sc_fwrev & 0xff, + zyd_rf_name(sc->sc_rfrev), sc->sc_al2230s, sc->sc_parev, + sc->sc_ledtype, sc->sc_bandedge6, sc->sc_newphy, + sc->sc_cckgain, sc->sc_fix_cr157); + + /* read regulatory domain (currently unused) */ + zyd_read32_m(sc, ZYD_EEPROM_SUBID, &val); + sc->sc_regdomain = val >> 16; + DPRINTF(sc, ZYD_DEBUG_INIT, "regulatory domain %x\n", + sc->sc_regdomain); + + /* we'll do software WEP decryption for now */ + DPRINTF(sc, ZYD_DEBUG_INIT, "%s: setting encryption type\n", + __func__); + zyd_write32_m(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); + + sc->sc_flags |= ZYD_FLAG_INITONCE; + } + + if (sc->sc_flags & ZYD_FLAG_RUNNING) + zyd_stop(sc); + + DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %6D\n", + vap ? vap->iv_myaddr : ic->ic_macaddr, ":"); + error = zyd_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + if (error != 0) + return; + + /* set basic rates */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x0003); + else if (ic->ic_curmode == IEEE80211_MODE_11A) + zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x1500); + else /* assumes 802.11b/g */ + zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0xff0f); + + /* promiscuous mode */ + zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0); + /* multicast setup */ + zyd_set_multi(sc); + /* set RX filter */ + error = zyd_set_rxfilter(sc); + if (error != 0) + goto fail; + + /* switch radio transmitter ON */ + error = zyd_switch_radio(sc, 1); + if (error != 0) + goto fail; + /* set default BSS channel */ + zyd_set_chan(sc, ic->ic_curchan); + + /* + * Allocate Tx and Rx xfer queues. + */ + zyd_setup_tx_list(sc); + + /* enable interrupts */ + zyd_write32_m(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); + + sc->sc_flags |= ZYD_FLAG_RUNNING; + usbd_xfer_set_stall(sc->sc_xfer[ZYD_BULK_WR]); + usbd_transfer_start(sc->sc_xfer[ZYD_BULK_RD]); + usbd_transfer_start(sc->sc_xfer[ZYD_INTR_RD]); + + return; + +fail: zyd_stop(sc); + return; +} + +static void +zyd_stop(struct zyd_softc *sc) +{ + int error; + + ZYD_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_flags &= ~ZYD_FLAG_RUNNING; + zyd_drain_mbufq(sc); + + /* + * Drain all the transfers, if not already drained: + */ + ZYD_UNLOCK(sc); + usbd_transfer_drain(sc->sc_xfer[ZYD_BULK_WR]); + usbd_transfer_drain(sc->sc_xfer[ZYD_BULK_RD]); + ZYD_LOCK(sc); + + zyd_unsetup_tx_list(sc); + + /* Stop now if the device was never set up */ + if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) + return; + + /* switch radio transmitter OFF */ + error = zyd_switch_radio(sc, 0); + if (error != 0) + goto fail; + /* disable Rx */ + zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0); + /* disable interrupts */ + zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); + +fail: + return; +} + +static int +zyd_loadfirmware(struct zyd_softc *sc) +{ + struct usb_device_request req; + size_t size; + u_char *fw; + uint8_t stat; + uint16_t addr; + + if (sc->sc_flags & ZYD_FLAG_FWLOADED) + return (0); + + if (sc->sc_macrev == ZYD_ZD1211) { + fw = (u_char *)zd1211_firmware; + size = sizeof(zd1211_firmware); + } else { + fw = (u_char *)zd1211b_firmware; + size = sizeof(zd1211b_firmware); + } + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = ZYD_DOWNLOADREQ; + USETW(req.wIndex, 0); + + addr = ZYD_FIRMWARE_START_ADDR; + while (size > 0) { + /* + * When the transfer size is 4096 bytes, it is not + * likely to be able to transfer it. + * The cause is port or machine or chip? + */ + const int mlen = min(size, 64); + + DPRINTF(sc, ZYD_DEBUG_FW, + "loading firmware block: len=%d, addr=0x%x\n", mlen, addr); + + USETW(req.wValue, addr); + USETW(req.wLength, mlen); + if (zyd_do_request(sc, &req, fw) != 0) + return (EIO); + + addr += mlen / 2; + fw += mlen; + size -= mlen; + } + + /* check whether the upload succeeded */ + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = ZYD_DOWNLOADSTS; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(stat)); + if (zyd_do_request(sc, &req, &stat) != 0) + return (EIO); + + sc->sc_flags |= ZYD_FLAG_FWLOADED; + + return (stat & 0x80) ? (EIO) : (0); +} + +static void +zyd_scan_start(struct ieee80211com *ic) +{ + struct zyd_softc *sc = ic->ic_softc; + + ZYD_LOCK(sc); + /* want broadcast address while scanning */ + zyd_set_bssid(sc, ieee80211broadcastaddr); + ZYD_UNLOCK(sc); +} + +static void +zyd_scan_end(struct ieee80211com *ic) +{ + struct zyd_softc *sc = ic->ic_softc; + + ZYD_LOCK(sc); + /* restore previous bssid */ + zyd_set_bssid(sc, sc->sc_bssid); + ZYD_UNLOCK(sc); +} + +static void +zyd_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + zyd_chan_2ghz, nitems(zyd_chan_2ghz), bands, 0); +} + +static void +zyd_set_channel(struct ieee80211com *ic) +{ + struct zyd_softc *sc = ic->ic_softc; + + ZYD_LOCK(sc); + zyd_set_chan(sc, ic->ic_curchan); + ZYD_UNLOCK(sc); +} + +static device_method_t zyd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, zyd_match), + DEVMETHOD(device_attach, zyd_attach), + DEVMETHOD(device_detach, zyd_detach), + DEVMETHOD_END +}; + +static driver_t zyd_driver = { + .name = "zyd", + .methods = zyd_methods, + .size = sizeof(struct zyd_softc) +}; + +static devclass_t zyd_devclass; + +DRIVER_MODULE(zyd, uhub, zyd_driver, zyd_devclass, NULL, 0); +MODULE_DEPEND(zyd, usb, 1, 1, 1); +MODULE_DEPEND(zyd, wlan, 1, 1, 1); +MODULE_VERSION(zyd, 1); +USB_PNP_HOST_INFO(zyd_devs); diff --git a/freebsd/sys/dev/usb/wlan/if_zydfw.h b/freebsd/sys/dev/usb/wlan/if_zydfw.h new file mode 100644 index 00000000..46f5c2ab --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_zydfw.h @@ -0,0 +1,1144 @@ +/* + * Copyright (C) 2001, 2002, 2003,2004 ZyDAS Technology Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that the following conditions are met: + * 1. Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +uint8_t zd1211_firmware[] = { + 0x08, 0x91, 0xFF, 0xED, 0x09, 0x93, 0x1E, 0xEE, + 0xD1, 0x94, 0x11, 0xEE, 0x88, 0xD4, 0xD1, 0x96, + 0xD1, 0x98, 0x5C, 0x99, 0x5C, 0x99, 0x4C, 0x99, + 0x04, 0x9D, 0xD1, 0x98, 0xD1, 0x9A, 0x03, 0xEE, + 0xF4, 0x94, 0xD3, 0xD4, 0x41, 0x2A, 0x40, 0x4A, + 0x45, 0xBE, 0x88, 0x92, 0x41, 0x24, 0x40, 0x44, + 0x53, 0xBE, 0x40, 0xF0, 0x93, 0xEE, 0x41, 0xEE, + 0x98, 0x9A, 0xD4, 0xF7, 0x02, 0x00, 0x1F, 0xEC, + 0x00, 0x00, 0xB2, 0xF8, 0x4D, 0x00, 0xA1, 0xEC, + 0x00, 0x00, 0xA6, 0xF7, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xD8, + 0xA0, 0x90, 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, + 0x40, 0xF0, 0xB4, 0xF0, 0xA0, 0x90, 0x98, 0x9A, + 0xA0, 0xD8, 0x40, 0xF0, 0x64, 0xEF, 0xA0, 0x90, + 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, 0xF6, 0xF0, + 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, + 0xF7, 0xF6, 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, + 0x40, 0xF0, 0xF8, 0xF5, 0xA0, 0x90, 0x98, 0x9A, + 0xA0, 0xD8, 0x40, 0xF0, 0xF1, 0xF0, 0xA0, 0x90, + 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, + 0x97, 0xF7, 0xA0, 0x90, 0x98, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, + 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x42, 0x02, + 0xC1, 0x92, 0x03, 0x96, 0x1B, 0xD7, 0x2A, 0x86, + 0x1A, 0xD5, 0x2B, 0x86, 0x09, 0xA3, 0x00, 0x80, + 0x19, 0xD3, 0x2C, 0x86, 0x00, 0xEE, 0x0A, 0x65, + 0xC0, 0x7A, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, + 0xFE, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00, + 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, 0xC5, 0xD4, + 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x01, 0xD4, + 0x42, 0x02, 0xC1, 0x96, 0x0A, 0x65, 0xC0, 0x7A, + 0x02, 0x99, 0xC4, 0x92, 0x41, 0xA2, 0xC4, 0xD2, + 0xC5, 0x98, 0x1C, 0xD9, 0x2A, 0x86, 0x01, 0x98, + 0x1C, 0xD9, 0x2B, 0x86, 0x1B, 0xD7, 0x2C, 0x86, + 0x00, 0xEE, 0x09, 0xB3, 0xFE, 0xFF, 0xC2, 0xD2, + 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x41, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, + 0xE5, 0xEE, 0x11, 0x93, 0xD8, 0xF7, 0x41, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0xAE, 0xEE, 0x40, 0xF1, + 0x40, 0x92, 0x19, 0xD3, 0xD8, 0xF7, 0xC5, 0x92, + 0x41, 0x92, 0x19, 0xD3, 0x00, 0x83, 0x40, 0x92, + 0x19, 0xD3, 0x00, 0x83, 0x0F, 0x9F, 0x95, 0xF8, + 0x0F, 0x9F, 0x99, 0xEE, 0x42, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0x99, 0xEE, 0x40, 0x92, 0x19, 0xD3, + 0xD8, 0xF7, 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, + 0x91, 0xEC, 0x40, 0xF0, 0x5F, 0xF2, 0x09, 0x63, + 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0F, 0x9F, + 0x99, 0xEE, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92, + 0x19, 0xD3, 0x12, 0x95, 0x19, 0xD3, 0x10, 0x95, + 0x19, 0xD3, 0x02, 0x80, 0x19, 0xD3, 0x03, 0x82, + 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, 0x91, 0xEC, + 0x40, 0xF0, 0x5F, 0xF2, 0x40, 0xF0, 0xDE, 0xF3, + 0x11, 0x93, 0x04, 0xEC, 0x42, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0xE3, 0xEE, 0x40, 0x92, 0x19, 0xD3, + 0x04, 0xEC, 0x40, 0xF0, 0x38, 0xF2, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, + 0x11, 0x93, 0x44, 0x96, 0x09, 0xB3, 0xFF, 0xFD, + 0x19, 0xD3, 0x44, 0x96, 0x40, 0xF0, 0x90, 0xF7, + 0x6E, 0x92, 0x19, 0xD3, 0x05, 0x84, 0x40, 0xF0, + 0xC4, 0xEE, 0x4B, 0x62, 0x0A, 0x95, 0x2E, 0xEE, + 0xD1, 0xD4, 0x0B, 0x97, 0x2B, 0xEE, 0xD1, 0xD6, + 0x0A, 0x95, 0x00, 0xEE, 0xD1, 0xD4, 0x0B, 0x97, + 0x2F, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x34, 0xEE, + 0xD1, 0xD4, 0x0B, 0x97, 0x39, 0xEE, 0xD1, 0xD6, + 0x0A, 0x95, 0x3E, 0xEE, 0xD1, 0xD4, 0x0B, 0x97, + 0x43, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x48, 0xEE, + 0xD1, 0xD4, 0x0B, 0x97, 0x4D, 0xEE, 0xD1, 0xD6, + 0x0A, 0x95, 0x4E, 0xEE, 0xC1, 0xD4, 0x0A, 0x65, + 0x00, 0x44, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, + 0xC2, 0xD2, 0x43, 0xF1, 0x09, 0x93, 0x01, 0x3F, + 0x19, 0xD3, 0xC0, 0x85, 0x11, 0x93, 0x44, 0x96, + 0x09, 0xB3, 0xFF, 0xFC, 0x19, 0xD3, 0x44, 0x96, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, 0x03, 0x96, + 0x41, 0x02, 0x03, 0x99, 0xC4, 0x94, 0x42, 0x04, + 0xC1, 0x04, 0xC2, 0x94, 0xC3, 0xD4, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, + 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0x13, 0x97, + 0x95, 0xEC, 0x1B, 0xD7, 0x02, 0x80, 0x11, 0x93, + 0x99, 0xEC, 0x19, 0xD3, 0x7C, 0x96, 0x0B, 0x97, + 0xA0, 0x00, 0x1B, 0xD7, 0x6E, 0xEC, 0x0A, 0x65, + 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, + 0xFF, 0xBF, 0x11, 0xA3, 0x9A, 0xEC, 0xC2, 0xD2, + 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, + 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, + 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, + 0x14, 0x99, 0x03, 0x80, 0x0C, 0xB3, 0x00, 0x10, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x97, 0xF0, + 0x11, 0x93, 0x9F, 0xEC, 0x41, 0x02, 0x19, 0xD3, + 0x9F, 0xEC, 0x11, 0x93, 0xD6, 0xF7, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x84, 0xEF, 0x0A, 0x65, + 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0x00, 0x04, 0xC2, 0xD2, 0x0F, 0x9F, 0xB1, 0xF0, + 0x11, 0x93, 0x94, 0xEC, 0x02, 0xD2, 0x40, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0xD0, 0xEF, 0x41, 0x92, + 0x19, 0xD3, 0x94, 0xEC, 0x19, 0xD3, 0x9F, 0xEC, + 0x12, 0x95, 0x02, 0x80, 0x1A, 0xD5, 0x95, 0xEC, + 0x13, 0x97, 0x7C, 0x96, 0x1B, 0xD7, 0x99, 0xEC, + 0x0A, 0x65, 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0x00, 0x40, 0x19, 0xD3, 0x9A, 0xEC, + 0x09, 0x63, 0x00, 0x40, 0xC2, 0xD2, 0x02, 0x94, + 0x1A, 0xD5, 0x7C, 0x96, 0x0C, 0xB3, 0x00, 0x08, + 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xB0, 0xEF, + 0x0C, 0xB3, 0xFF, 0x07, 0x0F, 0x9F, 0xB4, 0xEF, + 0x11, 0x93, 0x06, 0x80, 0x09, 0xB3, 0xFF, 0x07, + 0x09, 0x03, 0x00, 0xA0, 0x19, 0xD3, 0x97, 0xEC, + 0x40, 0x98, 0x0B, 0x97, 0x9C, 0xEC, 0x04, 0x95, + 0x03, 0x05, 0x14, 0x03, 0x97, 0xEC, 0x46, 0x02, + 0xC1, 0x92, 0xC2, 0xD2, 0x41, 0x08, 0x42, 0x48, + 0x02, 0x9E, 0x0F, 0x9F, 0xBB, 0xEF, 0x11, 0x93, + 0x97, 0xEC, 0xC1, 0x92, 0xC5, 0xD2, 0x5F, 0xB2, + 0x19, 0xD3, 0x9B, 0xEC, 0x0F, 0x9F, 0xD3, 0xEF, + 0x13, 0x97, 0x98, 0xEC, 0xC5, 0xD6, 0x11, 0x93, + 0x03, 0x80, 0x09, 0xB3, 0x00, 0x08, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xE9, 0xEF, 0x11, 0x93, + 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, + 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x10, + 0x19, 0xD3, 0xDB, 0xF7, 0x40, 0x98, 0x1C, 0xD9, + 0x9B, 0xEC, 0x12, 0x95, 0x9B, 0xEC, 0x40, 0x44, + 0x02, 0x4E, 0x0F, 0x9F, 0x86, 0xF0, 0x0A, 0xB3, + 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x07, 0xF0, 0x0A, 0xB3, 0x07, 0x00, 0x09, 0x05, + 0xA9, 0xEC, 0xC2, 0x94, 0x01, 0xD4, 0x09, 0x03, + 0xA1, 0xEC, 0xC1, 0x92, 0x19, 0xD3, 0x9B, 0xEC, + 0xC5, 0x94, 0x0A, 0xB5, 0x00, 0xFF, 0x01, 0xA5, + 0xC5, 0xD4, 0x0F, 0x9F, 0x13, 0xF0, 0x0A, 0x05, + 0xFF, 0xFF, 0x0A, 0x03, 0xB1, 0xEC, 0xC1, 0x92, + 0x01, 0xD2, 0x1A, 0xD5, 0x9B, 0xEC, 0xC5, 0x96, + 0x0B, 0x07, 0xFF, 0xFF, 0xC5, 0xD6, 0x11, 0x93, + 0x97, 0xEC, 0xC5, 0x98, 0xC1, 0xD8, 0x11, 0x93, + 0x97, 0xEC, 0x09, 0x05, 0x0B, 0x00, 0x03, 0xD4, + 0xC2, 0x96, 0x06, 0xD6, 0x7B, 0x95, 0x7A, 0x95, + 0x4C, 0x02, 0xC1, 0x92, 0x59, 0x93, 0x59, 0x93, + 0x01, 0xA5, 0x01, 0x98, 0x0C, 0xF5, 0x7B, 0x93, + 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xB3, + 0xFF, 0x00, 0x04, 0xD2, 0x5C, 0x93, 0x59, 0x93, + 0x04, 0x94, 0x01, 0xA5, 0x03, 0x96, 0xC3, 0xD4, + 0x11, 0x93, 0x97, 0xEC, 0x4C, 0x02, 0x05, 0xD2, + 0xC1, 0x92, 0x09, 0xB3, 0x00, 0xFF, 0x7C, 0x95, + 0x7A, 0x95, 0x02, 0xA3, 0x05, 0x98, 0xC4, 0xD2, + 0x12, 0x95, 0x97, 0xEC, 0x45, 0x04, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2, + 0x12, 0x95, 0x9B, 0xEC, 0x0A, 0xB3, 0x08, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5B, 0xF0, + 0x12, 0x95, 0x97, 0xEC, 0x4A, 0x04, 0x02, 0x99, + 0xC4, 0x92, 0x01, 0x98, 0x0C, 0xF3, 0x7B, 0x93, + 0x41, 0x02, 0x0F, 0x9F, 0x7C, 0xF0, 0x43, 0x44, + 0x02, 0x8E, 0x0F, 0x9F, 0x7D, 0xF0, 0x11, 0x93, + 0x97, 0xEC, 0x42, 0x02, 0x0A, 0x05, 0xFF, 0xFF, + 0xC1, 0xD4, 0x11, 0x93, 0x97, 0xEC, 0x4A, 0x02, + 0x12, 0x95, 0x60, 0x96, 0xC1, 0xD4, 0x12, 0x95, + 0x97, 0xEC, 0x4B, 0x04, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0x1F, 0xFF, 0xC2, 0xD2, 0x12, 0x95, + 0x97, 0xEC, 0x4B, 0x04, 0x11, 0x93, 0x62, 0x96, + 0x41, 0x93, 0x59, 0x93, 0x02, 0x99, 0xC4, 0xA2, + 0xC2, 0xD2, 0xC5, 0x92, 0x19, 0xD3, 0x98, 0xEC, + 0x0A, 0x95, 0x0C, 0x02, 0x1A, 0xD5, 0x02, 0x80, + 0x0F, 0x9F, 0xB1, 0xF0, 0x09, 0x63, 0xFE, 0x7F, + 0x01, 0x97, 0xC3, 0x94, 0x0A, 0xA5, 0x00, 0x04, + 0xC1, 0xD4, 0x11, 0x93, 0x9F, 0xEC, 0x09, 0xA3, + 0x00, 0x01, 0x19, 0xD3, 0x9F, 0xEC, 0x40, 0xF0, + 0x39, 0xEF, 0x0F, 0x9F, 0xB1, 0xF0, 0x11, 0x93, + 0x94, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0xA6, 0xF0, 0x40, 0xF0, 0x39, 0xEF, 0x11, 0x93, + 0x95, 0xEC, 0x44, 0xB2, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xB1, 0xF0, 0x48, 0x98, 0x1C, 0xD9, + 0x02, 0x80, 0x11, 0x93, 0x91, 0xEC, 0x41, 0x22, + 0x0A, 0x95, 0xB1, 0xF0, 0x88, 0xD4, 0x88, 0xDC, + 0x91, 0x9A, 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, + 0x04, 0x82, 0x48, 0xB2, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xC8, 0xF0, 0x0A, 0x65, 0xFD, 0x7D, + 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFF, 0xFE, + 0xC2, 0xD2, 0x41, 0x92, 0x19, 0xD3, 0xBF, 0xEC, + 0x11, 0x93, 0x04, 0x82, 0x43, 0xB2, 0x12, 0x95, + 0x03, 0x82, 0x02, 0xB3, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xEF, 0xF0, 0x0A, 0xB3, 0x00, 0xFF, + 0x48, 0xA2, 0x19, 0xD3, 0x03, 0x82, 0x40, 0xF0, + 0xEB, 0xF3, 0x11, 0x93, 0xBF, 0xEC, 0x41, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, + 0x07, 0x82, 0x11, 0x43, 0x03, 0xEC, 0x02, 0x0E, + 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, 0x03, 0x82, + 0x09, 0xA3, 0x00, 0x01, 0x19, 0xD3, 0x03, 0x82, + 0x40, 0x96, 0x1B, 0xD7, 0xBF, 0xEC, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x11, 0x93, 0x20, 0xBC, 0xC8, 0xD2, + 0x40, 0xF0, 0x48, 0xF1, 0x41, 0x00, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, + 0x41, 0x02, 0xC1, 0x92, 0x01, 0x97, 0xC3, 0x96, + 0xC2, 0xD6, 0x0A, 0x45, 0x00, 0x95, 0x02, 0x5E, + 0x0F, 0x9F, 0x45, 0xF1, 0xC1, 0x92, 0x41, 0xB2, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x45, 0xF1, + 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0x45, 0xF1, 0x41, 0x98, 0x1C, 0xD9, + 0xC0, 0xEC, 0x12, 0x95, 0x02, 0x80, 0x01, 0xD4, + 0x40, 0xF0, 0x56, 0xF2, 0x0B, 0x67, 0xFD, 0x7D, + 0x03, 0x99, 0xC4, 0x92, 0x0C, 0x99, 0x96, 0x03, + 0x1C, 0xD9, 0x06, 0x82, 0x41, 0x98, 0x1C, 0xD9, + 0x02, 0x82, 0x42, 0x98, 0x1C, 0xD9, 0x05, 0x82, + 0x0C, 0x69, 0x80, 0x7F, 0x1C, 0xD9, 0x00, 0xB0, + 0x09, 0xA3, 0x00, 0x01, 0xC3, 0xD2, 0x01, 0x94, + 0x0A, 0xB3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0x43, 0xF1, 0x42, 0xA4, 0x1A, 0xD5, + 0x02, 0x80, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00, + 0x05, 0x92, 0xC5, 0xD2, 0x60, 0xB2, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x55, 0xF1, 0x40, 0xF0, + 0x35, 0xF7, 0xC5, 0x94, 0x0A, 0xB3, 0x10, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5E, 0xF1, + 0x40, 0xF0, 0x23, 0xF6, 0xC5, 0x96, 0x0B, 0xB3, + 0x40, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x67, 0xF1, 0x40, 0xF0, 0x5D, 0xF5, 0xC5, 0x94, + 0x0A, 0xB3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xC8, 0xF1, 0x13, 0x97, 0x21, 0xBC, + 0x01, 0xD6, 0x0B, 0xB3, 0x02, 0x00, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x79, 0xF1, 0x40, 0xF0, + 0x62, 0xFB, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x82, 0xF1, + 0x40, 0xF0, 0x6C, 0xFB, 0x01, 0x96, 0x0B, 0xB3, + 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xA2, 0xF1, 0x40, 0xF0, 0xB0, 0xFA, 0x41, 0x92, + 0x19, 0xD3, 0xD5, 0xF7, 0x11, 0x93, 0x03, 0xEC, + 0x09, 0x43, 0x40, 0x00, 0x02, 0x5E, 0x0F, 0x9F, + 0x98, 0xF1, 0x40, 0x94, 0x1A, 0xD5, 0xD5, 0xF7, + 0x11, 0x93, 0x00, 0xEC, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xAB, 0xF1, 0x40, 0xF0, 0x38, 0xF2, + 0x0F, 0x9F, 0xAB, 0xF1, 0x01, 0x96, 0x0B, 0xB3, + 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xAB, 0xF1, 0x40, 0xF0, 0x7C, 0xFB, 0x01, 0x94, + 0x0A, 0xB3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0xB4, 0xF1, 0x40, 0xF0, 0x87, 0xFB, + 0x11, 0x93, 0x10, 0xEC, 0x42, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0xBF, 0xF1, 0x44, 0x96, 0x1B, 0xD7, + 0x0B, 0xBC, 0x0F, 0x9F, 0xC5, 0xF1, 0x41, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0xC5, 0xF1, 0x19, 0xD3, + 0x0B, 0xBC, 0x40, 0x92, 0x19, 0xD3, 0x10, 0xEC, + 0xC5, 0x94, 0x0A, 0xB3, 0x80, 0x00, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, 0x13, 0x97, + 0x28, 0xBC, 0x01, 0xD6, 0x0B, 0xB3, 0x40, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xDA, 0xF1, + 0x40, 0xF0, 0x18, 0xF7, 0x01, 0x94, 0x0A, 0xB3, + 0x02, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xED, 0xF1, 0x40, 0xF0, 0xC4, 0xEE, 0x40, 0xF0, + 0x8F, 0xFB, 0x40, 0xF0, 0x1B, 0xF2, 0x40, 0x96, + 0x1B, 0xD7, 0x00, 0xEC, 0x41, 0x92, 0x19, 0xD3, + 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x09, 0xF2, + 0x40, 0xF0, 0x9E, 0xFB, 0x09, 0x63, 0x00, 0x44, + 0x01, 0x97, 0xC3, 0x94, 0x48, 0xA4, 0xC1, 0xD4, + 0x00, 0xEE, 0x40, 0x92, 0x19, 0xD3, 0x12, 0x95, + 0x19, 0xD3, 0x10, 0x95, 0x19, 0xD3, 0x02, 0x80, + 0x19, 0xD3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xD3, + 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x08, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, + 0x40, 0xF0, 0xAE, 0xFB, 0x0A, 0x65, 0x00, 0x44, + 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, + 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, + 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, 0xEA, 0x43, + 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, + 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, + 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0xC0, 0x00, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, + 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, + 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEB, 0x43, + 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF, + 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3, + 0x02, 0x80, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x09, 0x93, 0x00, 0x09, + 0x19, 0xD3, 0x02, 0x80, 0x40, 0xF0, 0x56, 0xF2, + 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0xC8, 0xD2, + 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0, + 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, + 0x3B, 0xF5, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x85, 0xF2, 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, + 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, 0x0F, 0x9F, + 0x92, 0xF2, 0x40, 0xF0, 0x94, 0xF2, 0x40, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0x92, 0xF2, 0xC8, 0xD2, + 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0, + 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, + 0xF1, 0xBD, 0x19, 0xD3, 0xB6, 0xEC, 0x11, 0x93, + 0xB4, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0xAC, 0xF2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, + 0xC3, 0x94, 0x0A, 0x07, 0x07, 0x00, 0xC1, 0xD6, + 0x0A, 0x05, 0x00, 0xA0, 0x1A, 0xD5, 0x96, 0xEC, + 0x11, 0x93, 0xB6, 0xEC, 0x19, 0xD3, 0x01, 0x80, + 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, + 0x41, 0xA2, 0xC2, 0xD2, 0x40, 0x92, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x13, 0x97, 0xB4, 0xEC, 0x40, 0x46, + 0x02, 0x5E, 0x0F, 0x9F, 0x2C, 0xF3, 0x12, 0x95, + 0x96, 0xEC, 0x0A, 0x03, 0x07, 0x00, 0xC1, 0x92, + 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0x09, 0x05, + 0x01, 0x00, 0x48, 0x02, 0xC1, 0x92, 0xC2, 0xD2, + 0x11, 0x93, 0x96, 0xEC, 0x4E, 0x02, 0xC1, 0x94, + 0xC5, 0xD6, 0xC5, 0x92, 0x11, 0x07, 0x96, 0xEC, + 0x0B, 0x03, 0x0F, 0x00, 0xC1, 0x98, 0x46, 0x06, + 0x7A, 0x93, 0x79, 0x93, 0x5C, 0x95, 0x5A, 0x95, + 0x02, 0xA3, 0xC3, 0xD2, 0x04, 0x95, 0xC5, 0x96, + 0x41, 0x06, 0xC5, 0xD6, 0x42, 0x46, 0x02, 0x9E, + 0x0F, 0x9F, 0xD5, 0xF2, 0x11, 0x93, 0x96, 0xEC, + 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xC1, 0x92, + 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0xC1, 0x92, + 0x09, 0xB5, 0x1F, 0x00, 0x43, 0x44, 0x02, 0x8E, + 0x0F, 0x9F, 0x02, 0xF3, 0x40, 0x44, 0x02, 0x4E, + 0x0F, 0x9F, 0x03, 0xF3, 0x0A, 0x05, 0xFF, 0xFF, + 0x0F, 0x9F, 0x03, 0xF3, 0x43, 0x94, 0x11, 0x93, + 0x96, 0xEC, 0x42, 0x02, 0xC1, 0xD4, 0x11, 0x93, + 0x96, 0xEC, 0x49, 0x02, 0xC1, 0x92, 0x19, 0xD3, + 0xB4, 0xEC, 0x09, 0x05, 0xF2, 0xFF, 0x1A, 0xD5, + 0x92, 0xEC, 0x09, 0x43, 0xD0, 0x07, 0x02, 0x9E, + 0x0F, 0x9F, 0x2C, 0xF3, 0x11, 0x93, 0xDC, 0xF7, + 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93, + 0xDB, 0xF7, 0x09, 0xA3, 0x40, 0x00, 0x19, 0xD3, + 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, 0x01, 0x95, + 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, 0x40, 0x96, + 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, 0x92, 0xF3, + 0x11, 0x93, 0x92, 0xEC, 0x12, 0x95, 0xB6, 0xEC, + 0x02, 0x43, 0x02, 0x8E, 0x0F, 0x9F, 0x7A, 0xF3, + 0x02, 0x0E, 0x0F, 0x9F, 0x4D, 0xF3, 0x11, 0x93, + 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, + 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x80, 0x00, + 0x19, 0xD3, 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, + 0x01, 0x95, 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, + 0x40, 0x96, 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, + 0x92, 0xF3, 0x11, 0x93, 0x03, 0x80, 0x09, 0xB3, + 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x5F, 0xF3, 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, + 0x02, 0x5E, 0x0F, 0x9F, 0x5F, 0xF3, 0x40, 0xF0, + 0xA6, 0xF3, 0x0F, 0x9F, 0x94, 0xF3, 0x41, 0x92, + 0xC8, 0xD2, 0x0A, 0x95, 0x91, 0xEC, 0xC8, 0xD4, + 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, 0x11, 0x93, + 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x72, 0xF3, 0x42, 0x96, 0x1B, 0xD7, 0xC0, 0xEC, + 0x0F, 0x9F, 0x94, 0xF3, 0x0A, 0x65, 0xFE, 0x7F, + 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2, + 0x0F, 0x9F, 0x94, 0xF3, 0x12, 0x45, 0x03, 0xEC, + 0x02, 0x4E, 0x0F, 0x9F, 0x8C, 0xF3, 0x11, 0x93, + 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, + 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x08, + 0x19, 0xD3, 0xDB, 0xF7, 0x1A, 0xD5, 0x92, 0xEC, + 0x11, 0x93, 0x92, 0xEC, 0x19, 0x25, 0x92, 0xEC, + 0x09, 0x63, 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, + 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, 0xA6, 0xF3, + 0x40, 0x92, 0xC8, 0xD2, 0x09, 0x93, 0x91, 0xEC, + 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x11, 0x93, 0xD7, 0xF7, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xB6, 0xF3, 0x0A, 0x65, + 0xBC, 0x69, 0x02, 0x97, 0xC3, 0x92, 0x09, 0x83, + 0x00, 0x02, 0xC2, 0xD2, 0x11, 0x93, 0x03, 0x80, + 0x09, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0xC9, 0xF3, 0x11, 0x93, 0xDC, 0xF7, + 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93, + 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x20, 0x19, 0xD3, + 0xDB, 0xF7, 0x11, 0x93, 0xB5, 0xEC, 0x19, 0xD3, + 0x04, 0x80, 0x12, 0x95, 0xB4, 0xEC, 0x1A, 0xD5, + 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, + 0xC3, 0x96, 0x1B, 0xD7, 0xB5, 0xEC, 0x40, 0x94, + 0x1A, 0xD5, 0xB4, 0xEC, 0x19, 0xD3, 0xF2, 0xBD, + 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, + 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, 0xD3, + 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3, + 0x03, 0x82, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, + 0x01, 0x82, 0xC5, 0xD2, 0x40, 0x94, 0x01, 0xD4, + 0x13, 0x97, 0xB8, 0xEC, 0x02, 0xD6, 0x03, 0x95, + 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x05, 0x13, 0x97, + 0x03, 0xEC, 0x01, 0x27, 0x02, 0x99, 0xC4, 0x92, + 0x03, 0x03, 0xC2, 0xD2, 0x14, 0x99, 0xBA, 0xEC, + 0x03, 0x09, 0x1C, 0xD9, 0xBA, 0xEC, 0x12, 0x95, + 0x04, 0x82, 0x0A, 0xB3, 0x02, 0x00, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0x29, 0xF5, 0x01, 0x92, + 0x03, 0xD2, 0x0A, 0xA3, 0x02, 0x00, 0x19, 0xD3, + 0x04, 0x82, 0x02, 0x96, 0x0B, 0x05, 0x01, 0x00, + 0x1A, 0xD5, 0xB8, 0xEC, 0xC5, 0x92, 0x43, 0x42, + 0x02, 0x9E, 0x0F, 0x9F, 0x37, 0xF4, 0x42, 0x44, + 0x02, 0x8E, 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x93, + 0xBF, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0x37, 0xF4, 0x0C, 0x49, 0xD3, 0x08, 0x02, 0x8E, + 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x63, 0x07, 0x82, + 0x11, 0xA3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, + 0x79, 0x93, 0x79, 0x93, 0x03, 0xD2, 0xC5, 0x94, + 0x0A, 0xB5, 0xFC, 0xFF, 0x04, 0xD4, 0x03, 0x96, + 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x46, 0xF4, + 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x8E, + 0x0F, 0x9F, 0x4D, 0xF4, 0xC5, 0x98, 0x0C, 0x03, + 0xFF, 0xFF, 0x42, 0x42, 0x02, 0x8E, 0x0F, 0x9F, + 0x74, 0xF4, 0x0A, 0x95, 0xBB, 0xEC, 0x42, 0x92, + 0x19, 0xD3, 0xB9, 0xEC, 0xC5, 0x96, 0x43, 0x46, + 0x02, 0x9E, 0x0F, 0x9F, 0x66, 0xF4, 0x0B, 0x07, + 0xFC, 0xFF, 0xC5, 0xD6, 0xD2, 0x98, 0x1C, 0xD9, + 0xC8, 0xBC, 0xD2, 0x96, 0x1B, 0xD7, 0xCA, 0xBC, + 0x09, 0x03, 0xFF, 0xFF, 0x40, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0x52, 0xF4, 0x19, 0xD3, 0xB9, 0xEC, + 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x72, 0xF4, + 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xC2, 0xD2, + 0x0F, 0x9F, 0x74, 0xF4, 0x1A, 0xD5, 0x93, 0xEC, + 0x03, 0x98, 0x40, 0x48, 0x02, 0x5E, 0x0F, 0x9F, + 0xA1, 0xF4, 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, + 0x02, 0x9E, 0x0F, 0x9F, 0x84, 0xF4, 0x04, 0x94, + 0x48, 0x44, 0x02, 0x4E, 0x0F, 0x9F, 0x8F, 0xF4, + 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xA1, 0xF4, + 0x11, 0x93, 0x04, 0x82, 0x41, 0xB2, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xA1, 0xF4, 0x41, 0x96, + 0x01, 0xD6, 0x0A, 0x65, 0xBD, 0x43, 0x02, 0x99, + 0xC4, 0x92, 0x09, 0xA3, 0x80, 0x00, 0xC2, 0xD2, + 0x0A, 0x65, 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F, + 0xFA, 0xF4, 0xC5, 0x98, 0x43, 0x48, 0x02, 0x9E, + 0x0F, 0x9F, 0xFA, 0xF4, 0x4F, 0x96, 0x0C, 0xB3, + 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xAE, 0xF4, 0x47, 0x96, 0x11, 0x93, 0xB7, 0xEC, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, + 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0xD6, 0xF4, 0x12, 0x95, 0x00, 0x82, + 0x0A, 0x05, 0xFF, 0xAF, 0x05, 0xD4, 0xC8, 0xD6, + 0xC8, 0xD2, 0x40, 0xF0, 0x7B, 0xF7, 0x42, 0x00, + 0x05, 0x96, 0xC3, 0x94, 0x01, 0xB5, 0x40, 0x44, + 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, 0x06, 0x98, + 0x50, 0x98, 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x98, + 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x92, 0x03, 0xD2, + 0x0F, 0x9F, 0xFF, 0xF4, 0x03, 0x94, 0x40, 0x44, + 0x02, 0x5E, 0x0F, 0x9F, 0xE3, 0xF4, 0x0A, 0x65, + 0x5E, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x48, 0xA2, + 0xC2, 0xD2, 0x0F, 0x9F, 0xFF, 0xF4, 0x11, 0x93, + 0xB8, 0xEC, 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x03, + 0x04, 0x96, 0x13, 0x25, 0x03, 0xEC, 0xC1, 0xD4, + 0x11, 0x93, 0xBA, 0xEC, 0x19, 0x05, 0xBA, 0xEC, + 0x1B, 0xD7, 0x01, 0x82, 0x0A, 0x65, 0xFD, 0x7D, + 0x02, 0x99, 0xC4, 0x92, 0x43, 0xA2, 0xC2, 0xD2, + 0x41, 0x92, 0x01, 0xD2, 0x03, 0x94, 0x40, 0x44, + 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, 0x11, 0x93, + 0xB9, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0x0B, 0xF5, 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, + 0xBA, 0xEC, 0x19, 0xD3, 0xBB, 0xEC, 0x03, 0x96, + 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, + 0x41, 0x98, 0x1C, 0xD9, 0xB7, 0xEC, 0x11, 0x93, + 0xBF, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, + 0x24, 0xF5, 0x11, 0x93, 0x00, 0x82, 0x19, 0xD3, + 0x02, 0x82, 0x0A, 0x65, 0xFD, 0x7D, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2, + 0x40, 0x98, 0x1C, 0xD9, 0xBF, 0xEC, 0x0F, 0x9F, + 0x2C, 0xF5, 0x01, 0x92, 0x19, 0xD3, 0xB7, 0xEC, + 0x01, 0x94, 0x40, 0x44, 0x02, 0x5E, 0x0F, 0x9F, + 0x38, 0xF5, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2, + 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x12, 0x95, 0x03, 0x80, + 0x0A, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0x57, 0xF5, 0x0A, 0xB7, 0x00, 0x08, + 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x5A, 0xF5, + 0x11, 0x93, 0x03, 0xEC, 0x41, 0x02, 0x09, 0xB3, + 0xFE, 0xFF, 0x12, 0x95, 0x07, 0x80, 0x01, 0x45, + 0x02, 0x8E, 0x0F, 0x9F, 0x5A, 0xF5, 0x41, 0x92, + 0x0F, 0x9F, 0x5B, 0xF5, 0x40, 0x92, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, + 0x13, 0x97, 0x6E, 0xEC, 0x0B, 0x47, 0xA0, 0x00, + 0x02, 0x5E, 0x0F, 0x9F, 0x86, 0xF5, 0x09, 0x63, + 0x08, 0x43, 0x0A, 0x65, 0xFF, 0x5F, 0x01, 0x99, + 0xC4, 0xD4, 0x0A, 0x95, 0x9B, 0xEC, 0xD2, 0x96, + 0x1B, 0xD7, 0xFA, 0xBC, 0xD2, 0x96, 0xC4, 0xD6, + 0xD2, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, 0xD2, 0x96, + 0xC1, 0xD6, 0xC2, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, + 0x0F, 0x9F, 0xC4, 0xF5, 0x0C, 0x69, 0xFF, 0x6F, + 0x1C, 0xD9, 0xF8, 0xBC, 0x0B, 0x47, 0x10, 0x95, + 0x02, 0x5E, 0x0F, 0x9F, 0x9E, 0xF5, 0x0A, 0x95, + 0x6F, 0xEC, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, + 0xC4, 0xD6, 0xD2, 0x96, 0x1B, 0xD7, 0xF8, 0xBC, + 0x0C, 0x69, 0xEE, 0x6A, 0xC1, 0xD8, 0xC2, 0x94, + 0x1A, 0xD5, 0xF8, 0xBC, 0x40, 0x92, 0xC5, 0xD2, + 0x11, 0x43, 0xC1, 0xEC, 0x02, 0x0E, 0x0F, 0x9F, + 0xC1, 0xF5, 0xC5, 0x94, 0x0A, 0x03, 0x71, 0xEC, + 0xC1, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, 0x11, 0x93, + 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0xB3, 0xF5, 0x0A, 0x95, 0x6F, 0xEC, 0xC8, 0xD4, + 0x40, 0xF0, 0x9C, 0xF7, 0x19, 0xD3, 0xF8, 0xBC, + 0x41, 0x00, 0xC5, 0x96, 0x41, 0x06, 0xC5, 0xD6, + 0x13, 0x47, 0xC1, 0xEC, 0x02, 0x1E, 0x0F, 0x9F, + 0xA5, 0xF5, 0x40, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, + 0x40, 0x92, 0x19, 0xD3, 0x6E, 0xEC, 0x19, 0xD3, + 0xC1, 0xEC, 0x0A, 0x65, 0x52, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65, + 0xEB, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3, + 0xBF, 0xFF, 0xC2, 0xD2, 0x41, 0x00, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x43, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x06, 0x92, 0x01, 0xD2, 0x0A, 0x65, + 0xF0, 0x6A, 0x0B, 0x97, 0x6F, 0xEC, 0x02, 0x99, + 0xC4, 0x98, 0xD3, 0xD8, 0x02, 0xD6, 0x0A, 0x03, + 0x02, 0x00, 0x01, 0x97, 0xC3, 0x98, 0x02, 0x96, + 0xC3, 0xD8, 0x01, 0x96, 0xC1, 0xD6, 0x1A, 0xD5, + 0x6E, 0xEC, 0xC5, 0x98, 0x14, 0x99, 0x6F, 0xEC, + 0xC2, 0xD8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92, + 0xC8, 0xD2, 0x40, 0xF0, 0xD9, 0xF5, 0x41, 0x00, + 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0x13, 0xF6, 0x42, 0x42, 0x02, 0x5E, + 0x0F, 0x9F, 0x10, 0xF6, 0x0A, 0x65, 0xFE, 0x7F, + 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2, + 0x40, 0x92, 0x19, 0xD3, 0xC0, 0xEC, 0x0A, 0x65, + 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xE9, 0x43, + 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF, + 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x63, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93, + 0xAF, 0xBC, 0x47, 0xB2, 0x59, 0x95, 0x5A, 0x95, + 0x12, 0xA5, 0xBF, 0xBC, 0x0A, 0xB3, 0x01, 0x00, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x35, 0xF6, + 0x41, 0x04, 0x05, 0x93, 0x40, 0x96, 0x20, 0xD6, + 0x62, 0x97, 0x0F, 0x9F, 0x44, 0xF6, 0x14, 0x99, + 0xFC, 0xBC, 0xD1, 0xD8, 0x14, 0x99, 0xFE, 0xBC, + 0xD1, 0xD8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xD8, + 0x20, 0x98, 0x03, 0x49, 0x02, 0x1E, 0x0F, 0x9F, + 0x3B, 0xF6, 0xC5, 0x92, 0x62, 0x42, 0x02, 0x4E, + 0x0F, 0x9F, 0x5D, 0xF6, 0x02, 0x8E, 0x0F, 0x9F, + 0x57, 0xF6, 0x61, 0x42, 0x02, 0x4E, 0x0F, 0x9F, + 0x81, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, 0x63, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xA4, 0xF6, 0x0F, 0x9F, + 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, 0x0C, 0x99, + 0x71, 0xEC, 0x0B, 0x05, 0xFF, 0xFF, 0x40, 0x96, + 0x0F, 0x9F, 0x6A, 0xF6, 0xD1, 0x96, 0xD4, 0xD6, + 0x20, 0x96, 0x41, 0x06, 0x20, 0xD6, 0x02, 0x47, + 0x02, 0x1E, 0x0F, 0x9F, 0x66, 0xF6, 0x1A, 0xD5, + 0xC1, 0xEC, 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x99, + 0xC4, 0x92, 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, + 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F, + 0xAE, 0xF6, 0x0A, 0x03, 0xFE, 0xFF, 0x61, 0x95, + 0x40, 0x98, 0x20, 0xD8, 0x02, 0x49, 0x02, 0x0E, + 0x0F, 0x9F, 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, + 0x21, 0xD2, 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, + 0xC8, 0xD2, 0x21, 0x96, 0xC3, 0x92, 0x42, 0x06, + 0x21, 0xD6, 0xC8, 0xD2, 0x22, 0xD4, 0x40, 0xF0, + 0x01, 0xF1, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, + 0x20, 0xD8, 0x22, 0x94, 0x02, 0x49, 0x02, 0x1E, + 0x0F, 0x9F, 0x8D, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, + 0x0D, 0x03, 0x03, 0x00, 0xC8, 0xD2, 0x02, 0x92, + 0xC8, 0xD2, 0x01, 0x96, 0xC8, 0xD6, 0x40, 0xF0, + 0xB1, 0xF6, 0x43, 0x00, 0x63, 0x00, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x45, 0x20, 0x08, 0x0B, + 0x01, 0x00, 0x0D, 0x03, 0x08, 0x00, 0x08, 0x94, + 0xC5, 0xD4, 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, + 0x03, 0xD4, 0x42, 0x02, 0xC1, 0x92, 0x01, 0xD2, + 0x02, 0x97, 0xC5, 0x94, 0x0A, 0x83, 0xFF, 0xFF, + 0x11, 0xB3, 0x2C, 0x93, 0x09, 0xB3, 0xFB, 0xFF, + 0x19, 0xD3, 0x2C, 0x93, 0x03, 0x92, 0x40, 0x42, + 0x02, 0x4E, 0x0F, 0x9F, 0xE4, 0xF6, 0x01, 0x94, + 0xD2, 0x92, 0x19, 0xD3, 0x2C, 0x93, 0x01, 0xD4, + 0x02, 0x94, 0x12, 0x95, 0x2C, 0x93, 0x44, 0xA4, + 0x1A, 0xD5, 0x2C, 0x93, 0x0A, 0xB5, 0xFB, 0xFF, + 0x1A, 0xD5, 0x2C, 0x93, 0x0B, 0x07, 0xFF, 0xFF, + 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0xCF, 0xF6, + 0x09, 0x63, 0xD4, 0x6C, 0x01, 0x95, 0xC2, 0x96, + 0xC5, 0x94, 0x02, 0xA7, 0xC1, 0xD6, 0x03, 0x92, + 0x54, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xF4, 0xF6, + 0x0A, 0x83, 0xFF, 0xFF, 0x1B, 0xB3, 0x2C, 0x93, + 0x45, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, + 0x19, 0xD3, 0xF2, 0xBD, 0x40, 0xF0, 0x3B, 0xF5, + 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x08, 0xF7, + 0x40, 0xF0, 0x94, 0xF2, 0x0F, 0x9F, 0x16, 0xF7, + 0x40, 0x96, 0xC8, 0xD6, 0x09, 0x93, 0x91, 0xEC, + 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x0A, 0x65, + 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, + 0xC2, 0xD2, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x0A, 0x65, + 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3, + 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, + 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, + 0xC2, 0xD2, 0x40, 0x92, 0x19, 0xD3, 0x2D, 0xBC, + 0x0A, 0x65, 0xD8, 0x43, 0x02, 0x97, 0xC3, 0x92, + 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, + 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, + 0x09, 0x63, 0xEA, 0x43, 0x01, 0x97, 0xC3, 0x94, + 0x44, 0xA4, 0xC1, 0xD4, 0x11, 0x93, 0xB9, 0xEC, + 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x6F, 0xF7, + 0x12, 0x95, 0x93, 0xEC, 0x0B, 0x67, 0x36, 0x43, + 0xD2, 0x98, 0x1C, 0xD9, 0xC8, 0xBC, 0xD2, 0x98, + 0x03, 0x93, 0xC1, 0xD8, 0x11, 0x93, 0xB9, 0xEC, + 0x09, 0x03, 0xFF, 0xFF, 0x19, 0xD3, 0xB9, 0xEC, + 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x48, 0xF7, + 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, 0xBA, 0xEC, + 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xCA, 0xD2, + 0xC2, 0xD2, 0x0A, 0x65, 0x5E, 0x43, 0x02, 0x97, + 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65, + 0xEA, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3, + 0xFB, 0xFF, 0x0F, 0x9F, 0x78, 0xF7, 0x11, 0x93, + 0x03, 0xEC, 0x19, 0xD3, 0x01, 0x82, 0x0A, 0x65, + 0xFD, 0x7D, 0x02, 0x97, 0xC3, 0x92, 0x43, 0xA2, + 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x03, 0x92, 0x04, 0x96, + 0x0D, 0x5E, 0x50, 0x46, 0x02, 0x0E, 0x40, 0x92, + 0x09, 0xEE, 0x44, 0x46, 0x04, 0x0E, 0x59, 0x93, + 0x44, 0x26, 0x04, 0x5E, 0x46, 0xEE, 0x41, 0x93, + 0x41, 0x26, 0x43, 0x4E, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, + 0xB1, 0xFE, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, + 0x08, 0x0B, 0x01, 0x00, 0x88, 0x98, 0x90, 0x9A, + 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x03, 0x94, + 0x1A, 0xD5, 0xA3, 0xF7, 0x11, 0x93, 0x00, 0x90, + 0x88, 0x98, 0x90, 0x9A, 0x1D, 0x00, 0x1A, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x1A, 0x00, 0x1B, 0x00, 0x16, 0x00, 0x21, 0x00, + 0x12, 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, + 0x19, 0x00, 0x19, 0x00, 0x21, 0x00, 0x2D, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x69, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0xF2, 0xCD, 0xF7, 0x00, 0x00, 0x74, 0xF2, + 0xCD, 0xF7, 0x00, 0x00, 0xB9, 0xF2, 0xCA, 0xF7, + 0xD1, 0xF7, 0x00, 0x00, 0x97, 0xF3, 0xCD, 0xF7, + 0x05, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * current zd1211b firmware version. + */ +#define ZD1211B_FIRMWARE_VER 4705 + +uint8_t zd1211b_firmware[] = { + 0x08, 0x91, 0xff, 0xed, 0x09, 0x93, 0x1e, 0xee, 0xd1, 0x94, 0x11, + 0xee, 0x88, 0xd4, 0xd1, 0x96, 0xd1, 0x98, 0x5c, 0x99, 0x5c, 0x99, + 0x4c, 0x99, 0x04, 0x9d, 0xd1, 0x98, 0xd1, 0x9a, 0x03, 0xee, 0xf4, + 0x94, 0xd3, 0xd4, 0x41, 0x2a, 0x40, 0x4a, 0x45, 0xbe, 0x88, 0x92, + 0x41, 0x24, 0x40, 0x44, 0x53, 0xbe, 0x40, 0xf0, 0x4e, 0xee, 0x41, + 0xee, 0x98, 0x9a, 0x72, 0xf7, 0x02, 0x00, 0x1f, 0xec, 0x00, 0x00, + 0xb2, 0xf8, 0x4d, 0x00, 0xa1, 0xec, 0x00, 0x00, 0x43, 0xf7, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xd8, + 0xa0, 0x90, 0x98, 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x5a, + 0xf0, 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x0a, 0xef, + 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x97, 0xf0, 0xa0, + 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x94, 0xf6, 0xa0, 0x90, + 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x95, 0xf5, 0xa0, 0x90, 0x98, + 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x34, 0xf7, 0xa0, 0x90, + 0x98, 0x9a, 0x88, 0xda, 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x40, + 0xf0, 0x8e, 0xee, 0x40, 0x96, 0x0a, 0x65, 0x00, 0x7d, 0x11, 0x93, + 0x76, 0xf7, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x57, 0xee, 0x40, + 0xf1, 0x1b, 0xd7, 0x76, 0xf7, 0xc5, 0x92, 0x02, 0x99, 0x41, 0x92, + 0xc4, 0xd2, 0x40, 0x92, 0xc4, 0xd2, 0x0f, 0x9f, 0x95, 0xf8, 0x0f, + 0x9f, 0x57, 0xee, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0x19, 0xd3, 0x12, 0x95, 0x19, + 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, 0xd3, 0x03, 0x82, + 0x09, 0x93, 0x65, 0xf7, 0x19, 0xd3, 0x91, 0xec, 0x40, 0xf0, 0x07, + 0xf2, 0x40, 0xf0, 0x75, 0xf3, 0x11, 0x93, 0x04, 0xec, 0x42, 0x42, + 0x02, 0x5e, 0x0f, 0x9f, 0x8c, 0xee, 0x40, 0x92, 0x19, 0xd3, 0x04, + 0xec, 0x40, 0xf0, 0xe0, 0xf1, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff, + 0xfd, 0x19, 0xd3, 0x44, 0x96, 0x40, 0xf0, 0x2d, 0xf7, 0x40, 0xf0, + 0x6d, 0xee, 0x4b, 0x62, 0x0a, 0x95, 0x2e, 0xee, 0xd1, 0xd4, 0x0b, + 0x97, 0x2b, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x00, 0xee, 0xd1, 0xd4, + 0x0b, 0x97, 0x2f, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x34, 0xee, 0xd1, + 0xd4, 0x0b, 0x97, 0x39, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x3e, 0xee, + 0xd1, 0xd4, 0x0b, 0x97, 0x43, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x2e, + 0xee, 0xd1, 0xd4, 0x0b, 0x97, 0x48, 0xee, 0xd1, 0xd6, 0x0a, 0x95, + 0x49, 0xee, 0xc1, 0xd4, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, 0xc3, + 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x43, 0xf1, 0x09, 0x93, 0x01, 0x3f, + 0x19, 0xd3, 0xc0, 0x85, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff, + 0xfc, 0x19, 0xd3, 0x44, 0x96, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x03, 0x00, 0x03, 0x96, 0x41, + 0x02, 0x03, 0x99, 0xc4, 0x94, 0x42, 0x04, 0xc1, 0x04, 0xc2, 0x94, + 0xc3, 0xd4, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, + 0x00, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0x13, 0x97, 0x95, 0xec, + 0x1b, 0xd7, 0x02, 0x80, 0x11, 0x93, 0x99, 0xec, 0x19, 0xd3, 0x7c, + 0x96, 0x0b, 0x97, 0xa0, 0x00, 0x1b, 0xd7, 0x6e, 0xec, 0x0a, 0x65, + 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xff, 0xbf, 0x11, + 0xa3, 0x9a, 0xec, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x97, + 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, + 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, + 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01, + 0x00, 0x14, 0x99, 0x03, 0x80, 0x0c, 0xb3, 0x00, 0x10, 0x40, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0x3d, 0xf0, 0x11, 0x93, 0x9f, 0xec, 0x41, + 0x02, 0x19, 0xd3, 0x9f, 0xec, 0x11, 0x93, 0x74, 0xf7, 0x40, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0x2a, 0xef, 0x0a, 0x65, 0xfe, 0x7f, 0x02, + 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x04, 0xc2, 0xd2, 0x0f, 0x9f, + 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x02, 0xd2, 0x40, 0x42, 0x02, + 0x5e, 0x0f, 0x9f, 0x76, 0xef, 0x41, 0x92, 0x19, 0xd3, 0x94, 0xec, + 0x19, 0xd3, 0x9f, 0xec, 0x12, 0x95, 0x02, 0x80, 0x1a, 0xd5, 0x95, + 0xec, 0x13, 0x97, 0x7c, 0x96, 0x1b, 0xd7, 0x99, 0xec, 0x0a, 0x65, + 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0x00, 0x40, 0x19, + 0xd3, 0x9a, 0xec, 0x09, 0x63, 0x00, 0x40, 0xc2, 0xd2, 0x02, 0x94, + 0x1a, 0xd5, 0x7c, 0x96, 0x0c, 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, + 0x5e, 0x0f, 0x9f, 0x56, 0xef, 0x0c, 0xb3, 0xff, 0x07, 0x0f, 0x9f, + 0x5a, 0xef, 0x11, 0x93, 0x06, 0x80, 0x09, 0xb3, 0xff, 0x07, 0x09, + 0x03, 0x00, 0xa0, 0x19, 0xd3, 0x97, 0xec, 0x40, 0x98, 0x0b, 0x97, + 0x9c, 0xec, 0x04, 0x95, 0x03, 0x05, 0x14, 0x03, 0x97, 0xec, 0x46, + 0x02, 0xc1, 0x92, 0xc2, 0xd2, 0x41, 0x08, 0x42, 0x48, 0x02, 0x9e, + 0x0f, 0x9f, 0x61, 0xef, 0x11, 0x93, 0x97, 0xec, 0xc1, 0x92, 0xc5, + 0xd2, 0x5f, 0xb2, 0x19, 0xd3, 0x9b, 0xec, 0x0f, 0x9f, 0x79, 0xef, + 0x13, 0x97, 0x98, 0xec, 0xc5, 0xd6, 0x11, 0x93, 0x03, 0x80, 0x09, + 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x8f, 0xef, + 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, + 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x10, 0x19, 0xd3, 0x79, 0xf7, + 0x40, 0x98, 0x1c, 0xd9, 0x9b, 0xec, 0x12, 0x95, 0x9b, 0xec, 0x40, + 0x44, 0x02, 0x4e, 0x0f, 0x9f, 0x2c, 0xf0, 0x0a, 0xb3, 0x08, 0x00, + 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xad, 0xef, 0x0a, 0xb3, 0x07, + 0x00, 0x09, 0x05, 0xa9, 0xec, 0xc2, 0x94, 0x01, 0xd4, 0x09, 0x03, + 0xa1, 0xec, 0xc1, 0x92, 0x19, 0xd3, 0x9b, 0xec, 0xc5, 0x94, 0x0a, + 0xb5, 0x00, 0xff, 0x01, 0xa5, 0xc5, 0xd4, 0x0f, 0x9f, 0xb9, 0xef, + 0x0a, 0x05, 0xff, 0xff, 0x0a, 0x03, 0xb1, 0xec, 0xc1, 0x92, 0x01, + 0xd2, 0x1a, 0xd5, 0x9b, 0xec, 0xc5, 0x96, 0x0b, 0x07, 0xff, 0xff, + 0xc5, 0xd6, 0x11, 0x93, 0x97, 0xec, 0xc5, 0x98, 0xc1, 0xd8, 0x11, + 0x93, 0x97, 0xec, 0x09, 0x05, 0x0b, 0x00, 0x03, 0xd4, 0xc2, 0x96, + 0x06, 0xd6, 0x7b, 0x95, 0x7a, 0x95, 0x4c, 0x02, 0xc1, 0x92, 0x59, + 0x93, 0x59, 0x93, 0x01, 0xa5, 0x01, 0x98, 0x0c, 0xf5, 0x7b, 0x93, + 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xb3, 0xff, 0x00, 0x04, + 0xd2, 0x5c, 0x93, 0x59, 0x93, 0x04, 0x94, 0x01, 0xa5, 0x03, 0x96, + 0xc3, 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4c, 0x02, 0x05, 0xd2, 0xc1, + 0x92, 0x09, 0xb3, 0x00, 0xff, 0x7c, 0x95, 0x7a, 0x95, 0x02, 0xa3, + 0x05, 0x98, 0xc4, 0xd2, 0x12, 0x95, 0x97, 0xec, 0x45, 0x04, 0x02, + 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x01, 0xc2, 0xd2, 0x12, 0x95, + 0x9b, 0xec, 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0x01, 0xf0, 0x12, 0x95, 0x97, 0xec, 0x4a, 0x04, 0x02, 0x99, + 0xc4, 0x92, 0x01, 0x98, 0x0c, 0xf3, 0x7b, 0x93, 0x41, 0x02, 0x0f, + 0x9f, 0x22, 0xf0, 0x43, 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0x23, 0xf0, + 0x11, 0x93, 0x97, 0xec, 0x42, 0x02, 0x0a, 0x05, 0xff, 0xff, 0xc1, + 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4a, 0x02, 0x12, 0x95, 0x60, 0x96, + 0xc1, 0xd4, 0x12, 0x95, 0x97, 0xec, 0x4b, 0x04, 0x02, 0x97, 0xc3, + 0x92, 0x09, 0xb3, 0x1f, 0xff, 0xc2, 0xd2, 0x12, 0x95, 0x97, 0xec, + 0x4b, 0x04, 0x11, 0x93, 0x62, 0x96, 0x41, 0x93, 0x59, 0x93, 0x02, + 0x99, 0xc4, 0xa2, 0xc2, 0xd2, 0xc5, 0x92, 0x19, 0xd3, 0x98, 0xec, + 0x0a, 0x95, 0x0c, 0x02, 0x1a, 0xd5, 0x02, 0x80, 0x0f, 0x9f, 0x57, + 0xf0, 0x09, 0x63, 0xfe, 0x7f, 0x01, 0x97, 0xc3, 0x94, 0x0a, 0xa5, + 0x00, 0x04, 0xc1, 0xd4, 0x11, 0x93, 0x9f, 0xec, 0x09, 0xa3, 0x00, + 0x01, 0x19, 0xd3, 0x9f, 0xec, 0x40, 0xf0, 0xdf, 0xee, 0x0f, 0x9f, + 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, + 0x9f, 0x4c, 0xf0, 0x40, 0xf0, 0xdf, 0xee, 0x11, 0x93, 0x95, 0xec, + 0x44, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x57, 0xf0, 0x48, + 0x98, 0x1c, 0xd9, 0x02, 0x80, 0x11, 0x93, 0x91, 0xec, 0x41, 0x22, + 0x0a, 0x95, 0x57, 0xf0, 0x88, 0xd4, 0x88, 0xdc, 0x91, 0x9a, 0x47, + 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, + 0x11, 0x93, 0x04, 0x82, 0x48, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0x6e, 0xf0, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, + 0x09, 0xb3, 0xff, 0xfe, 0xc2, 0xd2, 0x41, 0x92, 0x19, 0xd3, 0xbf, + 0xec, 0x11, 0x93, 0x04, 0x82, 0x43, 0xb2, 0x12, 0x95, 0x03, 0x82, + 0x02, 0xb3, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf0, 0x0a, + 0xb3, 0x00, 0xff, 0x48, 0xa2, 0x19, 0xd3, 0x03, 0x82, 0x40, 0xf0, + 0x82, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, + 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x07, 0x82, 0x11, 0x43, 0x03, 0xec, + 0x02, 0x0e, 0x0f, 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x03, 0x82, 0x09, + 0xa3, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, 0x40, 0x96, 0x1b, 0xd7, + 0xbf, 0xec, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, + 0x00, 0x11, 0x93, 0x20, 0xbc, 0xc8, 0xd2, 0x40, 0xf0, 0xe9, 0xf0, + 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, + 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x05, 0x00, 0x05, 0x94, 0x41, 0x02, + 0xc1, 0x92, 0x01, 0x97, 0xc3, 0x96, 0xc2, 0xd6, 0x0a, 0x45, 0x00, + 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0xc1, 0x92, 0x41, 0xb2, + 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xe6, 0xf0, 0x11, 0x93, 0xc0, + 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0x41, 0x98, + 0x1c, 0xd9, 0xc0, 0xec, 0x12, 0x95, 0x02, 0x80, 0x01, 0xd4, 0x40, + 0xf0, 0xfe, 0xf1, 0x0b, 0x67, 0xfd, 0x7d, 0x03, 0x99, 0xc4, 0x92, + 0x0c, 0x99, 0x96, 0x03, 0x1c, 0xd9, 0x06, 0x82, 0x41, 0x98, 0x1c, + 0xd9, 0x02, 0x82, 0x42, 0x98, 0x1c, 0xd9, 0x05, 0x82, 0x0c, 0x69, + 0x80, 0x7f, 0x1c, 0xd9, 0x00, 0xb0, 0x09, 0xa3, 0x00, 0x01, 0xc3, + 0xd2, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, + 0x0f, 0x9f, 0xe4, 0xf0, 0x42, 0xa4, 0x1a, 0xd5, 0x02, 0x80, 0x42, + 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b, + 0x01, 0x00, 0x05, 0x92, 0xc5, 0xd2, 0x60, 0xb2, 0x40, 0x42, 0x02, + 0x4e, 0x0f, 0x9f, 0xf6, 0xf0, 0x40, 0xf0, 0xd2, 0xf6, 0xc5, 0x94, + 0x0a, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xff, + 0xf0, 0x40, 0xf0, 0xc0, 0xf5, 0xc5, 0x96, 0x0b, 0xb3, 0x40, 0x00, + 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x08, 0xf1, 0x40, 0xf0, 0xfa, + 0xf4, 0xc5, 0x94, 0x0a, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, + 0x0f, 0x9f, 0x70, 0xf1, 0x13, 0x97, 0x21, 0xbc, 0x01, 0xd6, 0x0b, + 0xb3, 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1a, 0xf1, + 0x40, 0xf0, 0x62, 0xfb, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, + 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf1, 0x40, 0xf0, 0x6c, 0xfb, + 0x01, 0x96, 0x0b, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0x4c, 0xf1, 0x40, 0xf0, 0xb0, 0xfa, 0x41, 0x92, 0x19, 0xd3, + 0x73, 0xf7, 0x11, 0x93, 0x03, 0xec, 0x09, 0x43, 0x40, 0x00, 0x02, + 0x5e, 0x0f, 0x9f, 0x39, 0xf1, 0x40, 0x94, 0x1a, 0xd5, 0x73, 0xf7, + 0x11, 0x93, 0x00, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, + 0xf1, 0x11, 0x93, 0xc1, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, + 0x55, 0xf1, 0x40, 0xf0, 0xe0, 0xf1, 0x41, 0x96, 0x1b, 0xd7, 0xc1, + 0xec, 0x0f, 0x9f, 0x55, 0xf1, 0x01, 0x94, 0x0a, 0xb3, 0x08, 0x00, + 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, 0xf1, 0x40, 0xf0, 0x7c, + 0xfb, 0x01, 0x96, 0x0b, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, + 0x0f, 0x9f, 0x5e, 0xf1, 0x40, 0xf0, 0x87, 0xfb, 0x11, 0x93, 0x10, + 0xec, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x67, 0xf1, 0x44, 0x92, + 0x0f, 0x9f, 0x6b, 0xf1, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x6d, + 0xf1, 0x19, 0xd3, 0x0b, 0xbc, 0x40, 0x94, 0x1a, 0xd5, 0x10, 0xec, + 0xc5, 0x96, 0x0b, 0xb3, 0x80, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0xba, 0xf1, 0x11, 0x93, 0x28, 0xbc, 0x01, 0xd2, 0x09, 0xb3, + 0x40, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x82, 0xf1, 0x40, + 0xf0, 0xb5, 0xf6, 0x01, 0x94, 0x0a, 0xb3, 0x02, 0x00, 0x40, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf1, 0x40, 0xf0, 0x6d, 0xee, 0x40, + 0xf0, 0x8f, 0xfb, 0x40, 0xf0, 0xc3, 0xf1, 0x40, 0x96, 0x1b, 0xd7, + 0x00, 0xec, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, 0x0a, + 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xb1, 0xf1, + 0x40, 0xf0, 0x9e, 0xfb, 0x09, 0x63, 0x00, 0x44, 0x01, 0x97, 0xc3, + 0x94, 0x48, 0xa4, 0xc1, 0xd4, 0x00, 0xee, 0x40, 0x92, 0x19, 0xd3, + 0x12, 0x95, 0x19, 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, + 0xd3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, + 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xba, + 0xf1, 0x40, 0xf0, 0xae, 0xfb, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, + 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, 0x98, 0x90, + 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40, + 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, + 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, + 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, + 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, + 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, + 0x63, 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xe8, 0x43, + 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, + 0x65, 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, + 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, + 0xb3, 0xfb, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x02, + 0x80, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, + 0x09, 0x93, 0x00, 0x09, 0x19, 0xd3, 0x02, 0x80, 0x40, 0xf0, 0xfe, + 0xf1, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0xc8, 0xd2, 0x09, 0x93, + 0x91, 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, + 0xd8, 0xf4, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x2d, 0xf2, 0x0a, + 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, + 0x0f, 0x9f, 0x3a, 0xf2, 0x40, 0xf0, 0x3c, 0xf2, 0x40, 0x42, 0x02, + 0x5e, 0x0f, 0x9f, 0x3a, 0xf2, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, + 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, + 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xf1, 0xbd, + 0x19, 0xd3, 0xb6, 0xec, 0x11, 0x93, 0xb4, 0xec, 0x40, 0x42, 0x02, + 0x5e, 0x0f, 0x9f, 0x54, 0xf2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97, + 0xc3, 0x94, 0x0a, 0x07, 0x07, 0x00, 0xc1, 0xd6, 0x0a, 0x05, 0x00, + 0xa0, 0x1a, 0xd5, 0x96, 0xec, 0x11, 0x93, 0xb6, 0xec, 0x19, 0xd3, + 0x01, 0x80, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x41, + 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x13, 0x97, 0xb4, 0xec, 0x40, + 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xc3, 0xf2, 0x12, 0x95, 0x96, 0xec, + 0x0a, 0x03, 0x07, 0x00, 0xc1, 0x92, 0xc2, 0xd2, 0x11, 0x93, 0x96, + 0xec, 0x09, 0x05, 0x01, 0x00, 0x48, 0x02, 0xc1, 0x92, 0xc2, 0xd2, + 0x11, 0x93, 0x96, 0xec, 0x4e, 0x02, 0xc1, 0x94, 0xc5, 0xd6, 0xc5, + 0x92, 0x11, 0x07, 0x96, 0xec, 0x0b, 0x03, 0x0f, 0x00, 0xc1, 0x98, + 0x46, 0x06, 0x7a, 0x93, 0x79, 0x93, 0x5c, 0x95, 0x5a, 0x95, 0x02, + 0xa3, 0xc3, 0xd2, 0x04, 0x95, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6, + 0x42, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0x7d, 0xf2, 0x11, 0x93, 0x96, + 0xec, 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xc1, 0x92, 0xc2, 0xd2, + 0x11, 0x93, 0x96, 0xec, 0xc1, 0x92, 0x09, 0xb5, 0x1f, 0x00, 0x43, + 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0xaa, 0xf2, 0x40, 0x44, 0x02, 0x4e, + 0x0f, 0x9f, 0xab, 0xf2, 0x0a, 0x05, 0xff, 0xff, 0x0f, 0x9f, 0xab, + 0xf2, 0x43, 0x94, 0x11, 0x93, 0x96, 0xec, 0x42, 0x02, 0xc1, 0xd4, + 0x13, 0x97, 0x96, 0xec, 0x03, 0x93, 0xd1, 0x94, 0x7a, 0x95, 0x7a, + 0x95, 0xc1, 0x92, 0x59, 0x93, 0x59, 0x93, 0x01, 0x05, 0x49, 0x06, + 0xc3, 0x92, 0x7f, 0xb2, 0x01, 0x05, 0x1a, 0xd5, 0xb4, 0xec, 0x0a, + 0x05, 0xf2, 0xff, 0x1a, 0xd5, 0x92, 0xec, 0x11, 0x93, 0x92, 0xec, + 0x12, 0x95, 0xb6, 0xec, 0x02, 0x43, 0x02, 0x8e, 0x0f, 0x9f, 0x11, + 0xf3, 0x02, 0x0e, 0x0f, 0x9f, 0xe4, 0xf2, 0x11, 0x93, 0x7a, 0xf7, + 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, + 0xa3, 0x80, 0x00, 0x19, 0xd3, 0x79, 0xf7, 0x09, 0x63, 0x00, 0x80, + 0x01, 0x95, 0xc2, 0x94, 0x1a, 0xd5, 0xb5, 0xec, 0x40, 0x96, 0x1b, + 0xd7, 0xb4, 0xec, 0x0f, 0x9f, 0x29, 0xf3, 0x11, 0x93, 0x03, 0x80, + 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xf6, + 0xf2, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, + 0xf6, 0xf2, 0x40, 0xf0, 0x3d, 0xf3, 0x0f, 0x9f, 0x2b, 0xf3, 0x41, + 0x92, 0xc8, 0xd2, 0x0a, 0x95, 0x91, 0xec, 0xc8, 0xd4, 0x40, 0xf0, + 0xd0, 0xee, 0x42, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, + 0x4e, 0x0f, 0x9f, 0x09, 0xf3, 0x42, 0x96, 0x1b, 0xd7, 0xc0, 0xec, + 0x0f, 0x9f, 0x2b, 0xf3, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, + 0x92, 0x42, 0xa2, 0xc2, 0xd2, 0x0f, 0x9f, 0x2b, 0xf3, 0x12, 0x45, + 0x03, 0xec, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf3, 0x11, 0x93, 0x7a, + 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, + 0x09, 0xa3, 0x00, 0x08, 0x19, 0xd3, 0x79, 0xf7, 0x1a, 0xd5, 0x92, + 0xec, 0x11, 0x93, 0x92, 0xec, 0x19, 0x25, 0x92, 0xec, 0x09, 0x63, + 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x41, 0x00, 0x88, 0x98, 0x90, + 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0x3d, 0xf3, + 0x40, 0x92, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, 0xc8, 0xd2, 0x40, + 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, + 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x75, 0xf7, 0x40, 0x42, 0x02, + 0x4e, 0x0f, 0x9f, 0x4d, 0xf3, 0x0a, 0x65, 0xbc, 0x69, 0x02, 0x97, + 0xc3, 0x92, 0x09, 0x83, 0x00, 0x02, 0xc2, 0xd2, 0x11, 0x93, 0x03, + 0x80, 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, + 0x60, 0xf3, 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, + 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x20, 0x19, 0xd3, + 0x79, 0xf7, 0x11, 0x93, 0xb5, 0xec, 0x19, 0xd3, 0x04, 0x80, 0x12, + 0x95, 0xb4, 0xec, 0x1a, 0xd5, 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, + 0x01, 0x97, 0xc3, 0x96, 0x1b, 0xd7, 0xb5, 0xec, 0x40, 0x94, 0x1a, + 0xd5, 0xb4, 0xec, 0x19, 0xd3, 0xf2, 0xbd, 0x88, 0x98, 0x90, 0x9a, + 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, + 0xd3, 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, + 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01, + 0x00, 0x11, 0x93, 0x01, 0x82, 0xc5, 0xd2, 0x40, 0x94, 0x01, 0xd4, + 0x13, 0x97, 0xb8, 0xec, 0x02, 0xd6, 0x03, 0x95, 0x0c, 0x99, 0xbb, + 0xec, 0x04, 0x05, 0x13, 0x97, 0x03, 0xec, 0x01, 0x27, 0x02, 0x99, + 0xc4, 0x92, 0x03, 0x03, 0xc2, 0xd2, 0x14, 0x99, 0xba, 0xec, 0x03, + 0x09, 0x1c, 0xd9, 0xba, 0xec, 0x12, 0x95, 0x04, 0x82, 0x0a, 0xb3, + 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xc6, 0xf4, 0x01, + 0x92, 0x03, 0xd2, 0x0a, 0xa3, 0x02, 0x00, 0x19, 0xd3, 0x04, 0x82, + 0x02, 0x96, 0x0b, 0x05, 0x01, 0x00, 0x1a, 0xd5, 0xb8, 0xec, 0xc5, + 0x92, 0x43, 0x42, 0x02, 0x9e, 0x0f, 0x9f, 0xce, 0xf3, 0x42, 0x44, + 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x40, + 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xce, 0xf3, 0x0c, 0x49, 0xd3, 0x08, + 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x63, 0x07, 0x82, 0x11, + 0xa3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, 0x79, 0x93, 0x79, 0x93, + 0x03, 0xd2, 0xc5, 0x94, 0x0a, 0xb5, 0xfc, 0xff, 0x04, 0xd4, 0x03, + 0x96, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xdd, 0xf3, 0x11, 0x93, + 0xb8, 0xec, 0x41, 0x42, 0x02, 0x8e, 0x0f, 0x9f, 0xe4, 0xf3, 0xc5, + 0x98, 0x0c, 0x03, 0xff, 0xff, 0x42, 0x42, 0x02, 0x8e, 0x0f, 0x9f, + 0x0b, 0xf4, 0x0a, 0x95, 0xbb, 0xec, 0x42, 0x92, 0x19, 0xd3, 0xb9, + 0xec, 0xc5, 0x96, 0x43, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0xfd, 0xf3, + 0x0b, 0x07, 0xfc, 0xff, 0xc5, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xc8, + 0xbc, 0xd2, 0x96, 0x1b, 0xd7, 0xca, 0xbc, 0x09, 0x03, 0xff, 0xff, + 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe9, 0xf3, 0x19, 0xd3, 0xb9, + 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x09, 0xf4, 0x0a, 0x05, + 0xfe, 0xff, 0xca, 0xd2, 0xc2, 0xd2, 0x0f, 0x9f, 0x0b, 0xf4, 0x1a, + 0xd5, 0x93, 0xec, 0x03, 0x98, 0x40, 0x48, 0x02, 0x5e, 0x0f, 0x9f, + 0x38, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x9e, 0x0f, + 0x9f, 0x1b, 0xf4, 0x04, 0x94, 0x48, 0x44, 0x02, 0x4e, 0x0f, 0x9f, + 0x26, 0xf4, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x38, 0xf4, 0x11, + 0x93, 0x04, 0x82, 0x41, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, + 0x38, 0xf4, 0x41, 0x96, 0x01, 0xd6, 0x0a, 0x65, 0xbd, 0x43, 0x02, + 0x99, 0xc4, 0x92, 0x09, 0xa3, 0x80, 0x00, 0xc2, 0xd2, 0x0a, 0x65, + 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, + 0xd2, 0x0f, 0x9f, 0x97, 0xf4, 0xc5, 0x98, 0x43, 0x48, 0x02, 0x9e, + 0x0f, 0x9f, 0x97, 0xf4, 0x4f, 0x96, 0x0c, 0xb3, 0x01, 0x00, 0x40, + 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x45, 0xf4, 0x47, 0x96, 0x11, 0x93, + 0xb7, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x73, 0xf4, 0x11, + 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x73, 0xf4, + 0x12, 0x95, 0x00, 0x82, 0x0a, 0x05, 0xff, 0xaf, 0x05, 0xd4, 0xc8, + 0xd6, 0xc8, 0xd2, 0x40, 0xf0, 0x18, 0xf7, 0x42, 0x00, 0x05, 0x96, + 0xc3, 0x94, 0x01, 0xb5, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x68, + 0xf4, 0x11, 0x93, 0xba, 0xec, 0x4d, 0x42, 0x02, 0x8e, 0x0f, 0x9f, + 0x73, 0xf4, 0x06, 0x98, 0x50, 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, + 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, 0x92, 0x03, 0xd2, 0x0f, 0x9f, + 0x9c, 0xf4, 0x03, 0x94, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x80, + 0xf4, 0x0a, 0x65, 0x5e, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x48, 0xa2, + 0xc2, 0xd2, 0x0f, 0x9f, 0x9c, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x0c, + 0x99, 0xbb, 0xec, 0x04, 0x03, 0x04, 0x96, 0x13, 0x25, 0x03, 0xec, + 0xc1, 0xd4, 0x11, 0x93, 0xba, 0xec, 0x19, 0x05, 0xba, 0xec, 0x1b, + 0xd7, 0x01, 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x99, 0xc4, 0x92, + 0x43, 0xa2, 0xc2, 0xd2, 0x41, 0x92, 0x01, 0xd2, 0x03, 0x94, 0x40, + 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x11, 0x93, 0xb9, 0xec, + 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa8, 0xf4, 0x19, 0xd3, 0xb8, + 0xec, 0x19, 0xd3, 0xba, 0xec, 0x19, 0xd3, 0xbb, 0xec, 0x03, 0x96, + 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x41, 0x98, 0x1c, + 0xd9, 0xb7, 0xec, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, + 0x0f, 0x9f, 0xc1, 0xf4, 0x11, 0x93, 0x00, 0x82, 0x19, 0xd3, 0x02, + 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, + 0x00, 0x01, 0xc2, 0xd2, 0x40, 0x98, 0x1c, 0xd9, 0xbf, 0xec, 0x0f, + 0x9f, 0xc9, 0xf4, 0x01, 0x92, 0x19, 0xd3, 0xb7, 0xec, 0x01, 0x94, + 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xd5, 0xf4, 0x0a, 0x65, 0xea, + 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0xc2, 0xd2, + 0x47, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, + 0x00, 0x12, 0x95, 0x03, 0x80, 0x0a, 0xb3, 0x00, 0x40, 0x40, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0xf4, 0xf4, 0x0a, 0xb7, 0x00, 0x08, 0x40, + 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xf7, 0xf4, 0x11, 0x93, 0x03, 0xec, + 0x41, 0x02, 0x09, 0xb3, 0xfe, 0xff, 0x12, 0x95, 0x07, 0x80, 0x01, + 0x45, 0x02, 0x8e, 0x0f, 0x9f, 0xf7, 0xf4, 0x41, 0x92, 0x0f, 0x9f, + 0xf8, 0xf4, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x41, + 0x20, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, + 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x13, 0x97, 0x6e, + 0xec, 0x0b, 0x47, 0xa0, 0x00, 0x02, 0x5e, 0x0f, 0x9f, 0x23, 0xf5, + 0x09, 0x63, 0x08, 0x43, 0x0a, 0x65, 0xff, 0x5f, 0x01, 0x99, 0xc4, + 0xd4, 0x0a, 0x95, 0x9b, 0xec, 0xd2, 0x96, 0x1b, 0xd7, 0xfa, 0xbc, + 0xd2, 0x96, 0xc4, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0xd2, + 0x96, 0xc1, 0xd6, 0xc2, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x0f, 0x9f, + 0x61, 0xf5, 0x0c, 0x69, 0xff, 0x6f, 0x1c, 0xd9, 0xf8, 0xbc, 0x0b, + 0x47, 0x10, 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0x3b, 0xf5, 0x0a, 0x95, + 0x6f, 0xec, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, 0xc4, 0xd6, 0xd2, + 0x96, 0x1b, 0xd7, 0xf8, 0xbc, 0x0c, 0x69, 0xee, 0x6a, 0xc1, 0xd8, + 0xc2, 0x94, 0x1a, 0xd5, 0xf8, 0xbc, 0x40, 0x92, 0xc5, 0xd2, 0x11, + 0x43, 0xc2, 0xec, 0x02, 0x0e, 0x0f, 0x9f, 0x5e, 0xf5, 0xc5, 0x94, + 0x0a, 0x03, 0x71, 0xec, 0xc1, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x11, + 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x50, 0xf5, + 0x0a, 0x95, 0x6f, 0xec, 0xc8, 0xd4, 0x40, 0xf0, 0x39, 0xf7, 0x19, + 0xd3, 0xf8, 0xbc, 0x41, 0x00, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6, + 0x13, 0x47, 0xc2, 0xec, 0x02, 0x1e, 0x0f, 0x9f, 0x42, 0xf5, 0x40, + 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0x40, 0x92, 0x19, 0xd3, 0x6e, 0xec, + 0x19, 0xd3, 0xc2, 0xec, 0x0a, 0x65, 0x52, 0x43, 0x02, 0x97, 0xc3, + 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x99, + 0xc4, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x41, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x43, 0x20, 0x08, 0x0b, 0x01, 0x00, + 0x06, 0x92, 0x01, 0xd2, 0x0a, 0x65, 0xf0, 0x6a, 0x0b, 0x97, 0x6f, + 0xec, 0x02, 0x99, 0xc4, 0x98, 0xd3, 0xd8, 0x02, 0xd6, 0x0a, 0x03, + 0x02, 0x00, 0x01, 0x97, 0xc3, 0x98, 0x02, 0x96, 0xc3, 0xd8, 0x01, + 0x96, 0xc1, 0xd6, 0x1a, 0xd5, 0x6e, 0xec, 0xc5, 0x98, 0x14, 0x99, + 0x6f, 0xec, 0xc2, 0xd8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, + 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0xc8, 0xd2, 0x40, 0xf0, + 0x76, 0xf5, 0x41, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, + 0x4e, 0x0f, 0x9f, 0xb0, 0xf5, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, + 0xad, 0xf5, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x42, + 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0xc0, 0xec, 0x0a, 0x65, + 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, + 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, + 0xbf, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x63, + 0x20, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xaf, 0xbc, 0x47, 0xb2, + 0x59, 0x95, 0x5a, 0x95, 0x12, 0xa5, 0xbf, 0xbc, 0x0a, 0xb3, 0x01, + 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xd2, 0xf5, 0x41, 0x04, + 0x05, 0x93, 0x40, 0x96, 0x20, 0xd6, 0x62, 0x97, 0x0f, 0x9f, 0xe1, + 0xf5, 0x14, 0x99, 0xfc, 0xbc, 0xd1, 0xd8, 0x14, 0x99, 0xfe, 0xbc, + 0xd1, 0xd8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x20, 0x98, 0x03, + 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0xd8, 0xf5, 0xc5, 0x92, 0x62, 0x42, + 0x02, 0x4e, 0x0f, 0x9f, 0xfa, 0xf5, 0x02, 0x8e, 0x0f, 0x9f, 0xf4, + 0xf5, 0x61, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1e, 0xf6, 0x0f, 0x9f, + 0x4b, 0xf6, 0x63, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x41, 0xf6, 0x0f, + 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x0c, 0x99, 0x71, 0xec, + 0x0b, 0x05, 0xff, 0xff, 0x40, 0x96, 0x0f, 0x9f, 0x07, 0xf6, 0xd1, + 0x96, 0xd4, 0xd6, 0x20, 0x96, 0x41, 0x06, 0x20, 0xd6, 0x02, 0x47, + 0x02, 0x1e, 0x0f, 0x9f, 0x03, 0xf6, 0x1a, 0xd5, 0xc2, 0xec, 0x0a, + 0x65, 0xeb, 0x43, 0x02, 0x99, 0xc4, 0x92, 0x09, 0xa3, 0xc0, 0x00, + 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, + 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x0f, 0x9f, 0x4b, 0xf6, 0x0a, 0x03, + 0xfe, 0xff, 0x61, 0x95, 0x40, 0x98, 0x20, 0xd8, 0x02, 0x49, 0x02, + 0x0e, 0x0f, 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x21, 0xd2, + 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, 0xc8, 0xd2, 0x21, 0x96, 0xc3, + 0x92, 0x42, 0x06, 0x21, 0xd6, 0xc8, 0xd2, 0x22, 0xd4, 0x40, 0xf0, + 0xa2, 0xf0, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x22, + 0x94, 0x02, 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0x2a, 0xf6, 0x0f, 0x9f, + 0x4b, 0xf6, 0x0d, 0x03, 0x03, 0x00, 0xc8, 0xd2, 0x02, 0x92, 0xc8, + 0xd2, 0x01, 0x96, 0xc8, 0xd6, 0x40, 0xf0, 0x4e, 0xf6, 0x43, 0x00, + 0x63, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x45, 0x20, 0x08, + 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x08, 0x00, 0x08, 0x94, 0xc5, 0xd4, + 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, 0x03, 0xd4, 0x42, 0x02, 0xc1, + 0x92, 0x01, 0xd2, 0x02, 0x97, 0xc5, 0x94, 0x0a, 0x83, 0xff, 0xff, + 0x11, 0xb3, 0x2c, 0x93, 0x09, 0xb3, 0xfb, 0xff, 0x19, 0xd3, 0x2c, + 0x93, 0x03, 0x92, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x81, 0xf6, + 0x01, 0x94, 0xd2, 0x92, 0x19, 0xd3, 0x2c, 0x93, 0x01, 0xd4, 0x02, + 0x94, 0x12, 0x95, 0x2c, 0x93, 0x44, 0xa4, 0x1a, 0xd5, 0x2c, 0x93, + 0x0a, 0xb5, 0xfb, 0xff, 0x1a, 0xd5, 0x2c, 0x93, 0x0b, 0x07, 0xff, + 0xff, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x6c, 0xf6, 0x09, 0x63, + 0xd4, 0x6c, 0x01, 0x95, 0xc2, 0x96, 0xc5, 0x94, 0x02, 0xa7, 0xc1, + 0xd6, 0x03, 0x92, 0x54, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x91, 0xf6, + 0x0a, 0x83, 0xff, 0xff, 0x1b, 0xb3, 0x2c, 0x93, 0x45, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, + 0x00, 0x40, 0x19, 0xd3, 0xf2, 0xbd, 0x40, 0xf0, 0xd8, 0xf4, 0x40, + 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa5, 0xf6, 0x40, 0xf0, 0x3c, 0xf2, + 0x0f, 0x9f, 0xb3, 0xf6, 0x40, 0x96, 0xc8, 0xd6, 0x09, 0x93, 0x91, + 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x0a, 0x65, 0xfe, 0x7f, + 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, + 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, + 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, + 0xfb, 0xff, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0x2d, 0xbc, 0x0a, + 0x65, 0xd8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, + 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, + 0x00, 0x09, 0x63, 0xea, 0x43, 0x01, 0x97, 0xc3, 0x94, 0x44, 0xa4, + 0xc1, 0xd4, 0x11, 0x93, 0xb9, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, + 0x9f, 0x0c, 0xf7, 0x12, 0x95, 0x93, 0xec, 0x0b, 0x67, 0x36, 0x43, + 0xd2, 0x98, 0x1c, 0xd9, 0xc8, 0xbc, 0xd2, 0x98, 0x03, 0x93, 0xc1, + 0xd8, 0x11, 0x93, 0xb9, 0xec, 0x09, 0x03, 0xff, 0xff, 0x19, 0xd3, + 0xb9, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe5, 0xf6, 0x19, + 0xd3, 0xb8, 0xec, 0x19, 0xd3, 0xba, 0xec, 0x0a, 0x05, 0xfe, 0xff, + 0xca, 0xd2, 0xca, 0xd2, 0xc2, 0xd2, 0x0a, 0x65, 0x5e, 0x43, 0x02, + 0x97, 0xc3, 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, + 0x02, 0x99, 0xc4, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0x0f, 0x9f, 0x15, + 0xf7, 0x11, 0x93, 0x03, 0xec, 0x19, 0xd3, 0x01, 0x82, 0x0a, 0x65, + 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x43, 0xa2, 0xc2, 0xd2, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x92, + 0x04, 0x96, 0x0d, 0x5e, 0x50, 0x46, 0x02, 0x0e, 0x40, 0x92, 0x09, + 0xee, 0x44, 0x46, 0x04, 0x0e, 0x59, 0x93, 0x44, 0x26, 0x04, 0x5e, + 0x46, 0xee, 0x41, 0x93, 0x41, 0x26, 0x43, 0x4e, 0x88, 0x98, 0x90, + 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0xb1, 0xfe, + 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x88, + 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x94, + 0x1a, 0xd5, 0x40, 0xf7, 0x11, 0x93, 0x00, 0x90, 0x88, 0x98, 0x90, + 0x9a, 0x1d, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00, + 0x21, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf2, 0x6b, 0xf7, 0x00, 0x00, + 0x1c, 0xf2, 0x6b, 0xf7, 0x00, 0x00, 0x61, 0xf2, 0x68, 0xf7, 0x6f, + 0xf7, 0x00, 0x00, 0x2e, 0xf3, 0x6b, 0xf7, 0x25, 0x47, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/freebsd/sys/dev/usb/wlan/if_zydreg.h b/freebsd/sys/dev/usb/wlan/if_zydreg.h new file mode 100644 index 00000000..724b8c57 --- /dev/null +++ b/freebsd/sys/dev/usb/wlan/if_zydreg.h @@ -0,0 +1,1319 @@ +/* $OpenBSD: if_zydreg.h,v 1.19 2006/11/30 19:28:07 damien Exp $ */ +/* $NetBSD: if_zydreg.h,v 1.2 2007/06/16 11:18:45 kiyohara Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * ZyDAS ZD1211/ZD1211B USB WLAN driver. + */ + +#define ZYD_CR_GPI_EN 0x9418 +#define ZYD_CR_RADIO_PD 0x942c +#define ZYD_CR_RF2948_PD 0x942c +#define ZYD_CR_EN_PS_MANUAL_AGC 0x943c +#define ZYD_CR_CONFIG_PHILIPS 0x9440 +#define ZYD_CR_I2C_WRITE 0x9444 +#define ZYD_CR_SA2400_SER_RP 0x9448 +#define ZYD_CR_RADIO_PE 0x9458 +#define ZYD_CR_RST_BUS_MASTER 0x945c +#define ZYD_CR_RFCFG 0x9464 +#define ZYD_CR_HSTSCHG 0x946c +#define ZYD_CR_PHY_ON 0x9474 +#define ZYD_CR_RX_DELAY 0x9478 +#define ZYD_CR_RX_PE_DELAY 0x947c +#define ZYD_CR_GPIO_1 0x9490 +#define ZYD_CR_GPIO_2 0x9494 +#define ZYD_CR_EnZYD_CRyBufMux 0x94a8 +#define ZYD_CR_PS_CTRL 0x9500 +#define ZYD_CR_ADDA_PWR_DWN 0x9504 +#define ZYD_CR_ADDA_MBIAS_WT 0x9508 +#define ZYD_CR_INTERRUPT 0x9510 +#define ZYD_CR_MAC_PS_STATE 0x950c +#define ZYD_CR_ATIM_WND_PERIOD 0x951c +#define ZYD_CR_BCN_INTERVAL 0x9520 +#define ZYD_CR_PRE_TBTT 0x9524 + +/* + * MAC registers. + */ +#define ZYD_MAC_MACADRL 0x9610 /* MAC address (low) */ +#define ZYD_MAC_MACADRH 0x9614 /* MAC address (high) */ +#define ZYD_MAC_BSSADRL 0x9618 /* BSS address (low) */ +#define ZYD_MAC_BSSADRH 0x961c /* BSS address (high) */ +#define ZYD_MAC_BCNCFG 0x9620 /* BCN configuration */ +#define ZYD_MAC_GHTBL 0x9624 /* Group hash table (low) */ +#define ZYD_MAC_GHTBH 0x9628 /* Group hash table (high) */ +#define ZYD_MAC_RX_TIMEOUT 0x962c /* Rx timeout value */ +#define ZYD_MAC_BAS_RATE 0x9630 /* Basic rate setting */ +#define ZYD_MAC_MAN_RATE 0x9634 /* Mandatory rate setting */ +#define ZYD_MAC_RTSCTSRATE 0x9638 /* RTS CTS rate */ +#define ZYD_MAC_BACKOFF_PROTECT 0x963c /* Backoff protection */ +#define ZYD_MAC_RX_THRESHOLD 0x9640 /* Rx threshold */ +#define ZYD_MAC_TX_PE_CONTROL 0x9644 /* Tx_PE control */ +#define ZYD_MAC_AFTER_PNP 0x9648 /* After PnP */ +#define ZYD_MAC_RX_PE_DELAY 0x964c /* Rx_pe delay */ +#define ZYD_MAC_RX_ADDR2_L 0x9650 /* RX address2 (low) */ +#define ZYD_MAC_RX_ADDR2_H 0x9654 /* RX address2 (high) */ +#define ZYD_MAC_SIFS_ACK_TIME 0x9658 /* Dynamic SIFS ack time */ +#define ZYD_MAC_PHY_DELAY 0x9660 /* PHY delay */ +#define ZYD_MAC_PHY_DELAY2 0x966c /* PHY delay */ +#define ZYD_MAC_BCNFIFO 0x9670 /* Beacon FIFO I/O port */ +#define ZYD_MAC_SNIFFER 0x9674 /* Sniffer on/off */ +#define ZYD_MAC_ENCRYPTION_TYPE 0x9678 /* Encryption type */ +#define ZYD_MAC_RETRY 0x967c /* Retry time */ +#define ZYD_MAC_MISC 0x9680 /* Misc */ +#define ZYD_MAC_STMACHINESTAT 0x9684 /* State machine status */ +#define ZYD_MAC_TX_UNDERRUN_CNT 0x9688 /* TX underrun counter */ +#define ZYD_MAC_RXFILTER 0x968c /* Send to host settings */ +#define ZYD_MAC_ACK_EXT 0x9690 /* Acknowledge extension */ +#define ZYD_MAC_BCNFIFOST 0x9694 /* BCN FIFO set and status */ +#define ZYD_MAC_DIFS_EIFS_SIFS 0x9698 /* DIFS, EIFS & SIFS settings */ +#define ZYD_MAC_RX_TIMEOUT_CNT 0x969c /* RX timeout count */ +#define ZYD_MAC_RX_TOTAL_FRAME 0x96a0 /* RX total frame count */ +#define ZYD_MAC_RX_CRC32_CNT 0x96a4 /* RX CRC32 frame count */ +#define ZYD_MAC_RX_CRC16_CNT 0x96a8 /* RX CRC16 frame count */ +#define ZYD_MAC_RX_UDEC 0x96ac /* RX unicast decr. error count */ +#define ZYD_MAC_RX_OVERRUN_CNT 0x96b0 /* RX FIFO overrun count */ +#define ZYD_MAC_RX_MDEC 0x96bc /* RX multicast decr. err. cnt. */ +#define ZYD_MAC_NAV_TCR 0x96c4 /* NAV timer count read */ +#define ZYD_MAC_BACKOFF_ST_RD 0x96c8 /* Backoff status read */ +#define ZYD_MAC_DM_RETRY_CNT_RD 0x96cc /* DM retry count read */ +#define ZYD_MAC_RX_ACR 0x96d0 /* RX arbitration count read */ +#define ZYD_MAC_TX_CCR 0x96d4 /* Tx complete count read */ +#define ZYD_MAC_TCB_ADDR 0x96e8 /* Current PCI process TCP addr */ +#define ZYD_MAC_RCB_ADDR 0x96ec /* Next RCB address */ +#define ZYD_MAC_CONT_WIN_LIMIT 0x96f0 /* Contention window limit */ +#define ZYD_MAC_TX_PKT 0x96f4 /* Tx total packet count read */ +#define ZYD_MAC_DL_CTRL 0x96f8 /* Download control */ +#define ZYD_MAC_CAM_MODE 0x9700 /* CAM: Continuous Access Mode */ +#define ZYD_MACB_TXPWR_CTL1 0x9b00 +#define ZYD_MACB_TXPWR_CTL2 0x9b04 +#define ZYD_MACB_TXPWR_CTL3 0x9b08 +#define ZYD_MACB_TXPWR_CTL4 0x9b0c +#define ZYD_MACB_AIFS_CTL1 0x9b10 +#define ZYD_MACB_AIFS_CTL2 0x9b14 +#define ZYD_MACB_TXOP 0x9b20 +#define ZYD_MACB_MAX_RETRY 0x9b28 + +/* + * Miscellaneous registers. + */ +#define ZYD_FIRMWARE_START_ADDR 0xee00 +#define ZYD_FIRMWARE_BASE_ADDR 0xee1d /* Firmware base address */ + +/* + * EEPROM registers. + */ +#define ZYD_EEPROM_START_HEAD 0xf800 /* EEPROM start */ +#define ZYD_EEPROM_SUBID 0xf817 +#define ZYD_EEPROM_POD 0xf819 +#define ZYD_EEPROM_MAC_ADDR_P1 0xf81b /* Part 1 of the MAC address */ +#define ZYD_EEPROM_MAC_ADDR_P2 0xf81d /* Part 2 of the MAC address */ +#define ZYD_EEPROM_PWR_CAL 0xf81f /* Calibration */ +#define ZYD_EEPROM_PWR_INT 0xf827 /* Calibration */ +#define ZYD_EEPROM_ALLOWEDCHAN 0xf82f /* Allowed CH mask, 1 bit each */ +#define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */ +#define ZYD_EEPROM_PHY_REG 0xf83c /* PHY registers */ +#define ZYD_EEPROM_36M_CAL 0xf83f /* Calibration */ +#define ZYD_EEPROM_11A_INT 0xf847 /* Interpolation */ +#define ZYD_EEPROM_48M_CAL 0xf84f /* Calibration */ +#define ZYD_EEPROM_48M_INT 0xf857 /* Interpolation */ +#define ZYD_EEPROM_54M_CAL 0xf85f /* Calibration */ +#define ZYD_EEPROM_54M_INT 0xf867 /* Interpolation */ + +/* + * Firmware registers offsets (relative to fwbase). + */ +#define ZYD_FW_FIRMWARE_REV 0x0000 /* Firmware version */ +#define ZYD_FW_USB_SPEED 0x0001 /* USB speed (!=0 if highspeed) */ +#define ZYD_FW_FIX_TX_RATE 0x0002 /* Fixed TX rate */ +#define ZYD_FW_LINK_STATUS 0x0003 +#define ZYD_FW_SOFT_RESET 0x0004 +#define ZYD_FW_FLASH_CHK 0x0005 + +/* possible flags for register ZYD_FW_LINK_STATUS */ +#define ZYD_LED1 (1 << 8) +#define ZYD_LED2 (1 << 9) + +/* + * RF IDs. + */ +#define ZYD_RF_UW2451 0x2 /* not supported yet */ +#define ZYD_RF_UCHIP 0x3 /* not supported yet */ +#define ZYD_RF_AL2230 0x4 +#define ZYD_RF_AL7230B 0x5 +#define ZYD_RF_THETA 0x6 /* not supported yet */ +#define ZYD_RF_AL2210 0x7 +#define ZYD_RF_MAXIM_NEW 0x8 +#define ZYD_RF_GCT 0x9 +#define ZYD_RF_AL2230S 0xa /* not supported yet */ +#define ZYD_RF_RALINK 0xb /* not supported yet */ +#define ZYD_RF_INTERSIL 0xc /* not supported yet */ +#define ZYD_RF_RFMD 0xd +#define ZYD_RF_MAXIM_NEW2 0xe +#define ZYD_RF_PHILIPS 0xf /* not supported yet */ + +/* + * PHY registers (8 bits, not documented). + */ +#define ZYD_CR0 0x9000 +#define ZYD_CR1 0x9004 +#define ZYD_CR2 0x9008 +#define ZYD_CR3 0x900c +#define ZYD_CR5 0x9010 +#define ZYD_CR6 0x9014 +#define ZYD_CR7 0x9018 +#define ZYD_CR8 0x901c +#define ZYD_CR4 0x9020 +#define ZYD_CR9 0x9024 +#define ZYD_CR10 0x9028 +#define ZYD_CR11 0x902c +#define ZYD_CR12 0x9030 +#define ZYD_CR13 0x9034 +#define ZYD_CR14 0x9038 +#define ZYD_CR15 0x903c +#define ZYD_CR16 0x9040 +#define ZYD_CR17 0x9044 +#define ZYD_CR18 0x9048 +#define ZYD_CR19 0x904c +#define ZYD_CR20 0x9050 +#define ZYD_CR21 0x9054 +#define ZYD_CR22 0x9058 +#define ZYD_CR23 0x905c +#define ZYD_CR24 0x9060 +#define ZYD_CR25 0x9064 +#define ZYD_CR26 0x9068 +#define ZYD_CR27 0x906c +#define ZYD_CR28 0x9070 +#define ZYD_CR29 0x9074 +#define ZYD_CR30 0x9078 +#define ZYD_CR31 0x907c +#define ZYD_CR32 0x9080 +#define ZYD_CR33 0x9084 +#define ZYD_CR34 0x9088 +#define ZYD_CR35 0x908c +#define ZYD_CR36 0x9090 +#define ZYD_CR37 0x9094 +#define ZYD_CR38 0x9098 +#define ZYD_CR39 0x909c +#define ZYD_CR40 0x90a0 +#define ZYD_CR41 0x90a4 +#define ZYD_CR42 0x90a8 +#define ZYD_CR43 0x90ac +#define ZYD_CR44 0x90b0 +#define ZYD_CR45 0x90b4 +#define ZYD_CR46 0x90b8 +#define ZYD_CR47 0x90bc +#define ZYD_CR48 0x90c0 +#define ZYD_CR49 0x90c4 +#define ZYD_CR50 0x90c8 +#define ZYD_CR51 0x90cc +#define ZYD_CR52 0x90d0 +#define ZYD_CR53 0x90d4 +#define ZYD_CR54 0x90d8 +#define ZYD_CR55 0x90dc +#define ZYD_CR56 0x90e0 +#define ZYD_CR57 0x90e4 +#define ZYD_CR58 0x90e8 +#define ZYD_CR59 0x90ec +#define ZYD_CR60 0x90f0 +#define ZYD_CR61 0x90f4 +#define ZYD_CR62 0x90f8 +#define ZYD_CR63 0x90fc +#define ZYD_CR64 0x9100 +#define ZYD_CR65 0x9104 +#define ZYD_CR66 0x9108 +#define ZYD_CR67 0x910c +#define ZYD_CR68 0x9110 +#define ZYD_CR69 0x9114 +#define ZYD_CR70 0x9118 +#define ZYD_CR71 0x911c +#define ZYD_CR72 0x9120 +#define ZYD_CR73 0x9124 +#define ZYD_CR74 0x9128 +#define ZYD_CR75 0x912c +#define ZYD_CR76 0x9130 +#define ZYD_CR77 0x9134 +#define ZYD_CR78 0x9138 +#define ZYD_CR79 0x913c +#define ZYD_CR80 0x9140 +#define ZYD_CR81 0x9144 +#define ZYD_CR82 0x9148 +#define ZYD_CR83 0x914c +#define ZYD_CR84 0x9150 +#define ZYD_CR85 0x9154 +#define ZYD_CR86 0x9158 +#define ZYD_CR87 0x915c +#define ZYD_CR88 0x9160 +#define ZYD_CR89 0x9164 +#define ZYD_CR90 0x9168 +#define ZYD_CR91 0x916c +#define ZYD_CR92 0x9170 +#define ZYD_CR93 0x9174 +#define ZYD_CR94 0x9178 +#define ZYD_CR95 0x917c +#define ZYD_CR96 0x9180 +#define ZYD_CR97 0x9184 +#define ZYD_CR98 0x9188 +#define ZYD_CR99 0x918c +#define ZYD_CR100 0x9190 +#define ZYD_CR101 0x9194 +#define ZYD_CR102 0x9198 +#define ZYD_CR103 0x919c +#define ZYD_CR104 0x91a0 +#define ZYD_CR105 0x91a4 +#define ZYD_CR106 0x91a8 +#define ZYD_CR107 0x91ac +#define ZYD_CR108 0x91b0 +#define ZYD_CR109 0x91b4 +#define ZYD_CR110 0x91b8 +#define ZYD_CR111 0x91bc +#define ZYD_CR112 0x91c0 +#define ZYD_CR113 0x91c4 +#define ZYD_CR114 0x91c8 +#define ZYD_CR115 0x91cc +#define ZYD_CR116 0x91d0 +#define ZYD_CR117 0x91d4 +#define ZYD_CR118 0x91d8 +#define ZYD_CR119 0x91dc +#define ZYD_CR120 0x91e0 +#define ZYD_CR121 0x91e4 +#define ZYD_CR122 0x91e8 +#define ZYD_CR123 0x91ec +#define ZYD_CR124 0x91f0 +#define ZYD_CR125 0x91f4 +#define ZYD_CR126 0x91f8 +#define ZYD_CR127 0x91fc +#define ZYD_CR128 0x9200 +#define ZYD_CR129 0x9204 +#define ZYD_CR130 0x9208 +#define ZYD_CR131 0x920c +#define ZYD_CR132 0x9210 +#define ZYD_CR133 0x9214 +#define ZYD_CR134 0x9218 +#define ZYD_CR135 0x921c +#define ZYD_CR136 0x9220 +#define ZYD_CR137 0x9224 +#define ZYD_CR138 0x9228 +#define ZYD_CR139 0x922c +#define ZYD_CR140 0x9230 +#define ZYD_CR141 0x9234 +#define ZYD_CR142 0x9238 +#define ZYD_CR143 0x923c +#define ZYD_CR144 0x9240 +#define ZYD_CR145 0x9244 +#define ZYD_CR146 0x9248 +#define ZYD_CR147 0x924c +#define ZYD_CR148 0x9250 +#define ZYD_CR149 0x9254 +#define ZYD_CR150 0x9258 +#define ZYD_CR151 0x925c +#define ZYD_CR152 0x9260 +#define ZYD_CR153 0x9264 +#define ZYD_CR154 0x9268 +#define ZYD_CR155 0x926c +#define ZYD_CR156 0x9270 +#define ZYD_CR157 0x9274 +#define ZYD_CR158 0x9278 +#define ZYD_CR159 0x927c +#define ZYD_CR160 0x9280 +#define ZYD_CR161 0x9284 +#define ZYD_CR162 0x9288 +#define ZYD_CR163 0x928c +#define ZYD_CR164 0x9290 +#define ZYD_CR165 0x9294 +#define ZYD_CR166 0x9298 +#define ZYD_CR167 0x929c +#define ZYD_CR168 0x92a0 +#define ZYD_CR169 0x92a4 +#define ZYD_CR170 0x92a8 +#define ZYD_CR171 0x92ac +#define ZYD_CR172 0x92b0 +#define ZYD_CR173 0x92b4 +#define ZYD_CR174 0x92b8 +#define ZYD_CR175 0x92bc +#define ZYD_CR176 0x92c0 +#define ZYD_CR177 0x92c4 +#define ZYD_CR178 0x92c8 +#define ZYD_CR179 0x92cc +#define ZYD_CR180 0x92d0 +#define ZYD_CR181 0x92d4 +#define ZYD_CR182 0x92d8 +#define ZYD_CR183 0x92dc +#define ZYD_CR184 0x92e0 +#define ZYD_CR185 0x92e4 +#define ZYD_CR186 0x92e8 +#define ZYD_CR187 0x92ec +#define ZYD_CR188 0x92f0 +#define ZYD_CR189 0x92f4 +#define ZYD_CR190 0x92f8 +#define ZYD_CR191 0x92fc +#define ZYD_CR192 0x9300 +#define ZYD_CR193 0x9304 +#define ZYD_CR194 0x9308 +#define ZYD_CR195 0x930c +#define ZYD_CR196 0x9310 +#define ZYD_CR197 0x9314 +#define ZYD_CR198 0x9318 +#define ZYD_CR199 0x931c +#define ZYD_CR200 0x9320 +#define ZYD_CR201 0x9324 +#define ZYD_CR202 0x9328 +#define ZYD_CR203 0x932c +#define ZYD_CR204 0x9330 +#define ZYD_CR205 0x9334 +#define ZYD_CR206 0x9338 +#define ZYD_CR207 0x933c +#define ZYD_CR208 0x9340 +#define ZYD_CR209 0x9344 +#define ZYD_CR210 0x9348 +#define ZYD_CR211 0x934c +#define ZYD_CR212 0x9350 +#define ZYD_CR213 0x9354 +#define ZYD_CR214 0x9358 +#define ZYD_CR215 0x935c +#define ZYD_CR216 0x9360 +#define ZYD_CR217 0x9364 +#define ZYD_CR218 0x9368 +#define ZYD_CR219 0x936c +#define ZYD_CR220 0x9370 +#define ZYD_CR221 0x9374 +#define ZYD_CR222 0x9378 +#define ZYD_CR223 0x937c +#define ZYD_CR224 0x9380 +#define ZYD_CR225 0x9384 +#define ZYD_CR226 0x9388 +#define ZYD_CR227 0x938c +#define ZYD_CR228 0x9390 +#define ZYD_CR229 0x9394 +#define ZYD_CR230 0x9398 +#define ZYD_CR231 0x939c +#define ZYD_CR232 0x93a0 +#define ZYD_CR233 0x93a4 +#define ZYD_CR234 0x93a8 +#define ZYD_CR235 0x93ac +#define ZYD_CR236 0x93b0 +#define ZYD_CR240 0x93c0 +#define ZYD_CR241 0x93c4 +#define ZYD_CR242 0x93c8 +#define ZYD_CR243 0x93cc +#define ZYD_CR244 0x93d0 +#define ZYD_CR245 0x93d4 +#define ZYD_CR251 0x93ec +#define ZYD_CR252 0x93f0 +#define ZYD_CR253 0x93f4 +#define ZYD_CR254 0x93f8 +#define ZYD_CR255 0x93fc + +/* nitems(ZYD_*_CHANTABLE) */ +static const uint8_t zyd_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + +/* copied nearly verbatim from the Linux driver rewrite */ +#define ZYD_DEF_PHY \ +{ \ + { ZYD_CR0, 0x0a }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ + { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xa0 }, \ + { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0x7f }, \ + { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ + { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ + { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0c }, \ + { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ + { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x19 }, \ + { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ + { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ + { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ + { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ + { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ + { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \ + { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ + { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ + { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ + { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ + { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ + { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ + { ZYD_CR79, 0x68 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ + { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x00 }, { ZYD_CR84, 0x00 }, \ + { ZYD_CR85, 0x02 }, { ZYD_CR86, 0x00 }, { ZYD_CR87, 0x00 }, \ + { ZYD_CR88, 0xff }, { ZYD_CR89, 0xfc }, { ZYD_CR90, 0x00 }, \ + { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x08 }, \ + { ZYD_CR94, 0x00 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0xff }, \ + { ZYD_CR97, 0xe7 }, { ZYD_CR98, 0x00 }, { ZYD_CR99, 0x00 }, \ + { ZYD_CR100, 0x00 }, { ZYD_CR101, 0xae }, { ZYD_CR102, 0x02 }, \ + { ZYD_CR103, 0x00 }, { ZYD_CR104, 0x03 }, { ZYD_CR105, 0x65 }, \ + { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ + { ZYD_CR109, 0xaa }, { ZYD_CR110, 0xaa }, { ZYD_CR111, 0x25 }, \ + { ZYD_CR112, 0x25 }, { ZYD_CR113, 0x00 }, { ZYD_CR119, 0x1e }, \ + { ZYD_CR125, 0x90 }, { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, \ + { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \ + { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \ + { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \ + { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x1E }, { ZYD_CR83, 0x24 }, \ + { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \ + { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \ + { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \ + { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x50 }, \ + { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR101, 0x13 }, \ + { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, \ + { ZYD_CR105, 0x12 }, { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, \ + { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \ + { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \ + { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \ + { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \ + { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0C }, \ + { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, { ZYD_CR138, 0xa0 }, \ + { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, { ZYD_CR141, 0x82 }, \ + { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ + { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, { ZYD_CR149, 0x50 }, \ + { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, { ZYD_CR160, 0xfe }, \ + { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, { ZYD_CR163, 0xfa }, \ + { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, { ZYD_CR166, 0xbe }, \ + { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, { ZYD_CR169, 0xba }, \ + { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, { ZYD_CR204, 0x7d }, \ + { ZYD_CR203, 0x30 }, { 0, 0} \ +} + +#define ZYD_DEF_PHYB \ +{ \ + { ZYD_CR0, 0x14 }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \ + { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xe0 }, \ + { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0xf0 }, \ + { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \ + { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \ + { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x10 }, { ZYD_CR21, 0x0e }, \ + { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \ + { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x10 }, \ + { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \ + { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \ + { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \ + { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ + { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ + { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x33 }, { ZYD_CR46, 0xff }, \ + { ZYD_CR47, 0x1E }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ + { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ + { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ + { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ + { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \ + { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \ + { ZYD_CR79, 0xf0 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \ + { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, \ + { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0c }, { ZYD_CR87, 0x12 }, \ + { ZYD_CR88, 0x0c }, { ZYD_CR89, 0x00 }, { ZYD_CR90, 0x58 }, \ + { ZYD_CR91, 0x04 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x00 }, \ + { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x20 }, { ZYD_CR96, 0x50 }, \ + { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR99, 0x00 }, \ + { ZYD_CR100, 0x01 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, \ + { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \ + { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, \ + { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, \ + { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfc }, \ + { ZYD_CR118, 0xfa }, { ZYD_CR119, 0x1e }, { ZYD_CR125, 0x90 }, \ + { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, { ZYD_CR128, 0x14 }, \ + { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0c }, \ + { ZYD_CR136, 0xdf }, { ZYD_CR137, 0xa0 }, { ZYD_CR138, 0xa8 }, \ + { ZYD_CR139, 0xb4 }, { ZYD_CR140, 0x98 }, { ZYD_CR141, 0x82 }, \ + { ZYD_CR142, 0x53 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ + { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x40 }, { ZYD_CR149, 0x40 }, \ + { ZYD_CR150, 0x14 }, { ZYD_CR151, 0x18 }, { ZYD_CR159, 0x70 }, \ + { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \ + { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \ + { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \ + { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \ + { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }, \ + { 0, 0 } \ +} + +#define ZYD_RFMD_PHY \ +{ \ + { ZYD_CR2, 0x1e }, { ZYD_CR9, 0x20 }, { ZYD_CR10, 0x89 }, \ + { ZYD_CR11, 0x00 }, { ZYD_CR15, 0xd0 }, { ZYD_CR17, 0x68 }, \ + { ZYD_CR19, 0x4a }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0e }, \ + { ZYD_CR23, 0x48 }, { ZYD_CR24, 0x14 }, { ZYD_CR26, 0x90 }, \ + { ZYD_CR27, 0x30 }, { ZYD_CR29, 0x20 }, { ZYD_CR31, 0xb2 }, \ + { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 }, \ + { ZYD_CR34, 0x0f }, { ZYD_CR35, 0xf0 }, { ZYD_CR41, 0x2a }, \ + { ZYD_CR46, 0x7f }, { ZYD_CR47, 0x1e }, { ZYD_CR51, 0xc5 }, \ + { ZYD_CR52, 0xc5 }, { ZYD_CR53, 0xc5 }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR82, 0x00 }, \ + { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, \ + { ZYD_CR86, 0x10 }, { ZYD_CR87, 0x2a }, { ZYD_CR88, 0x10 }, \ + { ZYD_CR89, 0x24 }, { ZYD_CR90, 0x18 }, { ZYD_CR91, 0x00 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR93, 0x00 }, { ZYD_CR94, 0x01 }, \ + { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x40 }, { ZYD_CR97, 0x37 }, \ + { ZYD_CR98, 0x05 }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \ + { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, \ + { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, { ZYD_CR106, 0x1a }, \ + { ZYD_CR107, 0x24 }, { ZYD_CR108, 0x0a }, { ZYD_CR109, 0x13 }, \ + { ZYD_CR110, 0x2f }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x40 }, \ + { ZYD_CR116, 0x40 }, { ZYD_CR117, 0xf0 }, { ZYD_CR118, 0xf0 }, \ + { ZYD_CR119, 0x16 }, { ZYD_CR122, 0x00 }, { ZYD_CR127, 0x03 }, \ + { ZYD_CR131, 0x08 }, { ZYD_CR138, 0x28 }, { ZYD_CR148, 0x44 }, \ + { ZYD_CR150, 0x10 }, { ZYD_CR169, 0xbb }, { ZYD_CR170, 0xbb } \ +} + +#define ZYD_RFMD_RF \ +{ \ + 0x000007, 0x07dd43, 0x080959, 0x0e6666, 0x116a57, 0x17dd43, \ + 0x1819f9, 0x1e6666, 0x214554, 0x25e7fa, 0x27fffa, 0x294128, \ + 0x2c0000, 0x300000, 0x340000, 0x381e0f, 0x6c180f \ +} + +#define ZYD_RFMD_CHANTABLE \ +{ \ + { 0x181979, 0x1e6666 }, \ + { 0x181989, 0x1e6666 }, \ + { 0x181999, 0x1e6666 }, \ + { 0x1819a9, 0x1e6666 }, \ + { 0x1819b9, 0x1e6666 }, \ + { 0x1819c9, 0x1e6666 }, \ + { 0x1819d9, 0x1e6666 }, \ + { 0x1819e9, 0x1e6666 }, \ + { 0x1819f9, 0x1e6666 }, \ + { 0x181a09, 0x1e6666 }, \ + { 0x181a19, 0x1e6666 }, \ + { 0x181a29, 0x1e6666 }, \ + { 0x181a39, 0x1e6666 }, \ + { 0x181a60, 0x1c0000 } \ +} + +#define ZYD_AL2230_PHY \ +{ \ + { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \ + { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \ + { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, \ + { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x2b }, \ + { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, { ZYD_CR10, 0x89 }, \ + { ZYD_CR17, 0x28 }, { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, \ + { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, \ + { ZYD_CR46, 0x96 }, { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, \ + { ZYD_CR89, 0x04 }, { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, \ + { ZYD_CR100, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x09 }, \ + { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfc }, \ + { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, \ + { ZYD_CR122, 0xe0 }, { ZYD_CR137, 0x88 }, { ZYD_CR252, 0xff }, \ + { ZYD_CR253, 0xff }, { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, \ + { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } \ +} + +#define ZYD_AL2230_PHY_B \ +{ \ + { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2B }, \ + { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, { ZYD_CR33, 0x28 }, \ + { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, \ + { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x99 }, { ZYD_CR47, 0x1e }, \ + { ZYD_CR48, 0x06 }, { ZYD_CR49, 0xf9 }, { ZYD_CR51, 0x01 }, \ + { ZYD_CR52, 0x80 }, { ZYD_CR53, 0x7e }, { ZYD_CR65, 0x00 }, \ + { ZYD_CR66, 0x00 }, { ZYD_CR67, 0x00 }, { ZYD_CR68, 0x00 }, \ + { ZYD_CR69, 0x28 }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ + { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ + { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, \ + { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, \ + { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfa }, \ + { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x6c }, \ + { ZYD_CR122, 0xfc }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \ + { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR137, 0x50 }, \ + { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR150, 0x0d }, \ + { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 } \ +} + +#define ZYD_AL2230_PHY_PART1 \ +{ \ + { ZYD_CR240, 0x57 }, { ZYD_CR9, 0xe0 } \ +} + +#define ZYD_AL2230_PHY_PART2 \ +{ \ + { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x7f }, \ +} + +#define ZYD_AL2230_PHY_PART3 \ +{ \ + { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ +} + +#define ZYD_AL2230S_PHY_INIT \ +{ \ + { ZYD_CR47, 0x1e }, { ZYD_CR106, 0x22 }, { ZYD_CR107, 0x2a }, \ + { ZYD_CR109, 0x13 }, { ZYD_CR118, 0xf8 }, { ZYD_CR119, 0x12 }, \ + { ZYD_CR122, 0xe0 }, { ZYD_CR128, 0x10 }, { ZYD_CR129, 0x0e }, \ + { ZYD_CR130, 0x10 } \ +} + +#define ZYD_AL2230_PHY_FINI_PART1 \ +{ \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, \ + { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 }, \ +} + +#define ZYD_AL2230_RF_PART1 \ +{ \ + 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3 \ +} + +#define ZYD_AL2230_RF_PART2 \ +{ \ + 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \ + 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f \ +} + +#define ZYD_AL2230_RF_PART3 \ +{ \ + 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f \ +} + +#define ZYD_AL2230_RF_B \ +{ \ + 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \ + 0x0005a4, 0x0f4dc5, 0x0805b6, 0x0146c7, 0x000688, 0x0403b9, \ + 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00580f \ +} + +#define ZYD_AL2230_RF_B_PART1 \ +{ \ + 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000 \ +} + +#define ZYD_AL2230_RF_B_PART2 \ +{ \ + 0x25a000, 0xa3b2f0, 0x6da010, 0xe36280, 0x116000, 0x9dc020, \ + 0x5ddb00, 0xd99000, 0x3ffbd0, 0xb00000, 0xf01a00 \ +} + +#define ZYD_AL2230_RF_B_PART3 \ +{ \ + 0xf01b00, 0xf01e00, 0xf01a00 \ +} + +#define ZYD_AL2230_CHANTABLE \ +{ \ + { 0x03f790, 0x033331, 0x00000d }, \ + { 0x03f790, 0x0b3331, 0x00000d }, \ + { 0x03e790, 0x033331, 0x00000d }, \ + { 0x03e790, 0x0b3331, 0x00000d }, \ + { 0x03f7a0, 0x033331, 0x00000d }, \ + { 0x03f7a0, 0x0b3331, 0x00000d }, \ + { 0x03e7a0, 0x033331, 0x00000d }, \ + { 0x03e7a0, 0x0b3331, 0x00000d }, \ + { 0x03f7b0, 0x033331, 0x00000d }, \ + { 0x03f7b0, 0x0b3331, 0x00000d }, \ + { 0x03e7b0, 0x033331, 0x00000d }, \ + { 0x03e7b0, 0x0b3331, 0x00000d }, \ + { 0x03f7c0, 0x033331, 0x00000d }, \ + { 0x03e7c0, 0x066661, 0x00000d } \ +} + +#define ZYD_AL2230_CHANTABLE_B \ +{ \ + { 0x09efc0, 0x8cccc0, 0xb00000 }, \ + { 0x09efc0, 0x8cccd0, 0xb00000 }, \ + { 0x09e7c0, 0x8cccc0, 0xb00000 }, \ + { 0x09e7c0, 0x8cccd0, 0xb00000 }, \ + { 0x05efc0, 0x8cccc0, 0xb00000 }, \ + { 0x05efc0, 0x8cccd0, 0xb00000 }, \ + { 0x05e7c0, 0x8cccc0, 0xb00000 }, \ + { 0x05e7c0, 0x8cccd0, 0xb00000 }, \ + { 0x0defc0, 0x8cccc0, 0xb00000 }, \ + { 0x0defc0, 0x8cccd0, 0xb00000 }, \ + { 0x0de7c0, 0x8cccc0, 0xb00000 }, \ + { 0x0de7c0, 0x8cccd0, 0xb00000 }, \ + { 0x03efc0, 0x8cccc0, 0xb00000 }, \ + { 0x03e7c0, 0x866660, 0xb00000 } \ +} + +#define ZYD_AL7230B_PHY_1 \ +{ \ + { ZYD_CR240, 0x57 }, { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, \ + { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, \ + { ZYD_CR29, 0x00 }, { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x22 }, \ + { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, \ + { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, \ + { ZYD_CR122, 0xfc }, { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x28 }, \ + { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, \ + { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x96 }, \ + { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ + { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x02 }, \ + { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR106, 0x22 }, \ + { ZYD_CR107, 0x3f }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x1f }, \ + { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, { ZYD_CR113, 0x27 }, \ + { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, { ZYD_CR116, 0x3f }, \ + { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfc }, { ZYD_CR119, 0x10 }, \ + { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR137, 0x88 }, \ + { ZYD_CR138, 0xa8 }, { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 }, \ + { ZYD_CR251, 0x2f } \ +} + +#define ZYD_AL7230B_PHY_2 \ +{ \ + { ZYD_CR251, 0x3f }, { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, \ + { ZYD_CR130, 0x10 }, { ZYD_CR38, 0x38 }, { ZYD_CR136, 0xdf } \ +} + +#define ZYD_AL7230B_PHY_3 \ +{ \ + { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 } \ +} + +#define ZYD_AL7230B_RF_1 \ +{ \ + 0x09ec04, 0x8cccc8, 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, \ + 0x6cf56a, 0xe04073, 0x193d76, 0x9dd844, 0x500007, 0xd8c010, \ + 0x3c9000, 0xbfffff, 0x700000, 0xf15d58 \ +} + +#define ZYD_AL7230B_RF_2 \ +{ \ + 0xf15d59, 0xf15d5c, 0xf15d58 \ +} + +#define ZYD_AL7230B_RF_SETCHANNEL \ +{ \ + 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, 0x6cf56a, 0xe04073, \ + 0x193d76, 0x9dd844, 0x500007, 0xd8c010, 0x3c9000, 0xf15d58 \ +} + +#define ZYD_AL7230B_CHANTABLE \ +{ \ + { 0x09ec00, 0x8cccc8 }, \ + { 0x09ec00, 0x8cccd8 }, \ + { 0x09ec00, 0x8cccc0 }, \ + { 0x09ec00, 0x8cccd0 }, \ + { 0x05ec00, 0x8cccc8 }, \ + { 0x05ec00, 0x8cccd8 }, \ + { 0x05ec00, 0x8cccc0 }, \ + { 0x05ec00, 0x8cccd0 }, \ + { 0x0dec00, 0x8cccc8 }, \ + { 0x0dec00, 0x8cccd8 }, \ + { 0x0dec00, 0x8cccc0 }, \ + { 0x0dec00, 0x8cccd0 }, \ + { 0x03ec00, 0x8cccc8 }, \ + { 0x03ec00, 0x866660 } \ +} + +#define ZYD_AL2210_PHY \ +{ \ + { ZYD_CR9, 0xe0 }, { ZYD_CR10, 0x91 }, { ZYD_CR12, 0x90 }, \ + { ZYD_CR15, 0xd0 }, { ZYD_CR16, 0x40 }, { ZYD_CR17, 0x58 }, \ + { ZYD_CR18, 0x04 }, { ZYD_CR23, 0x66 }, { ZYD_CR24, 0x14 }, \ + { ZYD_CR26, 0x90 }, { ZYD_CR31, 0x80 }, { ZYD_CR34, 0x06 }, \ + { ZYD_CR35, 0x3e }, { ZYD_CR38, 0x38 }, { ZYD_CR46, 0x90 }, \ + { ZYD_CR47, 0x1e }, { ZYD_CR64, 0x64 }, { ZYD_CR79, 0xb5 }, \ + { ZYD_CR80, 0x38 }, { ZYD_CR81, 0x30 }, { ZYD_CR113, 0xc0 }, \ + { ZYD_CR127, 0x03 } \ +} + +#define ZYD_AL2210_RF \ +{ \ + 0x2396c0, 0x00fcb1, 0x358132, 0x0108b3, 0xc77804, 0x456415, \ + 0xff2226, 0x806667, 0x7860f8, 0xbb01c9, 0x00000a, 0x00000b \ +} + +#define ZYD_AL2210_CHANTABLE \ +{ \ + 0x0196c0, 0x019710, 0x019760, 0x0197b0, 0x019800, 0x019850, \ + 0x0198a0, 0x0198f0, 0x019940, 0x019990, 0x0199e0, 0x019a30, \ + 0x019a80, 0x019b40 \ +} + +#define ZYD_GCT_PHY \ +{ \ + { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x28 }, \ + { ZYD_CR23, 0x38 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR27, 0x15 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \ + { ZYD_CR33, 0x28 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x43 }, \ + { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x92 }, \ + { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x04 }, { ZYD_CR49, 0xfa }, \ + { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, \ + { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, { ZYD_CR91, 0x00 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, { ZYD_CR99, 0x28 }, \ + { ZYD_CR100, 0x02 }, { ZYD_CR101, 0x09 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x1c }, { ZYD_CR107, 0x1c }, { ZYD_CR109, 0x13 }, \ + { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x1f }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x23 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xf0 }, \ + { ZYD_CR119, 0x1a }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x1f }, \ + { ZYD_CR122, 0xf0 }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \ + { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \ + { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR137, 0x50 }, \ + { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR146, 0x20 }, \ + { ZYD_CR252, 0xff }, { ZYD_CR253, 0xff } \ +} + +#define ZYD_GCT_RF \ +{ \ + 0x40002b, 0x519e4f, 0x6f81ad, 0x73fffe, 0x25f9c, 0x100047, \ + 0x200999, 0x307602, 0x346063, \ +} + +#define ZYD_GCT_VCO \ +{ \ + { 0x664d, 0x604d, 0x6675, 0x6475, 0x6655, 0x6455, 0x6665 }, \ + { 0x666d, 0x606d, 0x664d, 0x644d, 0x6675, 0x6475, 0x6655 }, \ + { 0x665d, 0x605d, 0x666d, 0x646d, 0x664d, 0x644d, 0x6675 }, \ + { 0x667d, 0x607d, 0x665d, 0x645d, 0x666d, 0x646d, 0x664d }, \ + { 0x6643, 0x6043, 0x667d, 0x647d, 0x665d, 0x645d, 0x666d }, \ + { 0x6663, 0x6063, 0x6643, 0x6443, 0x667d, 0x647d, 0x665d }, \ + { 0x6653, 0x6053, 0x6663, 0x6463, 0x6643, 0x6443, 0x667d }, \ + { 0x6673, 0x6073, 0x6653, 0x6453, 0x6663, 0x6463, 0x6643 }, \ + { 0x664b, 0x604b, 0x6673, 0x6473, 0x6653, 0x6453, 0x6663 }, \ + { 0x666b, 0x606b, 0x664b, 0x644b, 0x6673, 0x6473, 0x6653 }, \ + { 0x665b, 0x605b, 0x666b, 0x646b, 0x664b, 0x644b, 0x6673 } \ +} + +#define ZYD_GCT_TXGAIN \ +{ \ + 0x0e313, 0x0fb13, 0x0e093, 0x0f893, 0x0ea93, 0x1f093, 0x1f493, \ + 0x1f693, 0x1f393, 0x1f35b, 0x1e6db, 0x1ff3f, 0x1ffff, 0x361d7, \ + 0x37fbf, 0x3ff8b, 0x3ff33, 0x3fb3f, 0x3ffff \ +} + +#define ZYD_GCT_CHANNEL_ACAL \ +{ \ + 0x106847, 0x106847, 0x106867, 0x106867, 0x106867, 0x106867, \ + 0x106857, 0x106857, 0x106857, 0x106857, 0x106877, 0x106877, \ + 0x106877, 0x10684f \ +} + +#define ZYD_GCT_CHANNEL_STD \ +{ \ + 0x100047, 0x100047, 0x100067, 0x100067, 0x100067, 0x100067, \ + 0x100057, 0x100057, 0x100057, 0x100057, 0x100077, 0x100077, \ + 0x100077, 0x10004f \ +} + +#define ZYD_GCT_CHANNEL_DIV \ +{ \ + 0x200999, 0x20099b, 0x200998, 0x20099a, 0x200999, 0x20099b, \ + 0x200998, 0x20099a, 0x200999, 0x20099b, 0x200998, 0x20099a, \ + 0x200999, 0x200ccc \ +} + +#define ZYD_MAXIM2_PHY \ +{ \ + { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ + { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \ + { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \ + { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \ + { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ + { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ + { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \ + { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \ + { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \ + { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \ + { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR89, 0x18 }, \ + { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \ + { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \ + { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \ + { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe } \ +} + +#define ZYD_MAXIM2_RF \ +{ \ + 0x33334, 0x10a03, 0x00400, 0x00ca1, 0x10072, 0x18645, 0x04006, \ + 0x000a7, 0x08258, 0x03fc9, 0x0040a, 0x0000b, 0x0026c \ +} + +#define ZYD_MAXIM2_CHANTABLE_F \ +{ \ + 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, \ + 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x26664 \ +} + +#define ZYD_MAXIM2_CHANTABLE \ +{ \ + { 0x33334, 0x10a03 }, \ + { 0x08884, 0x20a13 }, \ + { 0x1ddd4, 0x30a13 }, \ + { 0x33334, 0x10a13 }, \ + { 0x08884, 0x20a23 }, \ + { 0x1ddd4, 0x30a23 }, \ + { 0x33334, 0x10a23 }, \ + { 0x08884, 0x20a33 }, \ + { 0x1ddd4, 0x30a33 }, \ + { 0x33334, 0x10a33 }, \ + { 0x08884, 0x20a43 }, \ + { 0x1ddd4, 0x30a43 }, \ + { 0x33334, 0x10a43 }, \ + { 0x26664, 0x20a53 } \ +} + +#define ZYD_TX_RATEDIV \ +{ \ + 0x1, 0x2, 0xb, 0xb, 0x1, 0x1, 0x1, 0x1, 0x30, 0x18, 0xc, 0x6, \ + 0x36, 0x24, 0x12, 0x9 \ +} + +/* + * Control pipe requests. + */ +#define ZYD_DOWNLOADREQ 0x30 +#define ZYD_DOWNLOADSTS 0x31 +#define ZYD_READFWDATAREQ 0x32 + +/* possible values for register ZYD_CR_INTERRUPT */ +#define ZYD_HWINT_MASK 0x004f0000 + +/* possible values for register ZYD_MAC_MISC */ +#define ZYD_UNLOCK_PHY_REGS 0x80 + +/* possible values for register ZYD_MAC_ENCRYPTION_TYPE */ +#define ZYD_ENC_SNIFFER 8 + +/* flags for register ZYD_MAC_RXFILTER */ +#define ZYD_FILTER_ASS_REQ (1 << 0) +#define ZYD_FILTER_ASS_RSP (1 << 1) +#define ZYD_FILTER_REASS_REQ (1 << 2) +#define ZYD_FILTER_REASS_RSP (1 << 3) +#define ZYD_FILTER_PRB_REQ (1 << 4) +#define ZYD_FILTER_PRB_RSP (1 << 5) +#define ZYD_FILTER_BCN (1 << 8) +#define ZYD_FILTER_ATIM (1 << 9) +#define ZYD_FILTER_DEASS (1 << 10) +#define ZYD_FILTER_AUTH (1 << 11) +#define ZYD_FILTER_DEAUTH (1 << 12) +#define ZYD_FILTER_PS_POLL (1 << 26) +#define ZYD_FILTER_RTS (1 << 27) +#define ZYD_FILTER_CTS (1 << 28) +#define ZYD_FILTER_ACK (1 << 29) +#define ZYD_FILTER_CFE (1 << 30) +#define ZYD_FILTER_CFE_A (1U << 31) + +/* helpers for register ZYD_MAC_RXFILTER */ +#define ZYD_FILTER_MONITOR 0xffffffff +#define ZYD_FILTER_BSS \ + (ZYD_FILTER_ASS_REQ | ZYD_FILTER_ASS_RSP | \ + ZYD_FILTER_REASS_REQ | ZYD_FILTER_REASS_RSP | \ + ZYD_FILTER_PRB_REQ | ZYD_FILTER_PRB_RSP | \ + (0x3 << 6) | \ + ZYD_FILTER_BCN | ZYD_FILTER_ATIM | ZYD_FILTER_DEASS | \ + ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH | \ + (0x7 << 13) | \ + ZYD_FILTER_PS_POLL | ZYD_FILTER_ACK) +#define ZYD_FILTER_HOSTAP \ + (ZYD_FILTER_ASS_REQ | ZYD_FILTER_REASS_REQ | \ + ZYD_FILTER_PRB_REQ | ZYD_FILTER_DEASS | ZYD_FILTER_AUTH | \ + ZYD_FILTER_DEAUTH | ZYD_FILTER_PS_POLL) + +struct zyd_tx_desc { + uint8_t phy; +#define ZYD_TX_PHY_SIGNAL(x) ((x) & 0xf) +#define ZYD_TX_PHY_OFDM (1 << 4) +#define ZYD_TX_PHY_SHPREAMBLE (1 << 5) /* CCK */ +#define ZYD_TX_PHY_5GHZ (1 << 5) /* OFDM */ + uint16_t len; + uint8_t flags; +#define ZYD_TX_FLAG_BACKOFF (1 << 0) +#define ZYD_TX_FLAG_MULTICAST (1 << 1) +#define ZYD_TX_FLAG_TYPE(x) (((x) & 0x3) << 2) +#define ZYD_TX_TYPE_DATA 0 +#define ZYD_TX_TYPE_PS_POLL 1 +#define ZYD_TX_TYPE_MGMT 2 +#define ZYD_TX_TYPE_CTL 3 +#define ZYD_TX_FLAG_WAKEUP (1 << 4) +#define ZYD_TX_FLAG_RTS (1 << 5) +#define ZYD_TX_FLAG_ENCRYPT (1 << 6) +#define ZYD_TX_FLAG_CTS_TO_SELF (1 << 7) + uint16_t pktlen; + uint16_t plcp_length; + uint8_t plcp_service; +#define ZYD_PLCP_LENGEXT 0x80 + uint16_t nextlen; +} __packed; + +struct zyd_plcphdr { + uint8_t signal; + uint8_t reserved[2]; + uint16_t service; /* unaligned! */ +} __packed; + +struct zyd_rx_stat { + uint8_t signal_cck; + uint8_t rssi; + uint8_t signal_ofdm; + uint8_t cipher; +#define ZYD_RX_CIPHER_WEP64 1 +#define ZYD_RX_CIPHER_TKIP 2 +#define ZYD_RX_CIPHER_AES 4 +#define ZYD_RX_CIPHER_WEP128 5 +#define ZYD_RX_CIPHER_WEP256 6 +#define ZYD_RX_CIPHER_WEP \ + (ZYD_RX_CIPHER_WEP64 | ZYD_RX_CIPHER_WEP128 | ZYD_RX_CIPHER_WEP256) + uint8_t flags; +#define ZYD_RX_OFDM (1 << 0) +#define ZYD_RX_TIMEOUT (1 << 1) +#define ZYD_RX_OVERRUN (1 << 2) +#define ZYD_RX_DECRYPTERR (1 << 3) +#define ZYD_RX_BADCRC32 (1 << 4) +#define ZYD_RX_NOT2ME (1 << 5) +#define ZYD_RX_BADCRC16 (1 << 6) +#define ZYD_RX_ERROR (1 << 7) +} __packed; + +/* this structure may be unaligned */ +struct zyd_rx_desc { +#define ZYD_MAX_RXFRAMECNT 3 + uWord len[ZYD_MAX_RXFRAMECNT]; + uWord tag; +#define ZYD_TAG_MULTIFRAME 0x697e +} __packed; + +/* I2C bus alike */ +struct zyd_rfwrite_cmd { + uint16_t code; + uint16_t width; + uint16_t bit[32]; +#define ZYD_RF_IF_LE (1 << 1) +#define ZYD_RF_CLK (1 << 2) +#define ZYD_RF_DATA (1 << 3) +} __packed; + +struct zyd_cmd { + uint16_t code; +#define ZYD_CMD_IOWR 0x0021 /* write HMAC or PHY register */ +#define ZYD_CMD_IORD 0x0022 /* read HMAC or PHY register */ +#define ZYD_CMD_RFCFG 0x0023 /* write RF register */ +#define ZYD_NOTIF_IORD 0x9001 /* response for ZYD_CMD_IORD */ +#define ZYD_NOTIF_MACINTR 0x9001 /* interrupt notification */ +#define ZYD_NOTIF_RETRYSTATUS 0xa001 /* Tx retry notification */ + uint8_t data[64]; +} __packed; + +/* structure for command ZYD_CMD_IOWR */ +struct zyd_pair { + uint16_t reg; +/* helpers macros to read/write 32-bit registers */ +#define ZYD_REG32_LO(reg) (reg) +#define ZYD_REG32_HI(reg) \ + ((reg) + ((((reg) & 0xf000) == 0x9000) ? 2 : 1)) + uint16_t val; +} __packed; + +/* structure for notification ZYD_NOTIF_RETRYSTATUS */ +struct zyd_notif_retry { + uint16_t rate; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint16_t count; +} __packed; + +#define ZYD_CONFIG_INDEX 0 +#define ZYD_IFACE_INDEX 0 + +#define ZYD_INTR_TIMEOUT 1000 +#define ZYD_TX_TIMEOUT 10000 + +#define ZYD_MAX_TXBUFSZ \ + (sizeof(struct zyd_tx_desc) + MCLBYTES) +#define ZYD_MIN_FRAGSZ \ + (sizeof(struct zyd_plcphdr) + IEEE80211_MIN_LEN + \ + sizeof(struct zyd_rx_stat)) +#define ZYD_MIN_RXBUFSZ ZYD_MIN_FRAGSZ +#define ZYX_MAX_RXBUFSZ \ + ((sizeof (struct zyd_plcphdr) + IEEE80211_MAX_LEN + \ + sizeof (struct zyd_rx_stat)) * ZYD_MAX_RXFRAMECNT + \ + sizeof (struct zyd_rx_desc)) +#define ZYD_TX_DESC_SIZE (sizeof (struct zyd_tx_desc)) + +#define ZYD_RX_LIST_CNT 1 +#define ZYD_TX_LIST_CNT 5 +#define ZYD_CMD_FLAG_READ (1 << 0) +#define ZYD_CMD_FLAG_SENT (1 << 1) + +/* quickly determine if a given rate is CCK or OFDM */ +#define ZYD_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) + +struct zyd_phy_pair { + uint16_t reg; + uint8_t val; +}; + +struct zyd_mac_pair { + uint16_t reg; + uint32_t val; +}; + +struct zyd_tx_data { + STAILQ_ENTRY(zyd_tx_data) next; + struct zyd_softc *sc; + struct zyd_tx_desc desc; + struct mbuf *m; + struct ieee80211_node *ni; + int rate; +}; +typedef STAILQ_HEAD(, zyd_tx_data) zyd_txdhead; + +struct zyd_rx_data { + struct mbuf *m; + int rssi; +}; + +struct zyd_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_antsignal; + int8_t wr_antnoise; +} __packed __aligned(8); + +#define ZYD_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct zyd_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed __aligned(8); + +#define ZYD_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct zyd_softc; /* forward declaration */ + +struct zyd_rf { + /* RF methods */ + int (*init)(struct zyd_rf *); + int (*switch_radio)(struct zyd_rf *, int); + int (*set_channel)(struct zyd_rf *, uint8_t); + int (*bandedge6)(struct zyd_rf *, + struct ieee80211_channel *); + /* RF attributes */ + struct zyd_softc *rf_sc; /* back-pointer */ + int width; + int idx; /* for GIT RF */ + int update_pwr; +}; + +struct zyd_rq { + struct zyd_cmd *cmd; + const uint16_t *idata; + struct zyd_pair *odata; + int ilen; + int olen; + int flags; + STAILQ_ENTRY(zyd_rq) rq; +}; + +struct zyd_vap { + struct ieee80211vap vap; + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define ZYD_VAP(vap) ((struct zyd_vap *)(vap)) + +enum { + ZYD_BULK_WR, + ZYD_BULK_RD, + ZYD_INTR_WR, + ZYD_INTR_RD, + ZYD_N_TRANSFER = 4, +}; + +struct zyd_softc { + struct ieee80211com sc_ic; + struct ieee80211_ratectl_tx_status sc_txs; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + + struct usb_xfer *sc_xfer[ZYD_N_TRANSFER]; + + int sc_flags; +#define ZYD_FLAG_FWLOADED (1 << 0) +#define ZYD_FLAG_INITONCE (1 << 1) +#define ZYD_FLAG_INITDONE (1 << 2) +#define ZYD_FLAG_DETACHED (1 << 3) +#define ZYD_FLAG_RUNNING (1 << 4) + + struct zyd_rf sc_rf; + + STAILQ_HEAD(, zyd_rq) sc_rtx; + STAILQ_HEAD(, zyd_rq) sc_rqh; + + uint16_t sc_fwbase; + uint8_t sc_regdomain; + uint8_t sc_macrev; + uint16_t sc_fwrev; + uint8_t sc_rfrev; + uint8_t sc_parev; + uint8_t sc_al2230s; + uint8_t sc_bandedge6; + uint8_t sc_newphy; + uint8_t sc_cckgain; + uint8_t sc_fix_cr157; + uint8_t sc_ledtype; + uint8_t sc_txled; + + uint32_t sc_atim_wnd; + uint32_t sc_pre_tbtt; + uint32_t sc_bcn_int; + + uint8_t sc_pwrcal[14]; + uint8_t sc_pwrint[14]; + uint8_t sc_ofdm36_cal[14]; + uint8_t sc_ofdm48_cal[14]; + uint8_t sc_ofdm54_cal[14]; + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + + struct mtx sc_mtx; + struct zyd_tx_data tx_data[ZYD_TX_LIST_CNT]; + zyd_txdhead tx_q; + zyd_txdhead tx_free; + int tx_nfree; + struct zyd_rx_desc sc_rx_desc; + struct zyd_rx_data sc_rx_data[ZYD_MAX_RXFRAMECNT]; + int sc_rx_count; + + struct zyd_cmd sc_ibuf; + + struct zyd_rx_radiotap_header sc_rxtap; + struct zyd_tx_radiotap_header sc_txtap; +}; + +#define ZYD_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define ZYD_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define ZYD_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) + |