diff --git a/sys/dev/otus/if_otus.c b/sys/dev/otus/if_otus.c index aee765ff4ac8..352af15d17cf 100644 --- a/sys/dev/otus/if_otus.c +++ b/sys/dev/otus/if_otus.c @@ -1,3316 +1,3313 @@ /* $OpenBSD: if_otus.c,v 1.49 2015/11/24 13:33:18 mpi Exp $ */ /*- * Copyright (c) 2009 Damien Bergamini * Copyright (c) 2015 Adrian Chadd * * 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. */ /* * Driver for Atheros AR9001U chipset. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #include #include #include "usbdevs.h" #define USB_DEBUG_VAR otus_debug #include #include "if_otusreg.h" static int otus_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, otus, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB otus"); SYSCTL_INT(_hw_usb_otus, OID_AUTO, debug, CTLFLAG_RWTUN, &otus_debug, 0, "Debug level"); #define OTUS_DEBUG_XMIT 0x00000001 #define OTUS_DEBUG_RECV 0x00000002 #define OTUS_DEBUG_TXDONE 0x00000004 #define OTUS_DEBUG_RXDONE 0x00000008 #define OTUS_DEBUG_CMD 0x00000010 #define OTUS_DEBUG_CMDDONE 0x00000020 #define OTUS_DEBUG_RESET 0x00000040 #define OTUS_DEBUG_STATE 0x00000080 #define OTUS_DEBUG_CMDNOTIFY 0x00000100 #define OTUS_DEBUG_REGIO 0x00000200 #define OTUS_DEBUG_IRQ 0x00000400 #define OTUS_DEBUG_TXCOMP 0x00000800 #define OTUS_DEBUG_RX_BUFFER 0x00001000 #define OTUS_DEBUG_ANY 0xffffffff #define OTUS_DPRINTF(sc, dm, ...) \ do { \ if ((dm == OTUS_DEBUG_ANY) || (dm & otus_debug)) \ device_printf(sc->sc_dev, __VA_ARGS__); \ } while (0) #define OTUS_DEV(v, p) { USB_VPI(v, p, 0) } static const STRUCT_USB_HOST_ID otus_devs[] = { OTUS_DEV(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_WN7512), OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_3CRUSBN275), OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_TG121N), OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9170), OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_WN612), OTUS_DEV(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_WN821NV2), OTUS_DEV(USB_VENDOR_AVM, USB_PRODUCT_AVM_FRITZWLAN), OTUS_DEV(USB_VENDOR_CACE, USB_PRODUCT_CACE_AIRPCAPNX), OTUS_DEV(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA130D1), OTUS_DEV(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA160A1), OTUS_DEV(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA160A2), OTUS_DEV(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_WNGDNUS2), OTUS_DEV(USB_VENDOR_NEC, USB_PRODUCT_NEC_WL300NUG), OTUS_DEV(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WN111V2), OTUS_DEV(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNA1000), OTUS_DEV(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNDA3100), OTUS_DEV(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US300), OTUS_DEV(USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_O8494), OTUS_DEV(USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_WNC0600), OTUS_DEV(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_UB81), OTUS_DEV(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_UB82), OTUS_DEV(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1221), OTUS_DEV(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_NWD271N), }; static device_probe_t otus_match; static device_attach_t otus_attach; static device_detach_t otus_detach; static int otus_attachhook(struct otus_softc *); static void otus_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel[]); int otus_load_firmware(struct otus_softc *, const char *, uint32_t); int otus_open_pipes(struct otus_softc *); void otus_close_pipes(struct otus_softc *); static int otus_alloc_tx_cmd_list(struct otus_softc *); static void otus_free_tx_cmd_list(struct otus_softc *); static int otus_alloc_rx_list(struct otus_softc *); static void otus_free_rx_list(struct otus_softc *); static int otus_alloc_tx_list(struct otus_softc *); static void otus_free_tx_list(struct otus_softc *); static void otus_free_list(struct otus_softc *, struct otus_data [], int); static struct otus_data *_otus_getbuf(struct otus_softc *); static struct otus_data *otus_getbuf(struct otus_softc *); static void otus_freebuf(struct otus_softc *, struct otus_data *); static struct otus_tx_cmd *_otus_get_txcmd(struct otus_softc *); static struct otus_tx_cmd *otus_get_txcmd(struct otus_softc *); static void otus_free_txcmd(struct otus_softc *, struct otus_tx_cmd *); void otus_next_scan(void *, int); static void otus_tx_task(void *, int pending); void otus_do_async(struct otus_softc *, void (*)(struct otus_softc *, void *), void *, int); int otus_newstate(struct ieee80211vap *, enum ieee80211_state, int); int otus_cmd(struct otus_softc *, uint8_t, const void *, int, void *, int); void otus_write(struct otus_softc *, uint32_t, uint32_t); int otus_write_barrier(struct otus_softc *); static struct ieee80211_node *otus_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]); int otus_read_eeprom(struct otus_softc *); void otus_newassoc(struct ieee80211_node *, int); void otus_cmd_rxeof(struct otus_softc *, uint8_t *, int); void otus_sub_rxeof(struct otus_softc *, uint8_t *, int, struct mbufq *); static int otus_tx(struct otus_softc *, struct ieee80211_node *, struct mbuf *, struct otus_data *, const struct ieee80211_bpf_params *); int otus_ioctl(if_t, u_long, caddr_t); int otus_set_multi(struct otus_softc *); static int otus_updateedca(struct ieee80211com *); static void otus_updateedca_locked(struct otus_softc *); static void otus_updateslot(struct otus_softc *); static void otus_set_operating_mode(struct otus_softc *sc); static void otus_set_rx_filter(struct otus_softc *sc); int otus_init_mac(struct otus_softc *); uint32_t otus_phy_get_def(struct otus_softc *, uint32_t); int otus_set_board_values(struct otus_softc *, struct ieee80211_channel *); int otus_program_phy(struct otus_softc *, struct ieee80211_channel *); int otus_set_rf_bank4(struct otus_softc *, struct ieee80211_channel *); void otus_get_delta_slope(uint32_t, uint32_t *, uint32_t *); static int otus_set_chan(struct otus_softc *, struct ieee80211_channel *, int); int otus_set_key(struct ieee80211com *, struct ieee80211_node *, struct ieee80211_key *); void otus_set_key_cb(struct otus_softc *, void *); void otus_delete_key(struct ieee80211com *, struct ieee80211_node *, struct ieee80211_key *); void otus_delete_key_cb(struct otus_softc *, void *); void otus_calibrate_to(void *, int); int otus_set_bssid(struct otus_softc *, const uint8_t *); int otus_set_macaddr(struct otus_softc *, const uint8_t *); void otus_led_newstate_type1(struct otus_softc *); void otus_led_newstate_type2(struct otus_softc *); void otus_led_newstate_type3(struct otus_softc *); int otus_init(struct otus_softc *sc); void otus_stop(struct otus_softc *sc); static device_method_t otus_methods[] = { DEVMETHOD(device_probe, otus_match), DEVMETHOD(device_attach, otus_attach), DEVMETHOD(device_detach, otus_detach), DEVMETHOD_END }; static driver_t otus_driver = { .name = "otus", .methods = otus_methods, .size = sizeof(struct otus_softc) }; DRIVER_MODULE(otus, uhub, otus_driver, NULL, NULL); MODULE_DEPEND(otus, wlan, 1, 1, 1); MODULE_DEPEND(otus, usb, 1, 1, 1); MODULE_DEPEND(otus, firmware, 1, 1, 1); MODULE_VERSION(otus, 1); static usb_callback_t otus_bulk_tx_callback; static usb_callback_t otus_bulk_rx_callback; static usb_callback_t otus_bulk_irq_callback; static usb_callback_t otus_bulk_cmd_callback; static const struct usb_config otus_config[OTUS_N_XFER] = { [OTUS_BULK_TX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = 0x200, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = otus_bulk_tx_callback, .timeout = 5000, /* ms */ }, [OTUS_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = OTUS_RXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1,.short_xfer_ok = 1,}, .callback = otus_bulk_rx_callback, }, [OTUS_BULK_IRQ] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = OTUS_MAX_CTRLSZ, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = otus_bulk_irq_callback, }, [OTUS_BULK_CMD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = OTUS_MAX_CTRLSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = otus_bulk_cmd_callback, .timeout = 5000, /* ms */ }, }; static int otus_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(otus_devs, sizeof(otus_devs), uaa)); } static int otus_attach(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); struct otus_softc *sc = device_get_softc(self); int error; uint8_t iface_index; 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); TIMEOUT_TASK_INIT(taskqueue_thread, &sc->scan_to, 0, otus_next_scan, sc); TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_to, 0, otus_calibrate_to, sc); TASK_INIT(&sc->tx_task, 0, otus_tx_task, sc); mbufq_init(&sc->sc_snd, ifqmaxlen); iface_index = 0; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, otus_config, OTUS_N_XFER, 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; } if ((error = otus_open_pipes(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not open pipes\n", __func__); goto fail; } /* XXX check return status; fail out if appropriate */ if (otus_attachhook(sc) != 0) goto fail; return (0); fail: otus_close_pipes(sc); fail_usb: mtx_destroy(&sc->sc_mtx); return (ENXIO); } static int otus_detach(device_t self) { struct otus_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; otus_stop(sc); usbd_transfer_unsetup(sc->sc_xfer, OTUS_N_XFER); taskqueue_drain_timeout(taskqueue_thread, &sc->scan_to); taskqueue_drain_timeout(taskqueue_thread, &sc->calib_to); taskqueue_drain(taskqueue_thread, &sc->tx_task); otus_close_pipes(sc); #if 0 /* Wait for all queued asynchronous commands to complete. */ usb_rem_wait_task(sc->sc_udev, &sc->sc_task); usbd_ref_wait(sc->sc_udev); #endif ieee80211_ifdetach(ic); mtx_destroy(&sc->sc_mtx); return 0; } static void otus_delay_ms(struct otus_softc *sc, int ms) { DELAY(1000 * ms); } static struct ieee80211vap * otus_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 otus_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); uvp = malloc(sizeof(struct otus_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); } /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = otus_newstate; vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_8; vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; ieee80211_ratectl_init(vap); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void otus_vap_delete(struct ieee80211vap *vap) { struct otus_vap *uvp = OTUS_VAP(vap); ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void otus_parent(struct ieee80211com *ic) { struct otus_softc *sc = ic->ic_softc; int startall = 0; if (ic->ic_nrunning > 0) { if (!sc->sc_running) { otus_init(sc); startall = 1; } else { (void) otus_set_multi(sc); } } else if (sc->sc_running) otus_stop(sc); if (startall) ieee80211_start_all(ic); } static void otus_drain_mbufq(struct otus_softc *sc) { struct mbuf *m; struct ieee80211_node *ni; OTUS_LOCK_ASSERT(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 otus_tx_start(struct otus_softc *sc) { taskqueue_enqueue(taskqueue_thread, &sc->tx_task); } static int otus_transmit(struct ieee80211com *ic, struct mbuf *m) { struct otus_softc *sc = ic->ic_softc; int error; OTUS_LOCK(sc); if (! sc->sc_running) { OTUS_UNLOCK(sc); return (ENXIO); } /* XXX TODO: handle fragments */ error = mbufq_enqueue(&sc->sc_snd, m); if (error) { OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, "%s: mbufq_enqueue failed: %d\n", __func__, error); OTUS_UNLOCK(sc); return (error); } OTUS_UNLOCK(sc); /* Kick TX */ otus_tx_start(sc); return (0); } static void _otus_start(struct otus_softc *sc) { struct ieee80211_node *ni; struct otus_data *bf; struct mbuf *m; OTUS_LOCK_ASSERT(sc); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { bf = otus_getbuf(sc); if (bf == NULL) { OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, "%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 (otus_tx(sc, ni, m, bf, NULL) != 0) { OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, "%s: failed to transmit\n", __func__); if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); otus_freebuf(sc, bf); ieee80211_free_node(ni); m_freem(m); break; } } } static void otus_tx_task(void *arg, int pending) { struct otus_softc *sc = arg; OTUS_LOCK(sc); _otus_start(sc); OTUS_UNLOCK(sc); } static int otus_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic= ni->ni_ic; struct otus_softc *sc = ic->ic_softc; struct otus_data *bf = NULL; int error = 0; /* Don't transmit if we're not running */ OTUS_LOCK(sc); if (! sc->sc_running) { error = ENETDOWN; goto error; } bf = otus_getbuf(sc); if (bf == NULL) { error = ENOBUFS; goto error; } if (otus_tx(sc, ni, m, bf, params) != 0) { error = EIO; goto error; } OTUS_UNLOCK(sc); return (0); error: if (bf) otus_freebuf(sc, bf); OTUS_UNLOCK(sc); m_freem(m); return (error); } static void otus_update_chw(struct ieee80211com *ic) { printf("%s: TODO\n", __func__); } static void otus_set_channel(struct ieee80211com *ic) { struct otus_softc *sc = ic->ic_softc; OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "%s: set channel: %d\n", __func__, ic->ic_curchan->ic_freq); OTUS_LOCK(sc); (void) otus_set_chan(sc, ic->ic_curchan, 0); OTUS_UNLOCK(sc); } static int otus_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { /* For now, no A-MPDU TX support in the driver */ return (0); } static void otus_scan_start(struct ieee80211com *ic) { // printf("%s: TODO\n", __func__); } static void otus_scan_end(struct ieee80211com *ic) { // printf("%s: TODO\n", __func__); } static void otus_update_mcast(struct ieee80211com *ic) { struct otus_softc *sc = ic->ic_softc; (void) otus_set_multi(sc); } static int otus_attachhook(struct otus_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; usb_device_request_t req; uint32_t in, out; int error; /* Not locked */ error = otus_load_firmware(sc, "otusfw_init", AR_FW_INIT_ADDR); if (error != 0) { device_printf(sc->sc_dev, "%s: could not load %s firmware\n", __func__, "init"); return (ENXIO); } /* XXX not locked? */ otus_delay_ms(sc, 1000); /* Not locked */ error = otus_load_firmware(sc, "otusfw_main", AR_FW_MAIN_ADDR); if (error != 0) { device_printf(sc->sc_dev, "%s: could not load %s firmware\n", __func__, "main"); return (ENXIO); } OTUS_LOCK(sc); /* Tell device that firmware transfer is complete. */ req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = AR_FW_DOWNLOAD_COMPLETE; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 0); if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, NULL, 0, NULL, 250) != 0) { OTUS_UNLOCK(sc); device_printf(sc->sc_dev, "%s: firmware initialization failed\n", __func__); return (ENXIO); } /* Send an ECHO command to check that everything is settled. */ in = 0xbadc0ffe; if (otus_cmd(sc, AR_CMD_ECHO, &in, sizeof in, &out, sizeof(out)) != 0) { OTUS_UNLOCK(sc); device_printf(sc->sc_dev, "%s: echo command failed\n", __func__); return (ENXIO); } if (in != out) { OTUS_UNLOCK(sc); device_printf(sc->sc_dev, "%s: echo reply mismatch: 0x%08x!=0x%08x\n", __func__, in, out); return (ENXIO); } /* Read entire EEPROM. */ if (otus_read_eeprom(sc) != 0) { OTUS_UNLOCK(sc); device_printf(sc->sc_dev, "%s: could not read EEPROM\n", __func__); return (ENXIO); } OTUS_UNLOCK(sc); sc->txmask = sc->eeprom.baseEepHeader.txMask; sc->rxmask = sc->eeprom.baseEepHeader.rxMask; sc->capflags = sc->eeprom.baseEepHeader.opCapFlags; IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->eeprom.baseEepHeader.macAddr); sc->sc_led_newstate = otus_led_newstate_type3; /* XXX */ if (sc->txmask == 0x5) ic->ic_txstream = 2; else ic->ic_txstream = 1; if (sc->rxmask == 0x5) ic->ic_rxstream = 2; else ic->ic_rxstream = 1; device_printf(sc->sc_dev, "MAC/BBP AR9170, RF AR%X, MIMO %dT%dR, address %s\n", (sc->capflags & AR5416_OPFLAGS_11A) ? 0x9104 : ((sc->txmask == 0x5) ? 0x9102 : 0x9101), (sc->txmask == 0x5) ? 2 : 1, (sc->rxmask == 0x5) ? 2 : 1, ether_sprintf(ic->ic_macaddr)); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(sc->sc_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 */ #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_FF | /* Atheros fast-frames supported. */ IEEE80211_C_MONITOR | /* Enable monitor mode */ IEEE80211_C_SWAMSDUTX | /* Do software A-MSDU TX */ IEEE80211_C_WPA; /* WPA/RSN. */ ic->ic_htcaps = IEEE80211_HTC_HT | #if 0 IEEE80211_HTC_AMPDU | #endif IEEE80211_HTC_AMSDU | IEEE80211_HTCAP_MAXAMSDU_3839 | IEEE80211_HTCAP_SMPS_OFF; otus_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); ieee80211_ifattach(ic); ic->ic_raw_xmit = otus_raw_xmit; ic->ic_scan_start = otus_scan_start; ic->ic_scan_end = otus_scan_end; ic->ic_set_channel = otus_set_channel; ic->ic_getradiocaps = otus_getradiocaps; ic->ic_vap_create = otus_vap_create; ic->ic_vap_delete = otus_vap_delete; ic->ic_update_mcast = otus_update_mcast; ic->ic_update_promisc = otus_update_mcast; ic->ic_parent = otus_parent; ic->ic_transmit = otus_transmit; ic->ic_update_chw = otus_update_chw; ic->ic_ampdu_enable = otus_ampdu_enable; ic->ic_wme.wme_update = otus_updateedca; ic->ic_newassoc = otus_newassoc; ic->ic_node_alloc = otus_node_alloc; #ifdef notyet ic->ic_set_key = otus_set_key; ic->ic_delete_key = otus_delete_key; #endif ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), OTUS_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), OTUS_RX_RADIOTAP_PRESENT); return (0); } static void otus_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { struct otus_softc *sc = ic->ic_softc; uint8_t bands[IEEE80211_MODE_BYTES]; /* Set supported .11b and .11g rates. */ memset(bands, 0, sizeof(bands)); if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) { setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); setbit(bands, IEEE80211_MODE_11NG); ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, ar_chans, 14, bands, 0); } if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) { setbit(bands, IEEE80211_MODE_11A); setbit(bands, IEEE80211_MODE_11NA); ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, &ar_chans[14], nitems(ar_chans) - 14, bands, 0); } } int otus_load_firmware(struct otus_softc *sc, const char *name, uint32_t addr) { usb_device_request_t req; char *ptr; const struct firmware *fw; int mlen, error, size; error = 0; /* Read firmware image from the filesystem. */ if ((fw = firmware_get(name)) == NULL) { device_printf(sc->sc_dev, "%s: failed loadfirmware of file %s\n", __func__, name); return (ENXIO); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = AR_FW_DOWNLOAD; USETW(req.wIndex, 0); OTUS_LOCK(sc); /* XXX const */ ptr = __DECONST(char *, fw->data); size = fw->datasize; addr >>= 8; while (size > 0) { mlen = MIN(size, 4096); USETW(req.wValue, addr); USETW(req.wLength, mlen); if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, ptr, 0, NULL, 250) != 0) { error = EIO; break; } addr += mlen >> 8; ptr += mlen; size -= mlen; } OTUS_UNLOCK(sc); firmware_put(fw, FIRMWARE_UNLOAD); if (error != 0) device_printf(sc->sc_dev, "%s: %s: error=%d\n", __func__, name, error); return error; } int otus_open_pipes(struct otus_softc *sc) { #if 0 int isize, error; int i; #endif int error; OTUS_UNLOCK_ASSERT(sc); if ((error = otus_alloc_tx_cmd_list(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not allocate command xfer\n", __func__); goto fail; } if ((error = otus_alloc_tx_list(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not allocate Tx xfers\n", __func__); goto fail; } if ((error = otus_alloc_rx_list(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not allocate Rx xfers\n", __func__); goto fail; } /* Enable RX transfers; needed for initial firmware messages */ OTUS_LOCK(sc); usbd_transfer_start(sc->sc_xfer[OTUS_BULK_RX]); usbd_transfer_start(sc->sc_xfer[OTUS_BULK_IRQ]); OTUS_UNLOCK(sc); return 0; fail: otus_close_pipes(sc); return error; } void otus_close_pipes(struct otus_softc *sc) { OTUS_LOCK(sc); otus_free_tx_cmd_list(sc); otus_free_tx_list(sc); otus_free_rx_list(sc); OTUS_UNLOCK(sc); usbd_transfer_unsetup(sc->sc_xfer, OTUS_N_XFER); } static void otus_free_cmd_list(struct otus_softc *sc, struct otus_tx_cmd cmd[], int ndata) { int i; /* XXX TODO: someone has to have waken up waiters! */ for (i = 0; i < ndata; i++) { struct otus_tx_cmd *dp = &cmd[i]; if (dp->buf != NULL) { free(dp->buf, M_USBDEV); dp->buf = NULL; } } } static int otus_alloc_cmd_list(struct otus_softc *sc, struct otus_tx_cmd cmd[], int ndata, int maxsz) { int i, error; for (i = 0; i < ndata; i++) { struct otus_tx_cmd *dp = &cmd[i]; dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT | M_ZERO); dp->odata = NULL; if (dp->buf == NULL) { device_printf(sc->sc_dev, "could not allocate buffer\n"); error = ENOMEM; goto fail; } } return (0); fail: otus_free_cmd_list(sc, cmd, ndata); return (error); } static int otus_alloc_tx_cmd_list(struct otus_softc *sc) { int error, i; error = otus_alloc_cmd_list(sc, sc->sc_cmd, OTUS_CMD_LIST_COUNT, OTUS_MAX_TXCMDSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_cmd_active); STAILQ_INIT(&sc->sc_cmd_inactive); STAILQ_INIT(&sc->sc_cmd_pending); STAILQ_INIT(&sc->sc_cmd_waiting); for (i = 0; i < OTUS_CMD_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_cmd_inactive, &sc->sc_cmd[i], next_cmd); return (0); } static void otus_free_tx_cmd_list(struct otus_softc *sc) { /* * XXX TODO: something needs to wake up any pending/sleeping * waiters! */ STAILQ_INIT(&sc->sc_cmd_active); STAILQ_INIT(&sc->sc_cmd_inactive); STAILQ_INIT(&sc->sc_cmd_pending); STAILQ_INIT(&sc->sc_cmd_waiting); otus_free_cmd_list(sc, sc->sc_cmd, OTUS_CMD_LIST_COUNT); } static int otus_alloc_list(struct otus_softc *sc, struct otus_data data[], int ndata, int maxsz) { int i, error; for (i = 0; i < ndata; i++) { struct otus_data *dp = &data[i]; dp->sc = sc; dp->m = NULL; dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT | M_ZERO); if (dp->buf == NULL) { device_printf(sc->sc_dev, "could not allocate buffer\n"); error = ENOMEM; goto fail; } dp->ni = NULL; } return (0); fail: otus_free_list(sc, data, ndata); return (error); } static int otus_alloc_rx_list(struct otus_softc *sc) { int error, i; error = otus_alloc_list(sc, sc->sc_rx, OTUS_RX_LIST_COUNT, OTUS_RXBUFSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < OTUS_RX_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); return (0); } static int otus_alloc_tx_list(struct otus_softc *sc) { int error, i; error = otus_alloc_list(sc, sc->sc_tx, OTUS_TX_LIST_COUNT, OTUS_TXBUFSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_tx_inactive); for (i = 0; i != OTUS_N_XFER; i++) { STAILQ_INIT(&sc->sc_tx_active[i]); STAILQ_INIT(&sc->sc_tx_pending[i]); } for (i = 0; i < OTUS_TX_LIST_COUNT; i++) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); } return (0); } static void otus_free_tx_list(struct otus_softc *sc) { int i; /* prevent further allocations from TX list(s) */ STAILQ_INIT(&sc->sc_tx_inactive); for (i = 0; i != OTUS_N_XFER; i++) { STAILQ_INIT(&sc->sc_tx_active[i]); STAILQ_INIT(&sc->sc_tx_pending[i]); } otus_free_list(sc, sc->sc_tx, OTUS_TX_LIST_COUNT); } static void otus_free_rx_list(struct otus_softc *sc) { /* prevent further allocations from RX list(s) */ STAILQ_INIT(&sc->sc_rx_inactive); STAILQ_INIT(&sc->sc_rx_active); otus_free_list(sc, sc->sc_rx, OTUS_RX_LIST_COUNT); } static void otus_free_list(struct otus_softc *sc, struct otus_data data[], int ndata) { int i; for (i = 0; i < ndata; i++) { struct otus_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 otus_data * _otus_getbuf(struct otus_softc *sc) { struct otus_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); else bf = NULL; /* XXX bzero? */ return (bf); } static struct otus_data * otus_getbuf(struct otus_softc *sc) { struct otus_data *bf; OTUS_LOCK_ASSERT(sc); bf = _otus_getbuf(sc); return (bf); } static void otus_freebuf(struct otus_softc *sc, struct otus_data *bf) { OTUS_LOCK_ASSERT(sc); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, bf, next); } static struct otus_tx_cmd * _otus_get_txcmd(struct otus_softc *sc) { struct otus_tx_cmd *bf; bf = STAILQ_FIRST(&sc->sc_cmd_inactive); if (bf != NULL) STAILQ_REMOVE_HEAD(&sc->sc_cmd_inactive, next_cmd); else bf = NULL; return (bf); } static struct otus_tx_cmd * otus_get_txcmd(struct otus_softc *sc) { struct otus_tx_cmd *bf; OTUS_LOCK_ASSERT(sc); bf = _otus_get_txcmd(sc); if (bf == NULL) { device_printf(sc->sc_dev, "%s: no tx cmd buffers\n", __func__); } return (bf); } static void otus_free_txcmd(struct otus_softc *sc, struct otus_tx_cmd *bf) { OTUS_LOCK_ASSERT(sc); STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, bf, next_cmd); } void otus_next_scan(void *arg, int pending) { #if 0 struct otus_softc *sc = arg; if (usbd_is_dying(sc->sc_udev)) return; usbd_ref_incr(sc->sc_udev); if (sc->sc_ic.ic_state == IEEE80211_S_SCAN) ieee80211_next_scan(&sc->sc_ic.ic_if); usbd_ref_decr(sc->sc_udev); #endif } int otus_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct otus_vap *uvp = OTUS_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct otus_softc *sc = ic->ic_softc; enum ieee80211_state ostate; ostate = vap->iv_state; OTUS_DPRINTF(sc, OTUS_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); OTUS_LOCK(sc); /* XXX TODO: more fleshing out! */ switch (nstate) { case IEEE80211_S_INIT: otus_set_operating_mode(sc); otus_set_rx_filter(sc); break; case IEEE80211_S_RUN: if (ic->ic_opmode == IEEE80211_M_STA) { otus_updateslot(sc); otus_set_operating_mode(sc); otus_set_rx_filter(sc); /* Start calibration timer. */ taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_to, hz); } break; default: break; } /* XXX TODO: calibration? */ sc->sc_led_newstate(sc); OTUS_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } int otus_cmd(struct otus_softc *sc, uint8_t code, const void *idata, int ilen, void *odata, int odatalen) { struct otus_tx_cmd *cmd; struct ar_cmd_hdr *hdr; int xferlen, error; OTUS_LOCK_ASSERT(sc); /* Always bulk-out a multiple of 4 bytes. */ xferlen = (sizeof (*hdr) + ilen + 3) & ~3; if (xferlen > OTUS_MAX_TXCMDSZ) { device_printf(sc->sc_dev, "%s: command (0x%02x) size (%d) > %d\n", __func__, code, xferlen, OTUS_MAX_TXCMDSZ); return (EIO); } cmd = otus_get_txcmd(sc); if (cmd == NULL) { device_printf(sc->sc_dev, "%s: failed to get buf\n", __func__); return (EIO); } hdr = (struct ar_cmd_hdr *)cmd->buf; hdr->code = code; hdr->len = ilen; hdr->token = ++sc->token; /* Don't care about endianness. */ cmd->token = hdr->token; /* XXX TODO: check max cmd length? */ memcpy((uint8_t *)&hdr[1], idata, ilen); OTUS_DPRINTF(sc, OTUS_DEBUG_CMD, "%s: sending command code=0x%02x len=%d token=%d\n", __func__, code, ilen, hdr->token); cmd->odata = odata; cmd->odatalen = odatalen; cmd->buflen = xferlen; /* Queue the command to the endpoint */ STAILQ_INSERT_TAIL(&sc->sc_cmd_pending, cmd, next_cmd); usbd_transfer_start(sc->sc_xfer[OTUS_BULK_CMD]); /* Sleep on the command; wait for it to complete */ error = msleep(cmd, &sc->sc_mtx, PCATCH, "otuscmd", hz); /* * At this point we don't own cmd any longer; it'll be * freed by the cmd bulk path or the RX notification * path. If the data is made available then it'll be copied * to the caller. All that is left to do is communicate * status back to the caller. */ if (error != 0) { device_printf(sc->sc_dev, "%s: timeout waiting for command 0x%02x reply\n", __func__, code); } return error; } void otus_write(struct otus_softc *sc, uint32_t reg, uint32_t val) { OTUS_LOCK_ASSERT(sc); sc->write_buf[sc->write_idx].reg = htole32(reg); sc->write_buf[sc->write_idx].val = htole32(val); if (++sc->write_idx > (AR_MAX_WRITE_IDX-1)) (void)otus_write_barrier(sc); } int otus_write_barrier(struct otus_softc *sc) { int error; OTUS_LOCK_ASSERT(sc); if (sc->write_idx == 0) return 0; /* Nothing to flush. */ OTUS_DPRINTF(sc, OTUS_DEBUG_REGIO, "%s: called; %d updates\n", __func__, sc->write_idx); error = otus_cmd(sc, AR_CMD_WREG, sc->write_buf, sizeof (sc->write_buf[0]) * sc->write_idx, NULL, 0); sc->write_idx = 0; return error; } static struct ieee80211_node * otus_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { return malloc(sizeof (struct otus_node), M_80211_NODE, M_NOWAIT | M_ZERO); } int otus_read_eeprom(struct otus_softc *sc) { uint32_t regs[8], reg; uint8_t *eep; int i, j, error; OTUS_LOCK_ASSERT(sc); /* Read EEPROM by blocks of 32 bytes. */ eep = (uint8_t *)&sc->eeprom; reg = AR_EEPROM_OFFSET; for (i = 0; i < sizeof (sc->eeprom) / 32; i++) { for (j = 0; j < 8; j++, reg += 4) regs[j] = htole32(reg); error = otus_cmd(sc, AR_CMD_RREG, regs, sizeof regs, eep, 32); if (error != 0) break; eep += 32; } return error; } void otus_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211com *ic = ni->ni_ic; struct otus_softc *sc = ic->ic_softc; struct otus_node *on = OTUS_NODE(ni); OTUS_DPRINTF(sc, OTUS_DEBUG_STATE, "new assoc isnew=%d addr=%s\n", isnew, ether_sprintf(ni->ni_macaddr)); on->tx_done = 0; on->tx_err = 0; on->tx_retries = 0; } static void otus_cmd_handle_response(struct otus_softc *sc, struct ar_cmd_hdr *hdr) { struct otus_tx_cmd *cmd; OTUS_LOCK_ASSERT(sc); OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, "%s: received reply code=0x%02x len=%d token=%d\n", __func__, hdr->code, hdr->len, hdr->token); /* * Walk the list, freeing items that aren't ours, * stopping when we hit our token. */ while ((cmd = STAILQ_FIRST(&sc->sc_cmd_waiting)) != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_cmd_waiting, next_cmd); OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, "%s: cmd=%p; hdr.token=%d, cmd.token=%d\n", __func__, cmd, (int) hdr->token, (int) cmd->token); if (hdr->token == cmd->token) { /* Copy answer into caller's supplied buffer. */ if (cmd->odata != NULL) { if (hdr->len != cmd->odatalen) { device_printf(sc->sc_dev, "%s: code 0x%02x, len=%d, olen=%d\n", __func__, (int) hdr->code, (int) hdr->len, (int) cmd->odatalen); } memcpy(cmd->odata, &hdr[1], MIN(cmd->odatalen, hdr->len)); } wakeup(cmd); } STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next_cmd); } } void otus_cmd_rxeof(struct otus_softc *sc, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ar_cmd_hdr *hdr; OTUS_LOCK_ASSERT(sc); if (__predict_false(len < sizeof (*hdr))) { OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, "cmd too small %d\n", len); return; } hdr = (struct ar_cmd_hdr *)buf; if (__predict_false(sizeof (*hdr) + hdr->len > len || sizeof (*hdr) + hdr->len > 64)) { OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, "cmd too large %d\n", hdr->len); return; } OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: code=%.02x\n", __func__, hdr->code); /* * This has to reach into the cmd queue "waiting for * an RX response" list, grab the head entry and check * if we need to wake anyone up. */ if ((hdr->code & 0xc0) != 0xc0) { otus_cmd_handle_response(sc, hdr); return; } /* Received unsolicited notification. */ switch (hdr->code & 0x3f) { case AR_EVT_BEACON: break; case AR_EVT_TX_COMP: { struct ar_evt_tx_comp *tx = (struct ar_evt_tx_comp *)&hdr[1]; struct ieee80211_node *ni; ni = ieee80211_find_node(&ic->ic_sta, tx->macaddr); if (ni == NULL) { device_printf(sc->sc_dev, "%s: txcomp on unknown node (%s)\n", __func__, ether_sprintf(tx->macaddr)); break; } OTUS_DPRINTF(sc, OTUS_DEBUG_TXCOMP, "tx completed %s status=%d phy=0x%x\n", ether_sprintf(tx->macaddr), le16toh(tx->status), le32toh(tx->phy)); switch (le16toh(tx->status)) { case AR_TX_STATUS_COMP: #if 0 ackfailcnt = 0; ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); #endif /* * We don't get the above; only error notifications. * Sigh. So, don't worry about this. */ break; case AR_TX_STATUS_RETRY_COMP: OTUS_NODE(ni)->tx_retries++; break; case AR_TX_STATUS_FAILED: OTUS_NODE(ni)->tx_err++; break; } ieee80211_free_node(ni); break; } case AR_EVT_TBTT: break; case AR_EVT_DO_BB_RESET: /* * This is "tell driver to reset baseband" from ar9170-fw. * * I'm not sure what we should do here, so I'm going to * fall through; it gets generated when RTSRetryCnt internally * reaches '5' - I guess the firmware authors thought that * meant that the BB may have gone deaf or something. */ default: device_printf(sc->sc_dev, "%s: received notification code=0x%02x len=%d\n", __func__, hdr->code, hdr->len); } } /* * Handle a single MPDU. * * This may be a single MPDU, or it may be a sub-frame from an A-MPDU. * In the latter case some of the header details need to be adjusted. */ void otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len, struct mbufq *rxq) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_rx_stats rxs; #if 0 struct ieee80211_node *ni; #endif struct ar_rx_macstatus *mac_status = NULL; struct ar_rx_phystatus *phy_status = NULL; struct ieee80211_frame *wh; struct mbuf *m; // int s; if (otus_debug & OTUS_DEBUG_RX_BUFFER) { device_printf(sc->sc_dev, "%s: %*D\n", __func__, len, buf, "-"); } /* * Before any data path stuff - check to see if this is a command * response. * * All bits in the PLCP header are set to 1 for non-MPDU. */ if ((len >= AR_PLCP_HDR_LEN) && memcmp(buf, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) { otus_cmd_rxeof(sc, buf + AR_PLCP_HDR_LEN, len - AR_PLCP_HDR_LEN); return; } /* * First step - get the status for the given frame. * This will tell us whether it's a single MPDU or * an A-MPDU subframe. */ if (len < sizeof(*mac_status)) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: sub-xfer too short (no mac_status) (len %d)\n", __func__, len); counter_u64_add(ic->ic_ierrors, 1); return; } /* * Remove the mac_status from the payload length. * * Note: cheating, don't reallocate the buffer! */ mac_status = (struct ar_rx_macstatus *)(buf + len - sizeof(*mac_status)); len -= sizeof(*mac_status); OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: mac status=0x%x\n", __func__, mac_status->status); /* * Next - check the MAC status before doing anything else. * Extract out the PLCP header for single and first frames; * since there's a single RX path we can shove PLCP headers * from both into sc->ar_last_rx_plcp[] so it can be reused. */ if (((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_SINGLE) || ((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_FIRST)) { /* * Ok, we need to at least have a PLCP header at * this point. */ if (len < AR_PLCP_HDR_LEN) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s sub-xfer too short (no mac+plcp) (len %d\n)", __func__, len); counter_u64_add(ic->ic_ierrors, 1); return; } memcpy(sc->ar_last_rx_plcp, buf, AR_PLCP_HDR_LEN); /* * At this point we can just consume the PLCP header. * The beginning of the frame should thus be data. */ buf += AR_PLCP_HDR_LEN; len -= AR_PLCP_HDR_LEN; } /* * Next - see if we have a PHY status. * * The PHY status is at the end of the final A-MPDU subframe * or a single MPDU frame. * * We'll use this to tag frames with noise floor / RSSI * if they have valid information. */ if (((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_SINGLE) || ((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_LAST)) { if (len < sizeof(*phy_status)) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s sub-xfer too short (no phy status) (len %d\n)", __func__, len); counter_u64_add(ic->ic_ierrors, 1); return; } /* * Take a pointer to the phy status and remove the length * from the end of the buffer. * * Note: we're cheating here; don't reallocate the buffer! */ phy_status = (struct ar_rx_phystatus *) (buf + len - sizeof(*phy_status)); len -= sizeof(*phy_status); } /* * Middle frames just have a MAC status (stripped above.) * No PHY status, and PLCP is from ar_last_rx_plcp. */ /* * Discard error frames; don't discard BAD_RA (eg monitor mode); * let net80211 do that */ if (__predict_false((mac_status->error & ~AR_RX_ERROR_BAD_RA) != 0)) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "error frame 0x%02x\n", mac_status->error); if (mac_status->error & AR_RX_ERROR_FCS) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "bad FCS\n"); } else if (mac_status->error & AR_RX_ERROR_MMIC) { /* Report Michael MIC failures to net80211. */ #if 0 ieee80211_notify_michael_failure(ni->ni_vap, wh, keyidx); #endif device_printf(sc->sc_dev, "%s: MIC failure\n", __func__); } counter_u64_add(ic->ic_ierrors, 1); return; } /* * Make sure there's room for an 802.11 header + FCS. * * Note: a CTS/ACK is 14 bytes (FC, DUR, RA, FCS). * Making it IEEE80211_MIN_LEN misses CTS/ACKs. * * This won't be tossed at this point; eventually once * rx radiotap is implemented this will allow for * CTS/ACK frames. Passing them up to net80211 will * currently make it angry (too short packets.) */ if (len < 2 + 2 + IEEE80211_ADDR_LEN + IEEE80211_CRC_LEN) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: too short for 802.11 (len %d)\n", __func__, len); counter_u64_add(ic->ic_ierrors, 1); return; } len -= IEEE80211_CRC_LEN; /* strip 802.11 FCS */ wh = (struct ieee80211_frame *) buf; /* * The firmware does seem to spit out a bunch of frames * with invalid frame control values here. Just toss them * rather than letting net80211 get angry and log. */ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: invalid 802.11 fc version (firmware bug?)\n", __func__); counter_u64_add(ic->ic_ierrors, 1); return; } m = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { device_printf(sc->sc_dev, "%s: failed m_get2() (len=%d)\n", __func__, len); counter_u64_add(ic->ic_ierrors, 1); return; } /* Finalize mbuf. */ memcpy(mtod(m, uint8_t *), wh, len); m->m_pkthdr.len = m->m_len = len; /* XXX TODO: add setting rx radiotap fields here */ /* * Ok, check the frame length and toss if it's too short * for net80211. This will toss ACK/CTS. */ if (m->m_len < IEEE80211_MIN_LEN) { /* XXX TODO: add radiotap receive here */ m_free(m); m = NULL; return; } /* Add RSSI to this mbuf if we have a PHY header */ bzero(&rxs, sizeof(rxs)); rxs.r_flags = IEEE80211_R_NF; rxs.c_nf = sc->sc_nf[0]; /* XXX chain 0 != combined rssi/nf */ if (phy_status != NULL) { rxs.r_flags |= IEEE80211_R_RSSI; rxs.c_rssi = phy_status->rssi; } /* XXX TODO: add MIMO RSSI/NF as well */ if (ieee80211_add_rx_params(m, &rxs) == 0) { counter_u64_add(ic->ic_ierrors, 1); return; } /* XXX make a method */ STAILQ_INSERT_TAIL(&rxq->mq_head, m, m_stailqpkt); #if 0 OTUS_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, wh); rxi.rxi_flags = 0; rxi.rxi_rssi = tail->rssi; rxi.rxi_tstamp = 0; /* unused */ ieee80211_input(ifp, m, ni, &rxi); /* Node is no longer needed. */ ieee80211_release_node(ic, ni); OTUS_LOCK(sc); #endif } static void otus_rxeof(struct usb_xfer *xfer, struct otus_data *data, struct mbufq *rxq) { struct otus_softc *sc = usbd_xfer_softc(xfer); caddr_t buf = data->buf; struct ar_rx_head *head; uint16_t hlen; int len, offset = 0; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: transfer completed; len=%d\n", __func__, len); if (otus_debug & OTUS_DEBUG_RX_BUFFER) { device_printf(sc->sc_dev, "%s: %*D\n", __func__, len, buf, "-"); } while (len >= sizeof (*head)) { head = (struct ar_rx_head *)buf; if (__predict_false(head->tag != htole16(AR_RX_HEAD_TAG))) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "tag not valid 0x%x\n", le16toh(head->tag)); break; } hlen = le16toh(head->len); OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: hlen=%d\n", __func__, hlen); if (__predict_false(sizeof (*head) + hlen > len)) { OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "xfer too short %d/%d\n", len, hlen); break; } /* Process sub-xfer. */ otus_sub_rxeof(sc, (uint8_t *) (((uint8_t *) buf) + 4), hlen, rxq); /* Next sub-xfer is aligned on a 32-bit boundary. */ hlen = (sizeof (*head) + hlen + 3) & ~3; offset += hlen; OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: rounded size is %d, next packet starts at %d\n", __func__, hlen, offset); buf += hlen; len -= hlen; } OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: done!\n", __func__); } static void otus_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { - struct epoch_tracker et; struct otus_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m; struct mbufq scrx; struct otus_data *data; OTUS_LOCK_ASSERT(sc); mbufq_init(&scrx, 1024); #if 0 device_printf(sc->sc_dev, "%s: called; state=%d; error=%d\n", __func__, USB_GET_STATE(xfer), error); #endif 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); otus_rxeof(xfer, data, &scrx); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: /* * XXX TODO: what if sc_rx isn't empty, but data * is empty? Then we leak mbufs. */ 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. */ OTUS_UNLOCK(sc); - NET_EPOCH_ENTER(et); while ((m = mbufq_dequeue(&scrx)) != NULL) { wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); 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); } - NET_EPOCH_EXIT(et); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_ff_age_all(ic, 100); #endif OTUS_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 tr_setup; } break; } } static void otus_txeof(struct usb_xfer *xfer, struct otus_data *data) { struct otus_softc *sc = usbd_xfer_softc(xfer); OTUS_DPRINTF(sc, OTUS_DEBUG_TXDONE, "%s: called; data=%p\n", __func__, data); OTUS_LOCK_ASSERT(sc); if (sc->sc_tx_n_active == 0) { device_printf(sc->sc_dev, "%s: completed but tx_active=0\n", __func__); } else { sc->sc_tx_n_active--; } if (data->m) { /* XXX status? */ /* XXX we get TX status via the RX path.. */ ieee80211_tx_complete(data->ni, data->m, 0); data->m = NULL; data->ni = NULL; } } static void otus_txcmdeof(struct usb_xfer *xfer, struct otus_tx_cmd *cmd) { struct otus_softc *sc = usbd_xfer_softc(xfer); OTUS_LOCK_ASSERT(sc); OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, "%s: called; data=%p; odata=%p\n", __func__, cmd, cmd->odata); /* * Non-response commands still need wakeup so the caller * knows it was submitted and completed OK; response commands should * wait until they're ACKed by the firmware with a response. */ if (cmd->odata) { STAILQ_INSERT_TAIL(&sc->sc_cmd_waiting, cmd, next_cmd); } else { wakeup(cmd); otus_free_txcmd(sc, cmd); } } static void otus_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { uint8_t which = OTUS_BULK_TX; struct otus_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct otus_data *data; OTUS_LOCK_ASSERT(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active[which]); if (data == NULL) goto tr_setup; OTUS_DPRINTF(sc, OTUS_DEBUG_TXDONE, "%s: transfer done %p\n", __func__, data); STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); otus_txeof(xfer, data); otus_freebuf(sc, data); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->sc_tx_pending[which]); if (data == NULL) { OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, "%s: empty pending queue sc %p\n", __func__, sc); sc->sc_tx_n_active = 0; goto finish; } 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); OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, "%s: submitting transfer %p\n", __func__, data); usbd_transfer_submit(xfer); sc->sc_tx_n_active++; break; default: data = STAILQ_FIRST(&sc->sc_tx_active[which]); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); otus_txeof(xfer, data); otus_freebuf(sc, data); } counter_u64_add(ic->ic_oerrors, 1); if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto tr_setup; } break; } finish: #ifdef IEEE80211_SUPPORT_SUPERG /* * If the TX active queue drops below a certain * threshold, ensure we age fast-frames out so they're * transmitted. */ if (sc->sc_tx_n_active < 2) { /* XXX ew - net80211 should defer this for us! */ OTUS_UNLOCK(sc); ieee80211_ff_flush(ic, WME_AC_VO); ieee80211_ff_flush(ic, WME_AC_VI); ieee80211_ff_flush(ic, WME_AC_BE); ieee80211_ff_flush(ic, WME_AC_BK); OTUS_LOCK(sc); } #endif /* Kick TX */ otus_tx_start(sc); } static void otus_bulk_cmd_callback(struct usb_xfer *xfer, usb_error_t error) { struct otus_softc *sc = usbd_xfer_softc(xfer); #if 0 struct ieee80211com *ic = &sc->sc_ic; #endif struct otus_tx_cmd *cmd; OTUS_LOCK_ASSERT(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: cmd = STAILQ_FIRST(&sc->sc_cmd_active); if (cmd == NULL) goto tr_setup; OTUS_DPRINTF(sc, OTUS_DEBUG_CMDDONE, "%s: transfer done %p\n", __func__, cmd); STAILQ_REMOVE_HEAD(&sc->sc_cmd_active, next_cmd); otus_txcmdeof(xfer, cmd); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: cmd = STAILQ_FIRST(&sc->sc_cmd_pending); if (cmd == NULL) { OTUS_DPRINTF(sc, OTUS_DEBUG_CMD, "%s: empty pending queue sc %p\n", __func__, sc); return; } STAILQ_REMOVE_HEAD(&sc->sc_cmd_pending, next_cmd); STAILQ_INSERT_TAIL(&sc->sc_cmd_active, cmd, next_cmd); usbd_xfer_set_frame_data(xfer, 0, cmd->buf, cmd->buflen); OTUS_DPRINTF(sc, OTUS_DEBUG_CMD, "%s: submitting transfer %p; buf=%p, buflen=%d\n", __func__, cmd, cmd->buf, cmd->buflen); usbd_transfer_submit(xfer); break; default: cmd = STAILQ_FIRST(&sc->sc_cmd_active); if (cmd != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_cmd_active, next_cmd); otus_txcmdeof(xfer, cmd); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } /* * This isn't used by carl9170; it however may be used by the * initial bootloader. */ static void otus_bulk_irq_callback(struct usb_xfer *xfer, usb_error_t error) { struct otus_softc *sc = usbd_xfer_softc(xfer); int actlen; int sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); OTUS_DPRINTF(sc, OTUS_DEBUG_IRQ, "%s: called; state=%d\n", __func__, USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* * Read usb frame data, if any. * "actlen" has the total length for all frames * transferred. */ OTUS_DPRINTF(sc, OTUS_DEBUG_IRQ, "%s: comp; %d bytes\n", __func__, actlen); #if 0 pc = usbd_xfer_get_frame(xfer, 0); otus_dump_usb_rx_page(sc, pc, actlen); #endif /* XXX fallthrough */ case USB_ST_SETUP: /* * Setup xfer frame lengths/count and data */ OTUS_DPRINTF(sc, OTUS_DEBUG_IRQ, "%s: setup\n", __func__); usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: /* Error */ /* * Print error message and clear stall * for example. */ OTUS_DPRINTF(sc, OTUS_DEBUG_IRQ, "%s: ERROR?\n", __func__); break; } } /* * Map net80211 rate to hw rate for otus MAC/PHY. */ static uint8_t otus_rate_to_hw_rate(struct otus_softc *sc, uint8_t rate) { int is_2ghz; is_2ghz = !! (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_curchan)); /* MCS check */ if (rate & 0x80) { return rate; } switch (rate) { /* CCK */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); /* OFDM */ 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); default: device_printf(sc->sc_dev, "%s: unknown rate '%d'\n", __func__, (int) rate); case 0: if (is_2ghz) return (0x0); /* 1MB CCK */ else return (0xb); /* 6MB OFDM */ } } static int otus_hw_rate_is_ht(struct otus_softc *sc, uint8_t hw_rate) { return !! (hw_rate & 0x80); } static int otus_hw_rate_is_ofdm(struct otus_softc *sc, uint8_t hw_rate) { switch (hw_rate) { case 0x0: case 0x1: case 0x2: case 0x3: return (0); default: return (1); } } static void otus_tx_update_ratectl(struct otus_softc *sc, struct ieee80211_node *ni) { struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; struct otus_node *on = OTUS_NODE(ni); txs->flags = IEEE80211_RATECTL_TX_STATS_NODE | IEEE80211_RATECTL_TX_STATS_RETRIES; txs->ni = ni; txs->nframes = on->tx_done; txs->nsuccess = on->tx_done - on->tx_err; txs->nretries = on->tx_retries; ieee80211_ratectl_tx_update(ni->ni_vap, txs); on->tx_done = on->tx_err = on->tx_retries = 0; } /* * XXX TODO: support tx bpf parameters for configuration! * * Relevant pieces: * * ac = params->ibp_pri & 3; * rate = params->ibp_rate0; * params->ibp_flags & IEEE80211_BPF_NOACK * params->ibp_flags & IEEE80211_BPF_RTS * params->ibp_flags & IEEE80211_BPF_CTS * tx->rts_ntries = params->ibp_try1; * tx->data_ntries = params->ibp_try0; */ static int otus_tx(struct otus_softc *sc, struct ieee80211_node *ni, struct mbuf *m, struct otus_data *data, const struct ieee80211_bpf_params *params) { const struct ieee80211_txparam *tp = ni->ni_txparms; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; struct ieee80211_key *k; struct ar_tx_head *head; uint32_t phyctl; uint16_t macctl, qos; uint8_t qid, rate; int hasqos, xferlen, type, ismcast; 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, "%s: m=%p: ieee80211_crypto_encap returns NULL\n", __func__, m); return (ENOBUFS); } wh = mtod(m, struct ieee80211_frame *); } /* Calculate transfer length; ensure data buffer is large enough */ xferlen = sizeof (*head) + m->m_pkthdr.len; if (xferlen > OTUS_TXBUFSZ) { device_printf(sc->sc_dev, "%s: 802.11 TX frame is %d bytes, max %d bytes\n", __func__, xferlen, OTUS_TXBUFSZ); return (ENOBUFS); } hasqos = !! IEEE80211_QOS_HAS_SEQ(wh); if (hasqos) { uint8_t tid; qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; tid = qos & IEEE80211_QOS_TID; qid = TID_TO_WME_AC(tid); } else { qos = 0; qid = WME_AC_BE; } type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); /* Pickup a rate index. */ if (params != NULL) rate = otus_rate_to_hw_rate(sc, params->ibp_rate0); else if (!!(m->m_flags & M_EAPOL) || type != IEEE80211_FC0_TYPE_DATA) rate = otus_rate_to_hw_rate(sc, tp->mgmtrate); else if (ismcast) rate = otus_rate_to_hw_rate(sc, tp->mcastrate); else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = otus_rate_to_hw_rate(sc, tp->ucastrate); else { (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = otus_rate_to_hw_rate(sc, ni->ni_txrate); } phyctl = 0; macctl = AR_TX_MAC_BACKOFF | AR_TX_MAC_HW_DUR | AR_TX_MAC_QID(qid); /* * XXX TODO: params for NOACK, ACK, RTS, CTS, etc */ if (ismcast || (hasqos && ((qos & IEEE80211_QOS_ACKPOLICY) == IEEE80211_QOS_ACKPOLICY_NOACK))) macctl |= AR_TX_MAC_NOACK; if (!ismcast) { if (m->m_pkthdr.len + IEEE80211_CRC_LEN >= vap->iv_rtsthreshold) macctl |= AR_TX_MAC_RTS; else if (otus_hw_rate_is_ht(sc, rate)) { if (ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) macctl |= AR_TX_MAC_RTS; } else if (ic->ic_flags & IEEE80211_F_USEPROT) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) macctl |= AR_TX_MAC_CTS; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) macctl |= AR_TX_MAC_RTS; } } phyctl |= AR_TX_PHY_MCS(rate & 0x7f); /* Note: MCS rates are 0x80 and above */ if (otus_hw_rate_is_ht(sc, rate)) { phyctl |= AR_TX_PHY_MT_HT; /* Always use all tx antennas for now, just to be safe */ phyctl |= AR_TX_PHY_ANTMSK(sc->txmask); /* Heavy clip */ phyctl |= (rate & 0x7) << AR_TX_PHY_TX_HEAVY_CLIP_SHIFT; } else if (otus_hw_rate_is_ofdm(sc, rate)) { phyctl |= AR_TX_PHY_MT_OFDM; /* Always use all tx antennas for now, just to be safe */ phyctl |= AR_TX_PHY_ANTMSK(sc->txmask); } else { /* CCK */ phyctl |= AR_TX_PHY_MT_CCK; phyctl |= AR_TX_PHY_ANTMSK(sc->txmask); } /* Update net80211 with the current counters */ otus_tx_update_ratectl(sc, ni); /* Update rate control stats for frames that are ACK'ed. */ if (!(macctl & AR_TX_MAC_NOACK)) OTUS_NODE(ni)->tx_done++; /* Fill Tx descriptor. */ head = (struct ar_tx_head *)data->buf; head->len = htole16(m->m_pkthdr.len + IEEE80211_CRC_LEN); head->macctl = htole16(macctl); head->phyctl = htole32(phyctl); m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&head[1]); data->buflen = xferlen; data->ni = ni; data->m = m; OTUS_DPRINTF(sc, OTUS_DEBUG_XMIT, "%s: tx: m=%p; data=%p; len=%d mac=0x%04x phy=0x%08x rate=0x%02x, ni_txrate=%d\n", __func__, m, data, le16toh(head->len), macctl, phyctl, (int) rate, (int) ni->ni_txrate); /* Submit transfer */ STAILQ_INSERT_TAIL(&sc->sc_tx_pending[OTUS_BULK_TX], data, next); usbd_transfer_start(sc->sc_xfer[OTUS_BULK_TX]); return 0; } static u_int otus_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { uint32_t val, *hashes = arg; val = le32dec(LLADDR(sdl) + 4); /* Get address byte 5 */ val = val & 0x0000ff00; val = val >> 8; /* As per below, shift it >> 2 to get only 6 bits */ val = val >> 2; if (val < 32) hashes[0] |= 1 << val; else hashes[1] |= 1 << (val - 32); return (1); } int otus_set_multi(struct otus_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t hashes[2]; int r; if (ic->ic_allmulti > 0 || ic->ic_promisc > 0 || ic->ic_opmode == IEEE80211_M_MONITOR) { hashes[0] = 0xffffffff; hashes[1] = 0xffffffff; } else { struct ieee80211vap *vap; hashes[0] = hashes[1] = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if_foreach_llmaddr(vap->iv_ifp, otus_hash_maddr, hashes); } #if 0 /* XXX openbsd code */ while (enm != NULL) { bit = enm->enm_addrlo[5] >> 2; if (bit < 32) hashes[0] |= 1 << bit; else hashes[1] |= 1 << (bit - 32); ETHER_NEXT_MULTI(step, enm); } #endif hashes[1] |= 1U << 31; /* Make sure the broadcast bit is set. */ OTUS_LOCK(sc); otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_L, hashes[0]); otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_H, hashes[1]); r = otus_write_barrier(sc); /* XXX operating mode? filter? */ OTUS_UNLOCK(sc); return (r); } static int otus_updateedca(struct ieee80211com *ic) { struct otus_softc *sc = ic->ic_softc; OTUS_LOCK(sc); /* * XXX TODO: take temporary copy of EDCA information * when scheduling this so we have a more time-correct view * of things. * XXX TODO: this can be done on the net80211 level */ otus_updateedca_locked(sc); OTUS_UNLOCK(sc); return (0); } static void otus_updateedca_locked(struct otus_softc *sc) { #define EXP2(val) ((1 << (val)) - 1) #define AIFS(val) ((val) * 9 + 10) struct chanAccParams chp; struct ieee80211com *ic = &sc->sc_ic; const struct wmeParams *edca; ieee80211_wme_ic_getparams(ic, &chp); OTUS_LOCK_ASSERT(sc); edca = chp.cap_wmeParams; /* Set CWmin/CWmax values. */ otus_write(sc, AR_MAC_REG_AC0_CW, EXP2(edca[WME_AC_BE].wmep_logcwmax) << 16 | EXP2(edca[WME_AC_BE].wmep_logcwmin)); otus_write(sc, AR_MAC_REG_AC1_CW, EXP2(edca[WME_AC_BK].wmep_logcwmax) << 16 | EXP2(edca[WME_AC_BK].wmep_logcwmin)); otus_write(sc, AR_MAC_REG_AC2_CW, EXP2(edca[WME_AC_VI].wmep_logcwmax) << 16 | EXP2(edca[WME_AC_VI].wmep_logcwmin)); otus_write(sc, AR_MAC_REG_AC3_CW, EXP2(edca[WME_AC_VO].wmep_logcwmax) << 16 | EXP2(edca[WME_AC_VO].wmep_logcwmin)); otus_write(sc, AR_MAC_REG_AC4_CW, /* Special TXQ. */ EXP2(edca[WME_AC_VO].wmep_logcwmax) << 16 | EXP2(edca[WME_AC_VO].wmep_logcwmin)); /* Set AIFSN values. */ otus_write(sc, AR_MAC_REG_AC1_AC0_AIFS, AIFS(edca[WME_AC_VI].wmep_aifsn) << 24 | AIFS(edca[WME_AC_BK].wmep_aifsn) << 12 | AIFS(edca[WME_AC_BE].wmep_aifsn)); otus_write(sc, AR_MAC_REG_AC3_AC2_AIFS, AIFS(edca[WME_AC_VO].wmep_aifsn) << 16 | /* Special TXQ. */ AIFS(edca[WME_AC_VO].wmep_aifsn) << 4 | AIFS(edca[WME_AC_VI].wmep_aifsn) >> 8); /* Set TXOP limit. */ otus_write(sc, AR_MAC_REG_AC1_AC0_TXOP, edca[WME_AC_BK].wmep_txopLimit << 16 | edca[WME_AC_BE].wmep_txopLimit); otus_write(sc, AR_MAC_REG_AC3_AC2_TXOP, edca[WME_AC_VO].wmep_txopLimit << 16 | edca[WME_AC_VI].wmep_txopLimit); /* XXX ACK policy? */ (void)otus_write_barrier(sc); #undef AIFS #undef EXP2 } static void otus_updateslot(struct otus_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t slottime; OTUS_LOCK_ASSERT(sc); slottime = IEEE80211_GET_SLOTTIME(ic); otus_write(sc, AR_MAC_REG_SLOT_TIME, slottime << 10); (void)otus_write_barrier(sc); } /* * Things to do based on 2GHz or 5GHz: * * + slottime * + dyn_sifs_ack * + rts_cts_rate * + slot time * + mac_rates * + mac_tpc * * And in the transmit path * + tpc: carl9170_tx_rate_tpc_chains * + carl9170_tx_physet() * + disable short premable tx */ int otus_init_mac(struct otus_softc *sc) { int error; OTUS_LOCK_ASSERT(sc); otus_write(sc, AR_MAC_REG_ACK_EXTENSION, 0x40); otus_write(sc, AR_MAC_REG_RETRY_MAX, 0); otus_write(sc, AR_MAC_REG_RX_THRESHOLD, 0xc1f80); otus_write(sc, AR_MAC_REG_RX_PE_DELAY, 0x70); otus_write(sc, AR_MAC_REG_EIFS_AND_SIFS, 0xa144000); otus_write(sc, AR_MAC_REG_SLOT_TIME, 9 << 10); otus_write(sc, AR_MAC_REG_TID_CFACK_CFEND_RATE, 0x19000000); /* NAV protects ACK only (in TXOP). */ otus_write(sc, AR_MAC_REG_TXOP_DURATION, 0x201); /* Set beacon Tx power to 0x7. */ otus_write(sc, AR_MAC_REG_BCN_HT1, 0x8000170); otus_write(sc, AR_MAC_REG_BACKOFF_PROTECT, 0x105); otus_write(sc, AR_MAC_REG_AMPDU_FACTOR, 0x10000a); otus_set_rx_filter(sc); otus_write(sc, AR_MAC_REG_BASIC_RATE, 0x150f); otus_write(sc, AR_MAC_REG_MANDATORY_RATE, 0x150f); otus_write(sc, AR_MAC_REG_RTS_CTS_RATE, 0x10b01bb); otus_write(sc, AR_MAC_REG_ACK_TPC, 0x4003c1e); /* Enable LED0 and LED1. */ otus_write(sc, AR_GPIO_REG_PORT_TYPE, 0x3); otus_write(sc, AR_GPIO_REG_PORT_DATA, 0x3); /* Switch MAC to OTUS interface. */ otus_write(sc, 0x1c3600, 0x3); otus_write(sc, AR_MAC_REG_AMPDU_RX_THRESH, 0xffff); otus_write(sc, AR_MAC_REG_MISC_680, 0xf00008); /* Disable Rx timeout (workaround). */ otus_write(sc, AR_MAC_REG_RX_TIMEOUT, 0); /* Set USB Rx stream mode maximum frame number to 2. */ otus_write(sc, 0x1e1110, 0x4); /* Set USB Rx stream mode timeout to 10us. */ otus_write(sc, 0x1e1114, 0x80); /* Set clock frequency to 88/80MHz. */ otus_write(sc, AR_PWR_REG_CLOCK_SEL, 0x73); /* Set WLAN DMA interrupt mode: generate intr per packet. */ otus_write(sc, AR_MAC_REG_TXRX_MPI, 0x110011); otus_write(sc, AR_MAC_REG_FCS_SELECT, 0x4); otus_write(sc, AR_MAC_REG_TXOP_NOT_ENOUGH_INDICATION, 0x141e0f48); /* Disable HW decryption for now. */ otus_write(sc, AR_MAC_REG_ENCRYPTION, 0x78); if ((error = otus_write_barrier(sc)) != 0) return error; /* Set default EDCA parameters. */ otus_updateedca_locked(sc); return 0; } /* * Return default value for PHY register based on current operating mode. */ uint32_t otus_phy_get_def(struct otus_softc *sc, uint32_t reg) { int i; for (i = 0; i < nitems(ar5416_phy_regs); i++) if (AR_PHY(ar5416_phy_regs[i]) == reg) return sc->phy_vals[i]; return 0; /* Register not found. */ } /* * Update PHY's programming based on vendor-specific data stored in EEPROM. * This is for FEM-type devices only. */ int otus_set_board_values(struct otus_softc *sc, struct ieee80211_channel *c) { const struct ModalEepHeader *eep; uint32_t tmp, offset; if (IEEE80211_IS_CHAN_5GHZ(c)) eep = &sc->eeprom.modalHeader[0]; else eep = &sc->eeprom.modalHeader[1]; /* Offset of chain 2. */ offset = 2 * 0x1000; tmp = le32toh(eep->antCtrlCommon); otus_write(sc, AR_PHY_SWITCH_COM, tmp); tmp = le32toh(eep->antCtrlChain[0]); otus_write(sc, AR_PHY_SWITCH_CHAIN_0, tmp); tmp = le32toh(eep->antCtrlChain[1]); otus_write(sc, AR_PHY_SWITCH_CHAIN_0 + offset, tmp); if (1 /* sc->sc_sco == AR_SCO_SCN */) { tmp = otus_phy_get_def(sc, AR_PHY_SETTLING); tmp &= ~(0x7f << 7); tmp |= (eep->switchSettling & 0x7f) << 7; otus_write(sc, AR_PHY_SETTLING, tmp); } tmp = otus_phy_get_def(sc, AR_PHY_DESIRED_SZ); tmp &= ~0xffff; tmp |= eep->pgaDesiredSize << 8 | eep->adcDesiredSize; otus_write(sc, AR_PHY_DESIRED_SZ, tmp); tmp = eep->txEndToXpaOff << 24 | eep->txEndToXpaOff << 16 | eep->txFrameToXpaOn << 8 | eep->txFrameToXpaOn; otus_write(sc, AR_PHY_RF_CTL4, tmp); tmp = otus_phy_get_def(sc, AR_PHY_RF_CTL3); tmp &= ~(0xff << 16); tmp |= eep->txEndToRxOn << 16; otus_write(sc, AR_PHY_RF_CTL3, tmp); tmp = otus_phy_get_def(sc, AR_PHY_CCA); tmp &= ~(0x7f << 12); tmp |= (eep->thresh62 & 0x7f) << 12; otus_write(sc, AR_PHY_CCA, tmp); tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN); tmp &= ~(0x3f << 12); tmp |= (eep->txRxAttenCh[0] & 0x3f) << 12; otus_write(sc, AR_PHY_RXGAIN, tmp); tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN + offset); tmp &= ~(0x3f << 12); tmp |= (eep->txRxAttenCh[1] & 0x3f) << 12; otus_write(sc, AR_PHY_RXGAIN + offset, tmp); tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ); tmp &= ~(0x3f << 18); tmp |= (eep->rxTxMarginCh[0] & 0x3f) << 18; if (IEEE80211_IS_CHAN_5GHZ(c)) { tmp &= ~(0xf << 10); tmp |= (eep->bswMargin[0] & 0xf) << 10; } otus_write(sc, AR_PHY_GAIN_2GHZ, tmp); tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ + offset); tmp &= ~(0x3f << 18); tmp |= (eep->rxTxMarginCh[1] & 0x3f) << 18; otus_write(sc, AR_PHY_GAIN_2GHZ + offset, tmp); tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4); tmp &= ~(0x3f << 5 | 0x1f); tmp |= (eep->iqCalICh[0] & 0x3f) << 5 | (eep->iqCalQCh[0] & 0x1f); otus_write(sc, AR_PHY_TIMING_CTRL4, tmp); tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4 + offset); tmp &= ~(0x3f << 5 | 0x1f); tmp |= (eep->iqCalICh[1] & 0x3f) << 5 | (eep->iqCalQCh[1] & 0x1f); otus_write(sc, AR_PHY_TIMING_CTRL4 + offset, tmp); tmp = otus_phy_get_def(sc, AR_PHY_TPCRG1); tmp &= ~(0xf << 16); tmp |= (eep->xpd & 0xf) << 16; otus_write(sc, AR_PHY_TPCRG1, tmp); return otus_write_barrier(sc); } int otus_program_phy(struct otus_softc *sc, struct ieee80211_channel *c) { const uint32_t *vals; int error, i; /* Select PHY programming based on band and bandwidth. */ if (IEEE80211_IS_CHAN_2GHZ(c)) { if (IEEE80211_IS_CHAN_HT40(c)) vals = ar5416_phy_vals_2ghz_40mhz; else vals = ar5416_phy_vals_2ghz_20mhz; } else { if (IEEE80211_IS_CHAN_HT40(c)) vals = ar5416_phy_vals_5ghz_40mhz; else vals = ar5416_phy_vals_5ghz_20mhz; } for (i = 0; i < nitems(ar5416_phy_regs); i++) otus_write(sc, AR_PHY(ar5416_phy_regs[i]), vals[i]); sc->phy_vals = vals; if (sc->eeprom.baseEepHeader.deviceType == 0x80) /* FEM */ if ((error = otus_set_board_values(sc, c)) != 0) return error; /* Initial Tx power settings. */ otus_write(sc, AR_PHY_POWER_TX_RATE_MAX, 0x7f); otus_write(sc, AR_PHY_POWER_TX_RATE1, 0x3f3f3f3f); otus_write(sc, AR_PHY_POWER_TX_RATE2, 0x3f3f3f3f); otus_write(sc, AR_PHY_POWER_TX_RATE3, 0x3f3f3f3f); otus_write(sc, AR_PHY_POWER_TX_RATE4, 0x3f3f3f3f); otus_write(sc, AR_PHY_POWER_TX_RATE5, 0x3f3f3f3f); otus_write(sc, AR_PHY_POWER_TX_RATE6, 0x3f3f3f3f); otus_write(sc, AR_PHY_POWER_TX_RATE7, 0x3f3f3f3f); otus_write(sc, AR_PHY_POWER_TX_RATE8, 0x3f3f3f3f); otus_write(sc, AR_PHY_POWER_TX_RATE9, 0x3f3f3f3f); if (IEEE80211_IS_CHAN_2GHZ(c)) otus_write(sc, AR_PWR_REG_PLL_ADDAC, 0x5163); else otus_write(sc, AR_PWR_REG_PLL_ADDAC, 0x5143); return otus_write_barrier(sc); } static __inline uint8_t otus_reverse_bits(uint8_t v) { v = ((v >> 1) & 0x55) | ((v & 0x55) << 1); v = ((v >> 2) & 0x33) | ((v & 0x33) << 2); v = ((v >> 4) & 0x0f) | ((v & 0x0f) << 4); return v; } int otus_set_rf_bank4(struct otus_softc *sc, struct ieee80211_channel *c) { uint8_t chansel, d0, d1; uint16_t data; int error; OTUS_LOCK_ASSERT(sc); d0 = 0; if (IEEE80211_IS_CHAN_5GHZ(c)) { chansel = (c->ic_freq - 4800) / 5; if (chansel & 1) d0 |= AR_BANK4_AMODE_REFSEL(2); else d0 |= AR_BANK4_AMODE_REFSEL(1); } else { d0 |= AR_BANK4_AMODE_REFSEL(2); if (c->ic_freq == 2484) { /* CH 14 */ d0 |= AR_BANK4_BMODE_LF_SYNTH_FREQ; chansel = 10 + (c->ic_freq - 2274) / 5; } else chansel = 16 + (c->ic_freq - 2272) / 5; chansel <<= 2; } d0 |= AR_BANK4_ADDR(1) | AR_BANK4_CHUP; d1 = otus_reverse_bits(chansel); /* Write bits 0-4 of d0 and d1. */ data = (d1 & 0x1f) << 5 | (d0 & 0x1f); otus_write(sc, AR_PHY(44), data); /* Write bits 5-7 of d0 and d1. */ data = (d1 >> 5) << 5 | (d0 >> 5); otus_write(sc, AR_PHY(58), data); if ((error = otus_write_barrier(sc)) == 0) otus_delay_ms(sc, 10); return error; } void otus_get_delta_slope(uint32_t coeff, uint32_t *exponent, uint32_t *mantissa) { #define COEFF_SCALE_SHIFT 24 uint32_t exp, man; /* exponent = 14 - floor(log2(coeff)) */ for (exp = 31; exp > 0; exp--) if (coeff & (1 << exp)) break; KASSERT(exp != 0, ("exp")); exp = 14 - (exp - COEFF_SCALE_SHIFT); /* mantissa = floor(coeff * 2^exponent + 0.5) */ man = coeff + (1 << (COEFF_SCALE_SHIFT - exp - 1)); *mantissa = man >> (COEFF_SCALE_SHIFT - exp); *exponent = exp - 16; #undef COEFF_SCALE_SHIFT } static int otus_set_chan(struct otus_softc *sc, struct ieee80211_channel *c, int assoc) { struct ieee80211com *ic = &sc->sc_ic; struct ar_cmd_frequency cmd; struct ar_rsp_frequency rsp; const uint32_t *vals; uint32_t coeff, exp, man, tmp; uint8_t code; int error, chan, i; error = 0; chan = ieee80211_chan2ieee(ic, c); OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "setting channel %d (%dMHz)\n", chan, c->ic_freq); tmp = IEEE80211_IS_CHAN_2GHZ(c) ? 0x105 : 0x104; otus_write(sc, AR_MAC_REG_DYNAMIC_SIFS_ACK, tmp); if ((error = otus_write_barrier(sc)) != 0) goto finish; /* Disable BB Heavy Clip. */ otus_write(sc, AR_PHY_HEAVY_CLIP_ENABLE, 0x200); if ((error = otus_write_barrier(sc)) != 0) goto finish; /* XXX Is that FREQ_START ? */ error = otus_cmd(sc, AR_CMD_FREQ_STRAT, NULL, 0, NULL, 0); if (error != 0) goto finish; /* Reprogram PHY and RF on channel band or bandwidth changes. */ if (sc->bb_reset || c->ic_flags != sc->sc_curchan->ic_flags) { OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "band switch\n"); /* Cold/Warm reset BB/ADDA. */ otus_write(sc, AR_PWR_REG_RESET, sc->bb_reset ? 0x800 : 0x400); if ((error = otus_write_barrier(sc)) != 0) goto finish; otus_write(sc, AR_PWR_REG_RESET, 0); if ((error = otus_write_barrier(sc)) != 0) goto finish; sc->bb_reset = 0; if ((error = otus_program_phy(sc, c)) != 0) { device_printf(sc->sc_dev, "%s: could not program PHY\n", __func__); goto finish; } /* Select RF programming based on band. */ if (IEEE80211_IS_CHAN_5GHZ(c)) vals = ar5416_banks_vals_5ghz; else vals = ar5416_banks_vals_2ghz; for (i = 0; i < nitems(ar5416_banks_regs); i++) otus_write(sc, AR_PHY(ar5416_banks_regs[i]), vals[i]); if ((error = otus_write_barrier(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not program RF\n", __func__); goto finish; } code = AR_CMD_RF_INIT; } else { code = AR_CMD_FREQUENCY; } if ((error = otus_set_rf_bank4(sc, c)) != 0) goto finish; tmp = (sc->txmask == 0x5) ? 0x340 : 0x240; otus_write(sc, AR_PHY_TURBO, tmp); if ((error = otus_write_barrier(sc)) != 0) goto finish; /* Send firmware command to set channel. */ cmd.freq = htole32((uint32_t)c->ic_freq * 1000); cmd.dynht2040 = htole32(0); cmd.htena = htole32(1); /* Set Delta Slope (exponent and mantissa). */ coeff = (100 << 24) / c->ic_freq; otus_get_delta_slope(coeff, &exp, &man); cmd.dsc_exp = htole32(exp); cmd.dsc_man = htole32(man); OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "ds coeff=%u exp=%u man=%u\n", coeff, exp, man); /* For Short GI, coeff is 9/10 that of normal coeff. */ coeff = (9 * coeff) / 10; otus_get_delta_slope(coeff, &exp, &man); cmd.dsc_shgi_exp = htole32(exp); cmd.dsc_shgi_man = htole32(man); OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "ds shgi coeff=%u exp=%u man=%u\n", coeff, exp, man); /* Set wait time for AGC and noise calibration (100 or 200ms). */ cmd.check_loop_count = assoc ? htole32(2000) : htole32(1000); OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "%s\n", (code == AR_CMD_RF_INIT) ? "RF_INIT" : "FREQUENCY"); error = otus_cmd(sc, code, &cmd, sizeof cmd, &rsp, sizeof(rsp)); if (error != 0) goto finish; if ((rsp.status & htole32(AR_CAL_ERR_AGC | AR_CAL_ERR_NF_VAL)) != 0) { OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "status=0x%x\n", le32toh(rsp.status)); /* Force cold reset on next channel. */ sc->bb_reset = 1; } #ifdef USB_DEBUG if (otus_debug & OTUS_DEBUG_RESET) { device_printf(sc->sc_dev, "calibration status=0x%x\n", le32toh(rsp.status)); for (i = 0; i < 2; i++) { /* 2 Rx chains */ /* Sign-extend 9-bit NF values. */ device_printf(sc->sc_dev, "noisefloor chain %d=%d\n", i, (((int32_t)le32toh(rsp.nf[i])) << 4) >> 23); device_printf(sc->sc_dev, "noisefloor ext chain %d=%d\n", i, ((int32_t)le32toh(rsp.nf_ext[i])) >> 23); } } #endif for (i = 0; i < OTUS_NUM_CHAINS; i++) { sc->sc_nf[i] = ((((int32_t)le32toh(rsp.nf[i])) << 4) >> 23); } sc->sc_curchan = c; finish: return (error); } #ifdef notyet int otus_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, struct ieee80211_key *k) { struct otus_softc *sc = ic->ic_softc; struct otus_cmd_key cmd; /* Defer setting of WEP keys until interface is brought up. */ if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) return 0; /* Do it in a process context. */ cmd.key = *k; cmd.associd = (ni != NULL) ? ni->ni_associd : 0; otus_do_async(sc, otus_set_key_cb, &cmd, sizeof cmd); return 0; } void otus_set_key_cb(struct otus_softc *sc, void *arg) { struct otus_cmd_key *cmd = arg; struct ieee80211_key *k = &cmd->key; struct ar_cmd_ekey key; uint16_t cipher; int error; memset(&key, 0, sizeof key); if (k->k_flags & IEEE80211_KEY_GROUP) { key.uid = htole16(k->k_id); IEEE80211_ADDR_COPY(key.macaddr, sc->sc_ic.ic_myaddr); key.macaddr[0] |= 0x80; } else { key.uid = htole16(OTUS_UID(cmd->associd)); IEEE80211_ADDR_COPY(key.macaddr, ni->ni_macaddr); } key.kix = htole16(0); /* Map net80211 cipher to hardware. */ switch (k->k_cipher) { case IEEE80211_CIPHER_WEP40: cipher = AR_CIPHER_WEP64; break; case IEEE80211_CIPHER_WEP104: cipher = AR_CIPHER_WEP128; break; case IEEE80211_CIPHER_TKIP: cipher = AR_CIPHER_TKIP; break; case IEEE80211_CIPHER_CCMP: cipher = AR_CIPHER_AES; break; default: return; } key.cipher = htole16(cipher); memcpy(key.key, k->k_key, MIN(k->k_len, 16)); error = otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL, 0); if (error != 0 || k->k_cipher != IEEE80211_CIPHER_TKIP) return; /* TKIP: set Tx/Rx MIC Key. */ key.kix = htole16(1); memcpy(key.key, k->k_key + 16, 16); (void)otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL, 0); } void otus_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, struct ieee80211_key *k) { struct otus_softc *sc = ic->ic_softc; struct otus_cmd_key cmd; if (!(ic->ic_if.if_flags & IFF_RUNNING) || ic->ic_state != IEEE80211_S_RUN) return; /* Nothing to do. */ /* Do it in a process context. */ cmd.key = *k; cmd.associd = (ni != NULL) ? ni->ni_associd : 0; otus_do_async(sc, otus_delete_key_cb, &cmd, sizeof cmd); } void otus_delete_key_cb(struct otus_softc *sc, void *arg) { struct otus_cmd_key *cmd = arg; struct ieee80211_key *k = &cmd->key; uint32_t uid; if (k->k_flags & IEEE80211_KEY_GROUP) uid = htole32(k->k_id); else uid = htole32(OTUS_UID(cmd->associd)); (void)otus_cmd(sc, AR_CMD_DKEY, &uid, sizeof uid, NULL, 0); } #endif /* * XXX TODO: check if we have to be doing any calibration in the host * or whether it's purely a firmware thing. */ void otus_calibrate_to(void *arg, int pending) { #if 0 struct otus_softc *sc = arg; device_printf(sc->sc_dev, "%s: called\n", __func__); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; int s; if (usbd_is_dying(sc->sc_udev)) return; usbd_ref_incr(sc->sc_udev); s = splnet(); ni = ic->ic_bss; ieee80211_amrr_choose(&sc->amrr, ni, &((struct otus_node *)ni)->amn); splx(s); if (!usbd_is_dying(sc->sc_udev)) timeout_add_sec(&sc->calib_to, 1); usbd_ref_decr(sc->sc_udev); #endif } int otus_set_bssid(struct otus_softc *sc, const uint8_t *bssid) { OTUS_LOCK_ASSERT(sc); otus_write(sc, AR_MAC_REG_BSSID_L, bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); otus_write(sc, AR_MAC_REG_BSSID_H, bssid[4] | bssid[5] << 8); return otus_write_barrier(sc); } int otus_set_macaddr(struct otus_softc *sc, const uint8_t *addr) { OTUS_LOCK_ASSERT(sc); otus_write(sc, AR_MAC_REG_MAC_ADDR_L, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); otus_write(sc, AR_MAC_REG_MAC_ADDR_H, addr[4] | addr[5] << 8); return otus_write_barrier(sc); } /* Default single-LED. */ void otus_led_newstate_type1(struct otus_softc *sc) { /* TBD */ device_printf(sc->sc_dev, "%s: TODO\n", __func__); } /* NETGEAR, dual-LED. */ void otus_led_newstate_type2(struct otus_softc *sc) { /* TBD */ device_printf(sc->sc_dev, "%s: TODO\n", __func__); } /* NETGEAR, single-LED/3 colors (blue, red, purple.) */ void otus_led_newstate_type3(struct otus_softc *sc) { #if 0 struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t state = sc->led_state; OTUS_LOCK_ASSERT(sc); if (!vap) { state = 0; /* led off */ } else if (vap->iv_state == IEEE80211_S_INIT) { state = 0; /* LED off. */ } else if (vap->iv_state == IEEE80211_S_RUN) { /* Associated, LED always on. */ if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) state = AR_LED0_ON; /* 2GHz=>Red. */ else state = AR_LED1_ON; /* 5GHz=>Blue. */ } else { /* Scanning, blink LED. */ state ^= AR_LED0_ON | AR_LED1_ON; if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) state &= ~AR_LED1_ON; else state &= ~AR_LED0_ON; } if (state != sc->led_state) { otus_write(sc, AR_GPIO_REG_PORT_DATA, state); if (otus_write_barrier(sc) == 0) sc->led_state = state; } #endif } static uint8_t zero_macaddr[IEEE80211_ADDR_LEN] = { 0,0,0,0,0,0 }; /* * Set up operating mode, MAC/BSS address and RX filter. */ static void otus_set_operating_mode(struct otus_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap; uint32_t cam_mode = AR_MAC_CAM_DEFAULTS; uint32_t rx_ctrl = AR_MAC_RX_CTRL_DEAGG | AR_MAC_RX_CTRL_SHORT_FILTER; uint32_t sniffer = AR_MAC_SNIFFER_DEFAULTS; uint32_t enc_mode = 0x78; /* XXX */ const uint8_t *macaddr; uint8_t bssid[IEEE80211_ADDR_LEN]; struct ieee80211_node *ni; OTUS_LOCK_ASSERT(sc); /* * If we're in sniffer mode or we don't have a MAC * address assigned, ensure it gets reset to all-zero. */ IEEE80211_ADDR_COPY(bssid, zero_macaddr); vap = TAILQ_FIRST(&ic->ic_vaps); macaddr = vap ? vap->iv_myaddr : ic->ic_macaddr; switch (ic->ic_opmode) { case IEEE80211_M_STA: if (vap) { ni = ieee80211_ref_node(vap->iv_bss); IEEE80211_ADDR_COPY(bssid, ni->ni_bssid); ieee80211_free_node(ni); } cam_mode |= AR_MAC_CAM_STA; rx_ctrl |= AR_MAC_RX_CTRL_PASS_TO_HOST; break; case IEEE80211_M_MONITOR: /* * Note: monitor mode ends up causing the MAC to * generate ACK frames for everything it sees. * So don't do that; instead just put it in STA mode * and disable RX filters. */ default: cam_mode |= AR_MAC_CAM_STA; rx_ctrl |= AR_MAC_RX_CTRL_PASS_TO_HOST; break; } /* * TODO: if/when we do hardware encryption, ensure it's * disabled if the NIC is in monitor mode. */ otus_write(sc, AR_MAC_REG_SNIFFER, sniffer); otus_write(sc, AR_MAC_REG_CAM_MODE, cam_mode); otus_write(sc, AR_MAC_REG_ENCRYPTION, enc_mode); otus_write(sc, AR_MAC_REG_RX_CONTROL, rx_ctrl); otus_set_macaddr(sc, macaddr); otus_set_bssid(sc, bssid); /* XXX barrier? */ } static void otus_set_rx_filter(struct otus_softc *sc) { // struct ieee80211com *ic = &sc->sc_ic; OTUS_LOCK_ASSERT(sc); #if 0 if (ic->ic_allmulti > 0 || ic->ic_promisc > 0 || ic->ic_opmode == IEEE80211_M_MONITOR) { otus_write(sc, AR_MAC_REG_FRAMETYPE_FILTER, 0xff00ffff); } else { #endif /* Filter any control frames, BAR is bit 24. */ otus_write(sc, AR_MAC_REG_FRAMETYPE_FILTER, 0x0500ffff); #if 0 } #endif } int otus_init(struct otus_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; int error; OTUS_UNLOCK_ASSERT(sc); OTUS_LOCK(sc); /* Drain any pending TX frames */ otus_drain_mbufq(sc); /* Init MAC */ if ((error = otus_init_mac(sc)) != 0) { OTUS_UNLOCK(sc); device_printf(sc->sc_dev, "%s: could not initialize MAC\n", __func__); return error; } otus_set_operating_mode(sc); otus_set_rx_filter(sc); (void) otus_set_operating_mode(sc); sc->bb_reset = 1; /* Force cold reset. */ if ((error = otus_set_chan(sc, ic->ic_curchan, 0)) != 0) { OTUS_UNLOCK(sc); device_printf(sc->sc_dev, "%s: could not set channel\n", __func__); return error; } /* Start Rx. */ otus_write(sc, AR_MAC_REG_DMA_TRIGGER, 0x100); (void)otus_write_barrier(sc); sc->sc_running = 1; OTUS_UNLOCK(sc); return 0; } void otus_stop(struct otus_softc *sc) { #if 0 int s; #endif OTUS_UNLOCK_ASSERT(sc); OTUS_LOCK(sc); sc->sc_running = 0; sc->sc_tx_timer = 0; OTUS_UNLOCK(sc); taskqueue_drain_timeout(taskqueue_thread, &sc->scan_to); taskqueue_drain_timeout(taskqueue_thread, &sc->calib_to); taskqueue_drain(taskqueue_thread, &sc->tx_task); OTUS_LOCK(sc); sc->sc_running = 0; /* Stop Rx. */ otus_write(sc, AR_MAC_REG_DMA_TRIGGER, 0); (void)otus_write_barrier(sc); /* Drain any pending TX frames */ otus_drain_mbufq(sc); OTUS_UNLOCK(sc); } diff --git a/sys/dev/rtwn/usb/rtwn_usb_rx.c b/sys/dev/rtwn/usb/rtwn_usb_rx.c index 30921bdc2101..5db967ddcc18 100644 --- a/sys/dev/rtwn/usb/rtwn_usb_rx.c +++ b/sys/dev/rtwn/usb/rtwn_usb_rx.c @@ -1,446 +1,443 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * 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 #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #include #include #include #include #include #include #include #include #include #include #include static struct mbuf * rtwn_rxeof(struct rtwn_softc *, struct rtwn_data *, uint8_t *, int); static int rtwn_rx_check_pre_alloc(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat) { uint32_t rxdw0; int pktlen; RTWN_ASSERT_LOCKED(sc); /* * don't pass packets to the ieee80211 framework if the driver isn't * RUNNING. */ if (!(sc->sc_flags & RTWN_RUNNING)) return (-1); rxdw0 = le32toh(stat->rxdw0); if (__predict_false(rxdw0 & (RTWN_RXDW0_CRCERR | RTWN_RXDW0_ICVERR))) { /* * This should not happen since we setup our Rx filter * to not receive these frames. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: RX flags error (%s)\n", __func__, rxdw0 & RTWN_RXDW0_CRCERR ? "CRC" : "ICV"); return (-1); } pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack))) { /* * Should not happen (because of Rx filter setup). */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: frame is too short: %d\n", __func__, pktlen); return (-1); } return (0); } static struct mbuf * rtwn_rx_copy_to_mbuf(struct rtwn_softc *sc, struct rtwn_rx_stat_common *stat, int totlen) { struct ieee80211com *ic = &sc->sc_ic; struct mbuf *m; RTWN_ASSERT_LOCKED(sc); /* Dump Rx descriptor. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC, "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X\n", __func__, le32toh(stat->rxdw0), le32toh(stat->rxdw1), le32toh(stat->rxdw2), le32toh(stat->rxdw3), le32toh(stat->rxdw4), le32toh(stat->tsf_low)); if (rtwn_rx_check_pre_alloc(sc, stat) != 0) 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\n", __func__); goto fail; } /* Finalize mbuf. */ memcpy(mtod(m, uint8_t *), (uint8_t *)stat, totlen); m->m_pkthdr.len = m->m_len = totlen; if (rtwn_check_frame(sc, m) != 0) { m_freem(m); goto fail; } return (m); fail: counter_u64_add(ic->ic_ierrors, 1); return (NULL); } static struct mbuf * rtwn_rxeof_fragmented(struct rtwn_usb_softc *uc, struct rtwn_data *data, uint8_t *buf, int len) { struct rtwn_softc *sc = &uc->uc_sc; struct ieee80211com *ic = &sc->sc_ic; struct rtwn_rx_stat_common *stat = &uc->uc_rx_stat; uint32_t rxdw0; int totlen, pktlen, infosz, min_len; int orig_len = len; int alloc_mbuf = 0; /* Check if Rx descriptor is not truncated. */ if (uc->uc_rx_stat_len < sizeof(*stat)) { min_len = min(sizeof(*stat) - uc->uc_rx_stat_len, len); memcpy((uint8_t *)stat + uc->uc_rx_stat_len, buf, min_len); uc->uc_rx_stat_len += min_len; buf += min_len; len -= min_len; if (uc->uc_rx_stat_len < sizeof(*stat)) goto end; KASSERT(data->m == NULL, ("%s: data->m != NULL!\n", __func__)); alloc_mbuf = 1; /* Dump Rx descriptor. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC, "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, " "tsfl %08X\n", __func__, le32toh(stat->rxdw0), le32toh(stat->rxdw1), le32toh(stat->rxdw2), le32toh(stat->rxdw3), le32toh(stat->rxdw4), le32toh(stat->tsf_low)); } rxdw0 = le32toh(stat->rxdw0); pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; totlen = sizeof(*stat) + infosz + pktlen; if (alloc_mbuf) { if (rtwn_rx_check_pre_alloc(sc, stat) == 0) { data->m = m_getm(NULL, totlen, M_NOWAIT, MT_DATA); if (data->m != NULL) { m_copyback(data->m, 0, uc->uc_rx_stat_len, (caddr_t)stat); if (rtwn_check_frame(sc, data->m) != 0) { m_freem(data->m); data->m = NULL; counter_u64_add(ic->ic_ierrors, 1); } } else counter_u64_add(ic->ic_ierrors, 1); } else counter_u64_add(ic->ic_ierrors, 1); uc->uc_rx_off = sizeof(*stat); } /* If mbuf allocation fails just discard the data. */ min_len = min(totlen - uc->uc_rx_off, len); if (data->m != NULL) m_copyback(data->m, uc->uc_rx_off, min_len, buf); uc->uc_rx_off += min_len; if (uc->uc_rx_off == totlen) { /* Align next frame. */ min_len = rtwn_usb_align_rx(uc, orig_len - len + min_len, orig_len); min_len -= (orig_len - len); KASSERT(len >= min_len, ("%s: len (%d) < min_len (%d)!\n", __func__, len, min_len)); /* Clear mbuf stats. */ uc->uc_rx_stat_len = 0; uc->uc_rx_off = 0; } len -= min_len; buf += min_len; end: if (uc->uc_rx_stat_len == 0) return (rtwn_rxeof(sc, data, buf, len)); else return (NULL); } static struct mbuf * rtwn_rxeof(struct rtwn_softc *sc, struct rtwn_data *data, uint8_t *buf, int len) { struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); struct rtwn_rx_stat_common *stat; struct mbuf *m, *m0 = NULL; uint32_t rxdw0; int totlen, pktlen, infosz; /* Prepend defragmented frame (if any). */ if (data->m != NULL) { m0 = m = data->m; data->m = NULL; } /* Process packets. */ while (len >= sizeof(*stat)) { stat = (struct rtwn_rx_stat_common *)buf; rxdw0 = le32toh(stat->rxdw0); pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); if (__predict_false(pktlen == 0)) break; infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; /* Make sure everything fits in xfer. */ totlen = sizeof(*stat) + infosz + pktlen; if (totlen > len) { RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: frame is fragmented (totlen %d len %d)\n", __func__, totlen, len); break; } if (m0 == NULL) m0 = m = rtwn_rx_copy_to_mbuf(sc, stat, totlen); else { m->m_nextpkt = rtwn_rx_copy_to_mbuf(sc, stat, totlen); if (m->m_nextpkt != NULL) m = m->m_nextpkt; } /* Align next frame. */ totlen = rtwn_usb_align_rx(uc, totlen, len); buf += totlen; len -= totlen; } if (len > 0) (void)rtwn_rxeof_fragmented(uc, data, buf, len); return (m0); } static struct mbuf * rtwn_report_intr(struct rtwn_usb_softc *uc, struct usb_xfer *xfer, struct rtwn_data *data) { struct rtwn_softc *sc = &uc->uc_sc; struct ieee80211com *ic = &sc->sc_ic; uint8_t *buf; int len; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); if (__predict_false(len < sizeof(struct rtwn_rx_stat_common) && uc->uc_rx_stat_len == 0)) { counter_u64_add(ic->ic_ierrors, 1); return (NULL); } buf = data->buf; if (uc->uc_rx_stat_len > 0) return (rtwn_rxeof_fragmented(uc, data, data->buf, len)); switch (rtwn_classify_intr(sc, buf, len)) { case RTWN_RX_DATA: return (rtwn_rxeof(sc, data, buf, len)); case RTWN_RX_TX_REPORT: if (sc->sc_ratectl != RTWN_RATECTL_NET80211) { /* shouldn't happen */ device_printf(sc->sc_dev, "%s called while ratectl = %d!\n", __func__, sc->sc_ratectl); break; } RTWN_NT_LOCK(sc); rtwn_handle_tx_report(sc, buf, len); RTWN_NT_UNLOCK(sc); #ifdef IEEE80211_SUPPORT_SUPERG /* * NB: this will executed only when 'report' bit is set. */ if (sc->sc_tx_n_active > 0 && --sc->sc_tx_n_active <= 1) rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all); #endif break; case RTWN_RX_OTHER: rtwn_handle_c2h_report(sc, buf, len); break; default: /* NOTREACHED */ KASSERT(0, ("unknown Rx classification code")); break; } return (NULL); } static struct ieee80211_node * rtwn_rx_frame(struct rtwn_softc *sc, struct mbuf *m) { struct rtwn_rx_stat_common stat; /* Imitate PCIe layout. */ m_copydata(m, 0, sizeof(stat), (caddr_t)&stat); m_adj(m, sizeof(stat)); return (rtwn_rx_common(sc, m, &stat)); } void rtwn_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { - struct epoch_tracker et; struct rtwn_usb_softc *uc = usbd_xfer_softc(xfer); struct rtwn_softc *sc = &uc->uc_sc; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct mbuf *m0, *m = NULL, *next; struct rtwn_data *data; RTWN_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&uc->uc_rx_active); if (data == NULL) goto tr_setup; STAILQ_REMOVE_HEAD(&uc->uc_rx_active, next); m = rtwn_report_intr(uc, xfer, data); STAILQ_INSERT_TAIL(&uc->uc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&uc->uc_rx_inactive); if (data == NULL) { KASSERT(m == NULL, ("mbuf isn't NULL")); goto finish; } STAILQ_REMOVE_HEAD(&uc->uc_rx_inactive, next); STAILQ_INSERT_TAIL(&uc->uc_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. */ m0 = m; while (m != NULL) { M_ASSERTPKTHDR(m); m->m_pkthdr.PH_loc.ptr = rtwn_rx_frame(sc, m); m = m->m_nextpkt; } - NET_EPOCH_ENTER(et); RTWN_UNLOCK(sc); m = m0; while (m != NULL) { next = m->m_nextpkt; m->m_nextpkt = NULL; ni = m->m_pkthdr.PH_loc.ptr; m->m_pkthdr.PH_loc.ptr = NULL; if (ni != NULL) { (void)ieee80211_input_mimo(ni, m); ieee80211_free_node(ni); } else { (void)ieee80211_input_mimo_all(ic, m); } m = next; } RTWN_LOCK(sc); - NET_EPOCH_EXIT(et); break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&uc->uc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&uc->uc_rx_active, next); STAILQ_INSERT_TAIL(&uc->uc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { /* XXX restart device if frame was fragmented? */ usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } break; } finish: /* Kick-start more transmit in case we stalled */ rtwn_start(sc); } diff --git a/sys/dev/usb/wlan/if_rsu.c b/sys/dev/usb/wlan/if_rsu.c index 7a1e85f652a2..e000d1fb5992 100644 --- a/sys/dev/usb/wlan/if_rsu.c +++ b/sys/dev/usb/wlan/if_rsu.c @@ -1,3765 +1,3762 @@ /* $OpenBSD: if_rsu.c,v 1.17 2013/04/15 09:23:01 mglocker Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * * 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 /* * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU. * * TODO: * o tx a-mpdu * o hostap / ibss / mesh * o power-save operation */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include /* XXX */ #include #define RSU_RATE_IS_CCK RTWN_RATE_IS_CCK #ifdef USB_DEBUG static int rsu_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW | CTLFLAG_MPSAFE, 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(SITECOMEU, WLA1000), 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[IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); 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) }; DRIVER_MODULE(rsu, uhub, rsu_driver, NULL, NULL); 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 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; case 0x3: /* "green" NIC */ sc->sc_rftype = RTL8712_RFCONFIG_1T2R; sc->sc_nrxstream = 2; sc->sc_ntxstream = 1; rft = "1T2R ('green')"; 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; if_t 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; if_setcapabilities(ifp, IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6); RSU_LOCK(sc); if (sc->sc_rx_checksum_enable) if_setcapenablebit(ifp, IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6, 0); 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_channels_default_2ghz(chans, maxchans, nchans, bands, (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) ? NET80211_CBW_FLAG_HT40 : 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 u_int rsu_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { uint32_t *mfilt = arg; uint8_t pos; pos = rsu_get_multi_pos(LLADDR(sdl)); mfilt[pos / 32] |= (1 << (pos % 32)); return (1); } 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; /* * Merge multicast addresses to form the hardware filter. */ mfilt[0] = mfilt[1] = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if_foreach_llmaddr(vap->iv_ifp, rsu_hash_maddr, &mfilt); } 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, ni); 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); } CTASSERT(MCLBYTES > sizeof(struct ieee80211_frame)); 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; uint32_t ieslen; uint32_t pktlen; if (__predict_false(len < sizeof(*bss))) return; bss = (struct ndis_wlan_bssid_ex *)buf; ieslen = le32toh(bss->ieslen); /* range check length of information element */ if (__predict_false(ieslen > (uint32_t)(len - sizeof(*bss)))) 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), ieslen, 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! */ if (__predict_false(ieslen > (size_t)(MCLBYTES - sizeof(*wh)))) return; pktlen = sizeof(*wh) + ieslen; 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], 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_BAND; 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); rxs.c_band = 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); } /* 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 (rate < 12) { rxs.c_rate = ridx2rate[rate]; if (RSU_RATE_IS_CCK(rate)) rxs.c_pktflags |= IEEE80211_RX_F_CCK; else rxs.c_pktflags |= IEEE80211_RX_F_OFDM; } else { rxs.c_rate = IEEE80211_RATE_MCS | (rate - 12); rxs.c_pktflags |= IEEE80211_RX_F_HT; } 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; tap->wr_rate = rxs.c_rate; tap->wr_dbm_antsignal = rssi; }; (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 epoch_tracker et; 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. */ - NET_EPOCH_ENTER(et); 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; } - NET_EPOCH_EXIT(et); 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) { const struct ieee80211_txparam *tp = ni->ni_txparms; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; struct r92s_tx_desc *txd; uint8_t rate, ridx, type, cipher, qos; int prio = 0; uint8_t which; int hasqos; int ismcast; int xferlen; int qid; RSU_ASSERT_LOCKED(sc); wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: data=%p, m=%p\n", __func__, data, m0); /* Choose a TX rate index. */ if (type == IEEE80211_FC0_TYPE_MGT || type == IEEE80211_FC0_TYPE_CTL || (m0->m_flags & M_EAPOL) != 0) rate = tp->mgmtrate; else if (ismcast) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = 0; if (rate != 0) ridx = rate2ridx(rate); 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; qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; } 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; qos = 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 (ismcast) txd->txdw2 |= htole32(R92S_TXDW2_BMCAST); if (!ismcast && (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK)) { txd->txdw2 |= htole32(R92S_TXDW2_RTY_LMT_ENA); txd->txdw2 |= htole32(SM(R92S_TXDW2_RTY_LMT, tp->maxretry)); } /* Force mgmt / mcast / ucast rate if needed. */ if (rate != 0) { /* Data rate fallback limit (max). */ txd->txdw5 |= htole32(SM(R92S_TXDW5_DATARATE_FB_LMT, 0x1f)); txd->txdw5 |= htole32(SM(R92S_TXDW5_DATARATE, ridx)); txd->txdw4 |= htole32(R92S_TXDW4_DRVRATE); } /* * 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; ieee80211_radiotap_tx(vap, m0); } xferlen = sizeof(*txd) + m0->m_pkthdr.len; KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__)); 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) { if_t ifp = vap->iv_ifp; if_setcapenablebit(ifp, 0, IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6); if_setcapenablebit(ifp, rxmask, 0); } 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); } CTASSERT(sizeof(size_t) >= sizeof(uint32_t)); 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; uint32_t 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 (imemsz > (size_t)(size - sizeof(*hdr)) || ememsz > (size_t)(size - sizeof(*hdr) - imemsz)) { 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. */ 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); }