diff --git a/sys/dev/usb/wlan/if_mtw.c b/sys/dev/usb/wlan/if_mtw.c index 137590651948..6967e5081542 100644 --- a/sys/dev/usb/wlan/if_mtw.c +++ b/sys/dev/usb/wlan/if_mtw.c @@ -1,4685 +1,4690 @@ /*- * Copyright (c) 2008-2010 Damien Bergamini * Copyright (c) 2013-2014 Kevin Lo * Copyright (c) 2021 James Hastings * Ported to FreeBSD by Jesper Schmitz Mouridsen jsm@FreeBSD.org * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * MediaTek MT7601U 802.11b/g/n WLAN. */ #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 #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR mtw_debug #include #include #include "if_mtwreg.h" #include "if_mtwvar.h" #define MTW_DEBUG #ifdef MTW_DEBUG int mtw_debug; static SYSCTL_NODE(_hw_usb, OID_AUTO, mtw, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB mtw"); SYSCTL_INT(_hw_usb_mtw, OID_AUTO, debug, CTLFLAG_RWTUN, &mtw_debug, 0, "mtw debug level"); enum { MTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ MTW_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ MTW_DEBUG_RECV = 0x00000004, /* basic recv operation */ MTW_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ MTW_DEBUG_STATE = 0x00000010, /* 802.11 state transitions */ MTW_DEBUG_RATE = 0x00000020, /* rate adaptation */ MTW_DEBUG_USB = 0x00000040, /* usb requests */ MTW_DEBUG_FIRMWARE = 0x00000080, /* firmware(9) loading debug */ MTW_DEBUG_BEACON = 0x00000100, /* beacon handling */ MTW_DEBUG_INTR = 0x00000200, /* ISR */ MTW_DEBUG_TEMP = 0x00000400, /* temperature calibration */ MTW_DEBUG_ROM = 0x00000800, /* various ROM info */ MTW_DEBUG_KEY = 0x00001000, /* crypto keys management */ MTW_DEBUG_TXPWR = 0x00002000, /* dump Tx power values */ MTW_DEBUG_RSSI = 0x00004000, /* dump RSSI lookups */ MTW_DEBUG_RESET = 0x00008000, /* initialization progress */ MTW_DEBUG_CALIB = 0x00010000, /* calibration progress */ MTW_DEBUG_CMD = 0x00020000, /* command queue */ MTW_DEBUG_ANY = 0xffffffff }; #define MTW_DPRINTF(_sc, _m, ...) \ do { \ if (mtw_debug & (_m)) \ device_printf((_sc)->sc_dev, __VA_ARGS__); \ } while (0) #else #define MTW_DPRINTF(_sc, _m, ...) \ do { \ (void)_sc; \ } while (0) #endif #define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh) /* NB: "11" is the maximum number of padding bytes needed for Tx */ #define MTW_MAX_TXSZ \ (sizeof(struct mtw_txd) + sizeof(struct mtw_txwi) + MCLBYTES + 11) /* * Because of LOR in mtw_key_delete(), use atomic instead. * '& MTW_CMDQ_MASQ' is to loop cmdq[]. */ #define MTW_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & MTW_CMDQ_MASQ) static const STRUCT_USB_HOST_ID mtw_devs[] = { #define MTW_DEV(v, p) \ { \ USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) \ } MTW_DEV(EDIMAX, MT7601U), MTW_DEV(RALINK, MT7601U), MTW_DEV(XIAOMI, MT7601U) }; #undef MTW_DEV static device_probe_t mtw_match; static device_attach_t mtw_attach; static device_detach_t mtw_detach; static usb_callback_t mtw_bulk_rx_callback; static usb_callback_t mtw_bulk_tx_callback0; static usb_callback_t mtw_bulk_tx_callback1; static usb_callback_t mtw_bulk_tx_callback2; static usb_callback_t mtw_bulk_tx_callback3; static usb_callback_t mtw_bulk_tx_callback4; static usb_callback_t mtw_bulk_tx_callback5; static usb_callback_t mtw_fw_callback; static void mtw_autoinst(void *, struct usb_device *, struct usb_attach_arg *); static int mtw_driver_loaded(struct module *, int, void *); static void mtw_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index); static struct ieee80211vap *mtw_vap_create(struct ieee80211com *, const char[IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t[IEEE80211_ADDR_LEN], const uint8_t[IEEE80211_ADDR_LEN]); static void mtw_vap_delete(struct ieee80211vap *); static void mtw_cmdq_cb(void *, int); static void mtw_setup_tx_list(struct mtw_softc *, struct mtw_endpoint_queue *); static void mtw_unsetup_tx_list(struct mtw_softc *, struct mtw_endpoint_queue *); static void mtw_load_microcode(void *arg); static usb_error_t mtw_do_request(struct mtw_softc *, struct usb_device_request *, void *); static int mtw_read(struct mtw_softc *, uint16_t, uint32_t *); static int mtw_read_region_1(struct mtw_softc *, uint16_t, uint8_t *, int); static int mtw_write_2(struct mtw_softc *, uint16_t, uint16_t); static int mtw_write(struct mtw_softc *, uint16_t, uint32_t); static int mtw_write_region_1(struct mtw_softc *, uint16_t, uint8_t *, int); static int mtw_set_region_4(struct mtw_softc *, uint16_t, uint32_t, int); static int mtw_efuse_read_2(struct mtw_softc *, uint16_t, uint16_t *); static int mtw_bbp_read(struct mtw_softc *, uint8_t, uint8_t *); static int mtw_bbp_write(struct mtw_softc *, uint8_t, uint8_t); static int mtw_mcu_cmd(struct mtw_softc *sc, uint8_t cmd, void *buf, int len); static void mtw_get_txpower(struct mtw_softc *); static int mtw_read_eeprom(struct mtw_softc *); static struct ieee80211_node *mtw_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static int mtw_media_change(if_t); static int mtw_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int mtw_wme_update(struct ieee80211com *); static void mtw_key_set_cb(void *); static int mtw_key_set(struct ieee80211vap *, struct ieee80211_key *); static void mtw_key_delete_cb(void *); static int mtw_key_delete(struct ieee80211vap *, struct ieee80211_key *); static void mtw_ratectl_to(void *); static void mtw_ratectl_cb(void *, int); static void mtw_drain_fifo(void *); static void mtw_iter_func(void *, struct ieee80211_node *); static void mtw_newassoc_cb(void *); static void mtw_newassoc(struct ieee80211_node *, int); static int mtw_mcu_radio(struct mtw_softc *sc, int func, uint32_t val); static void mtw_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, const struct ieee80211_rx_stats *, int, int); static void mtw_rx_frame(struct mtw_softc *, struct mbuf *, uint32_t); static void mtw_tx_free(struct mtw_endpoint_queue *pq, struct mtw_tx_data *, int); static void mtw_set_tx_desc(struct mtw_softc *, struct mtw_tx_data *); static int mtw_tx(struct mtw_softc *, struct mbuf *, struct ieee80211_node *); static int mtw_tx_mgt(struct mtw_softc *, struct mbuf *, struct ieee80211_node *); static int mtw_sendprot(struct mtw_softc *, const struct mbuf *, struct ieee80211_node *, int, int); static int mtw_tx_param(struct mtw_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static int mtw_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static int mtw_transmit(struct ieee80211com *, struct mbuf *); static void mtw_start(struct mtw_softc *); static void mtw_parent(struct ieee80211com *); static void mtw_select_chan_group(struct mtw_softc *, int); static int mtw_set_chan(struct mtw_softc *, struct ieee80211_channel *); static void mtw_set_channel(struct ieee80211com *); static void mtw_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static void mtw_scan_start(struct ieee80211com *); static void mtw_scan_end(struct ieee80211com *); static void mtw_update_beacon(struct ieee80211vap *, int); static void mtw_update_beacon_cb(void *); static void mtw_updateprot(struct ieee80211com *); static void mtw_updateprot_cb(void *); static void mtw_usb_timeout_cb(void *); static int mtw_reset(struct mtw_softc *sc); static void mtw_enable_tsf_sync(struct mtw_softc *); static void mtw_enable_mrr(struct mtw_softc *); static void mtw_set_txpreamble(struct mtw_softc *); static void mtw_set_basicrates(struct mtw_softc *); static void mtw_set_leds(struct mtw_softc *, uint16_t); static void mtw_set_bssid(struct mtw_softc *, const uint8_t *); static void mtw_set_macaddr(struct mtw_softc *, const uint8_t *); static void mtw_updateslot(struct ieee80211com *); static void mtw_updateslot_cb(void *); static void mtw_update_mcast(struct ieee80211com *); static int8_t mtw_rssi2dbm(struct mtw_softc *, uint8_t, uint8_t); static void mtw_update_promisc_locked(struct mtw_softc *); static void mtw_update_promisc(struct ieee80211com *); static int mtw_txrx_enable(struct mtw_softc *); static void mtw_init_locked(struct mtw_softc *); static void mtw_stop(void *); static void mtw_delay(struct mtw_softc *, u_int); static void mtw_update_chw(struct ieee80211com *ic); static int mtw_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); static eventhandler_tag mtw_etag; static const struct { uint8_t reg; uint8_t val; } mt7601_rf_bank0[] = { MT7601_BANK0_RF }, mt7601_rf_bank4[] = { MT7601_BANK4_RF }, mt7601_rf_bank5[] = { MT7601_BANK5_RF }; static const struct { uint32_t reg; uint32_t val; } mt7601_def_mac[] = { MT7601_DEF_MAC }; static const struct { uint8_t reg; uint8_t val; } mt7601_def_bbp[] = { MT7601_DEF_BBP }; static const struct { u_int chan; uint8_t r17, r18, r19, r20; } mt7601_rf_chan[] = { MT7601_RF_CHAN }; static const struct usb_config mtw_config[MTW_N_XFER] = { [MTW_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = MTW_MAX_RXSZ, .flags = {.pipe_bof = 1, .short_xfer_ok = 1,}, .callback = mtw_bulk_rx_callback, }, [MTW_BULK_TX_BE] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MTW_MAX_TXSZ, .flags = {.pipe_bof = 1, .force_short_xfer = 0,}, .callback = mtw_bulk_tx_callback0, .timeout = 5000, /* ms */ }, [MTW_BULK_TX_BK] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MTW_MAX_TXSZ, .flags = {.pipe_bof = 1, .force_short_xfer = 1,}, .callback = mtw_bulk_tx_callback1, .timeout = 5000, /* ms */ }, [MTW_BULK_TX_VI] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MTW_MAX_TXSZ, .flags = {.pipe_bof = 1, .force_short_xfer = 1,}, .callback = mtw_bulk_tx_callback2, .timeout = 5000, /* ms */ }, [MTW_BULK_TX_VO] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MTW_MAX_TXSZ, .flags = {.pipe_bof = 1, .force_short_xfer = 1,}, .callback = mtw_bulk_tx_callback3, .timeout = 5000, /* ms */ }, [MTW_BULK_TX_HCCA] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MTW_MAX_TXSZ, .flags = {.pipe_bof = 1, .force_short_xfer = 1, .no_pipe_ok = 1,}, .callback = mtw_bulk_tx_callback4, .timeout = 5000, /* ms */ }, [MTW_BULK_TX_PRIO] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MTW_MAX_TXSZ, .flags = {.pipe_bof = 1, .force_short_xfer = 1, .no_pipe_ok = 1,}, .callback = mtw_bulk_tx_callback5, .timeout = 5000, /* ms */ }, [MTW_BULK_FW_CMD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = 0x2c44, .flags = {.pipe_bof = 1, .force_short_xfer = 1, .no_pipe_ok = 1,}, .callback = mtw_fw_callback, }, [MTW_BULK_RAW_TX] = { .type = UE_BULK, .ep_index = 0, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MTW_MAX_TXSZ, .flags = {.pipe_bof = 1, .force_short_xfer = 1, .no_pipe_ok = 1,}, .callback = mtw_bulk_tx_callback0, .timeout = 5000, /* ms */ }, }; static uint8_t mtw_wme_ac_xfer_map[4] = { [WME_AC_BE] = MTW_BULK_TX_BE, [WME_AC_BK] = MTW_BULK_TX_BK, [WME_AC_VI] = MTW_BULK_TX_VI, [WME_AC_VO] = MTW_BULK_TX_VO, }; static void mtw_autoinst(void *arg, struct usb_device *udev, struct usb_attach_arg *uaa) { struct usb_interface *iface; struct usb_interface_descriptor *id; if (uaa->dev_state != UAA_DEV_READY) return; iface = usbd_get_iface(udev, 0); if (iface == NULL) return; id = iface->idesc; if (id == NULL || id->bInterfaceClass != UICLASS_MASS) return; if (usbd_lookup_id_by_uaa(mtw_devs, sizeof(mtw_devs), uaa)) return; if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0) uaa->dev_state = UAA_DEV_EJECTING; } static int mtw_driver_loaded(struct module *mod, int what, void *arg) { switch (what) { case MOD_LOAD: mtw_etag = EVENTHANDLER_REGISTER(usb_dev_configured, mtw_autoinst, NULL, EVENTHANDLER_PRI_ANY); break; case MOD_UNLOAD: EVENTHANDLER_DEREGISTER(usb_dev_configured, mtw_etag); break; default: return (EOPNOTSUPP); } return (0); } static const char * mtw_get_rf(int rev) { switch (rev) { case MT7601_RF_7601: return ("MT7601"); case MT7610_RF_7610: return ("MT7610"); case MT7612_RF_7612: return ("MT7612"); } return ("unknown"); } static int mtw_wlan_enable(struct mtw_softc *sc, int enable) { uint32_t tmp; int error = 0; if (enable) { mtw_read(sc, MTW_WLAN_CTRL, &tmp); if (sc->asic_ver == 0x7612) tmp &= ~0xfffff000; tmp &= ~MTW_WLAN_CLK_EN; tmp |= MTW_WLAN_EN; mtw_write(sc, MTW_WLAN_CTRL, tmp); mtw_delay(sc, 2); tmp |= MTW_WLAN_CLK_EN; if (sc->asic_ver == 0x7612) { tmp |= (MTW_WLAN_RESET | MTW_WLAN_RESET_RF); } mtw_write(sc, MTW_WLAN_CTRL, tmp); mtw_delay(sc, 2); mtw_read(sc, MTW_OSC_CTRL, &tmp); tmp |= MTW_OSC_EN; mtw_write(sc, MTW_OSC_CTRL, tmp); tmp |= MTW_OSC_CAL_REQ; mtw_write(sc, MTW_OSC_CTRL, tmp); } else { mtw_read(sc, MTW_WLAN_CTRL, &tmp); tmp &= ~(MTW_WLAN_CLK_EN | MTW_WLAN_EN); mtw_write(sc, MTW_WLAN_CTRL, tmp); mtw_read(sc, MTW_OSC_CTRL, &tmp); tmp &= ~MTW_OSC_EN; mtw_write(sc, MTW_OSC_CTRL, tmp); } return (error); } static int mtw_read_cfg(struct mtw_softc *sc, uint16_t reg, uint32_t *val) { usb_device_request_t req; uint32_t tmp; uint16_t actlen; int error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = MTW_READ_CFG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 4); error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, &tmp, 0, &actlen, 1000); if (error == 0) *val = le32toh(tmp); else *val = 0xffffffff; return (error); } static int mtw_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != 0) return (ENXIO); if (uaa->info.bIfaceIndex != 0) return (ENXIO); return (usbd_lookup_id_by_uaa(mtw_devs, sizeof(mtw_devs), uaa)); } static int mtw_attach(device_t self) { struct mtw_softc *sc = device_get_softc(self); struct usb_attach_arg *uaa = device_get_ivars(self); struct ieee80211com *ic = &sc->sc_ic; uint32_t ver; int i, ret; uint32_t tmp; uint8_t iface_index; int ntries, error; device_set_usb_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; sc->sc_sent = 0; mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); iface_index = 0; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, mtw_config, MTW_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 detach; } for (i = 0; i < 4; i++) { sc->txd_fw[i] = (struct mtw_txd_fw *) malloc(sizeof(struct mtw_txd_fw), M_USBDEV, M_NOWAIT | M_ZERO); } MTW_LOCK(sc); sc->sc_idx = 0; mbufq_init(&sc->sc_snd, ifqmaxlen); /*enable WLAN core */ if ((error = mtw_wlan_enable(sc, 1)) != 0) { device_printf(sc->sc_dev, "could not enable WLAN core\n"); return (ENXIO); } /* wait for the chip to settle */ DELAY(100); for (ntries = 0; ntries < 100; ntries++) { if (mtw_read(sc, MTW_ASIC_VER, &ver) != 0) { goto detach; } if (ver != 0 && ver != 0xffffffff) break; DELAY(10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for NIC to initialize\n"); goto detach; } sc->asic_ver = ver >> 16; sc->asic_rev = ver & 0xffff; DELAY(100); if (sc->asic_ver != 0x7601) { device_printf(sc->sc_dev, "Your revision 0x04%x is not supported yet\n", sc->asic_rev); goto detach; } if (mtw_read(sc, MTW_MAC_VER_ID, &tmp) != 0) goto detach; sc->mac_rev = tmp & 0xffff; mtw_load_microcode(sc); ret = msleep(&sc->fwloading, &sc->sc_mtx, 0, "fwload", 3 * hz); if (ret == EWOULDBLOCK || sc->fwloading != 1) { device_printf(sc->sc_dev, "timeout waiting for MCU to initialize\n"); goto detach; } sc->sc_srom_read = mtw_efuse_read_2; /* retrieve RF rev. no and various other things from EEPROM */ mtw_read_eeprom(sc); device_printf(sc->sc_dev, "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n", sc->asic_ver, sc->mac_rev, mtw_get_rf(sc->rf_rev), sc->ntxchains, sc->nrxchains, ether_sprintf(ic->ic_macaddr)); DELAY(100); //mtw_set_leds(sc,5); // mtw_mcu_radio(sc,0x31,0); MTW_UNLOCK(sc); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(self); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ ic->ic_caps = IEEE80211_C_STA | /* station mode supported */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_WDS | /* 4-address traffic works */ IEEE80211_C_MBSS | IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_WME | /* WME */ IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */ device_printf(sc->sc_dev, "[HT] Enabling 802.11n\n"); ic->ic_htcaps = IEEE80211_HTC_HT | IEEE80211_HTC_AMPDU | IEEE80211_HTC_AMSDU | IEEE80211_HTCAP_MAXAMSDU_3839 | IEEE80211_HTCAP_SMPS_OFF; ic->ic_rxstream = sc->nrxchains; ic->ic_txstream = sc->ntxchains; ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_AES_CCM | IEEE80211_CRYPTO_AES_OCB | IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_TKIPMIC; ic->ic_flags |= IEEE80211_F_DATAPAD; ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; + ic->ic_flags_ext |= IEEE80211_FEXT_SEQNO_OFFLOAD; mtw_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); ieee80211_ifattach(ic); ic->ic_scan_start = mtw_scan_start; ic->ic_scan_end = mtw_scan_end; ic->ic_set_channel = mtw_set_channel; ic->ic_getradiocaps = mtw_getradiocaps; ic->ic_node_alloc = mtw_node_alloc; ic->ic_newassoc = mtw_newassoc; ic->ic_update_mcast = mtw_update_mcast; ic->ic_updateslot = mtw_updateslot; ic->ic_wme.wme_update = mtw_wme_update; ic->ic_raw_xmit = mtw_raw_xmit; ic->ic_update_promisc = mtw_update_promisc; ic->ic_vap_create = mtw_vap_create; ic->ic_vap_delete = mtw_vap_delete; ic->ic_transmit = mtw_transmit; ic->ic_parent = mtw_parent; ic->ic_update_chw = mtw_update_chw; ic->ic_ampdu_enable = mtw_ampdu_enable; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), MTW_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), MTW_RX_RADIOTAP_PRESENT); TASK_INIT(&sc->cmdq_task, 0, mtw_cmdq_cb, sc); TASK_INIT(&sc->ratectl_task, 0, mtw_ratectl_cb, sc); usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0); if (bootverbose) ieee80211_announce(ic); return (0); detach: MTW_UNLOCK(sc); mtw_detach(self); return (ENXIO); } static void mtw_drain_mbufq(struct mtw_softc *sc) { struct mbuf *m; struct ieee80211_node *ni; MTW_LOCK_ASSERT(sc, MA_OWNED); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; ieee80211_free_node(ni); m_freem(m); } } static int mtw_detach(device_t self) { struct mtw_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; int i; MTW_LOCK(sc); mtw_reset(sc); DELAY(10000); sc->sc_detached = 1; MTW_UNLOCK(sc); /* stop all USB transfers */ for (i = 0; i < MTW_N_XFER; i++) usbd_transfer_drain(sc->sc_xfer[i]); MTW_LOCK(sc); sc->ratectl_run = MTW_RATECTL_OFF; sc->cmdq_run = sc->cmdq_key_set = MTW_CMDQ_ABORT; /* free TX list, if any */ if (ic->ic_nrunning > 0) for (i = 0; i < MTW_EP_QUEUES; i++) mtw_unsetup_tx_list(sc, &sc->sc_epq[i]); /* Free TX queue */ mtw_drain_mbufq(sc); MTW_UNLOCK(sc); if (sc->sc_ic.ic_softc == sc) { /* drain tasks */ usb_callout_drain(&sc->ratectl_ch); ieee80211_draintask(ic, &sc->cmdq_task); ieee80211_draintask(ic, &sc->ratectl_task); ieee80211_ifdetach(ic); } for (i = 0; i < 4; i++) { free(sc->txd_fw[i], M_USBDEV); } firmware_unregister("/mediatek/mt7601u"); mtx_destroy(&sc->sc_mtx); return (0); } static struct ieee80211vap * mtw_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 mtw_softc *sc = ic->ic_softc; struct mtw_vap *rvp; struct ieee80211vap *vap; int i; if (sc->rvp_cnt >= MTW_VAP_MAX) { device_printf(sc->sc_dev, "number of VAPs maxed out\n"); return (NULL); } switch (opmode) { case IEEE80211_M_STA: /* enable s/w bmiss handling for sta mode */ flags |= IEEE80211_CLONE_NOBEACONS; /* fall though */ case IEEE80211_M_IBSS: case IEEE80211_M_MONITOR: case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: /* other than WDS vaps, only one at a time */ if (!TAILQ_EMPTY(&ic->ic_vaps)) return (NULL); break; case IEEE80211_M_WDS: TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap->iv_opmode != IEEE80211_M_HOSTAP) continue; /* WDS vap's always share the local mac address. */ flags &= ~IEEE80211_CLONE_BSSID; break; } if (vap == NULL) { device_printf(sc->sc_dev, "wds only supported in ap mode\n"); return (NULL); } break; default: device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); return (NULL); } rvp = malloc(sizeof(struct mtw_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &rvp->vap; if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid) != 0) { /* out of memory */ free(rvp, M_80211_VAP); return (NULL); } vap->iv_update_beacon = mtw_update_beacon; vap->iv_max_aid = MTW_WCID_MAX; /* * The linux rt2800 driver limits 1 stream devices to a 32KB * RX AMPDU. */ if (ic->ic_rxstream > 1) vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; else vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_2; /* 2uS */ /* * To delete the right key from h/w, we need wcid. * Luckily, there is unused space in ieee80211_key{}, wk_pad, * and matching wcid will be written into there. So, cast * some spells to remove 'const' from ieee80211_key{} */ vap->iv_key_delete = (void *)mtw_key_delete; vap->iv_key_set = (void *)mtw_key_set; // override state transition machine rvp->newstate = vap->iv_newstate; vap->iv_newstate = mtw_newstate; if (opmode == IEEE80211_M_IBSS) { rvp->recv_mgmt = vap->iv_recv_mgmt; vap->iv_recv_mgmt = mtw_recv_mgmt; } ieee80211_ratectl_init(vap); ieee80211_ratectl_setinterval(vap, 1000); // 1 second /* complete setup */ ieee80211_vap_attach(vap, mtw_media_change, ieee80211_media_status, mac); /* make sure id is always unique */ for (i = 0; i < MTW_VAP_MAX; i++) { if ((sc->rvp_bmap & 1 << i) == 0) { sc->rvp_bmap |= 1 << i; rvp->rvp_id = i; break; } } if (sc->rvp_cnt++ == 0) ic->ic_opmode = opmode; if (opmode == IEEE80211_M_HOSTAP) sc->cmdq_run = MTW_CMDQ_GO; MTW_DPRINTF(sc, MTW_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n", rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt); return (vap); } static void mtw_vap_delete(struct ieee80211vap *vap) { struct mtw_vap *rvp = MTW_VAP(vap); struct ieee80211com *ic; struct mtw_softc *sc; uint8_t rvp_id; if (vap == NULL) return; ic = vap->iv_ic; sc = ic->ic_softc; MTW_LOCK(sc); m_freem(rvp->beacon_mbuf); rvp->beacon_mbuf = NULL; rvp_id = rvp->rvp_id; sc->ratectl_run &= ~(1 << rvp_id); sc->rvp_bmap &= ~(1 << rvp_id); mtw_set_region_4(sc, MTW_SKEY(rvp_id, 0), 0, 256); mtw_set_region_4(sc, (0x7800 + (rvp_id) * 512), 0, 512); --sc->rvp_cnt; MTW_DPRINTF(sc, MTW_DEBUG_STATE, "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n", vap, rvp_id, sc->rvp_bmap, sc->rvp_cnt); MTW_UNLOCK(sc); ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); } /* * There are numbers of functions need to be called in context thread. * Rather than creating taskqueue event for each of those functions, * here is all-for-one taskqueue callback function. This function * guarantees deferred functions are executed in the same order they * were enqueued. * '& MTW_CMDQ_MASQ' is to loop cmdq[]. */ static void mtw_cmdq_cb(void *arg, int pending) { struct mtw_softc *sc = arg; uint8_t i; /* call cmdq[].func locked */ MTW_LOCK(sc); for (i = sc->cmdq_exec; sc->cmdq[i].func && pending; i = sc->cmdq_exec, pending--) { MTW_DPRINTF(sc, MTW_DEBUG_CMD, "cmdq_exec=%d pending=%d\n", i, pending); if (sc->cmdq_run == MTW_CMDQ_GO) { /* * If arg0 is NULL, callback func needs more * than one arg. So, pass ptr to cmdq struct. */ if (sc->cmdq[i].arg0) sc->cmdq[i].func(sc->cmdq[i].arg0); else sc->cmdq[i].func(&sc->cmdq[i]); } sc->cmdq[i].arg0 = NULL; sc->cmdq[i].func = NULL; sc->cmdq_exec++; sc->cmdq_exec &= MTW_CMDQ_MASQ; } MTW_UNLOCK(sc); } static void mtw_setup_tx_list(struct mtw_softc *sc, struct mtw_endpoint_queue *pq) { struct mtw_tx_data *data; memset(pq, 0, sizeof(*pq)); STAILQ_INIT(&pq->tx_qh); STAILQ_INIT(&pq->tx_fh); for (data = &pq->tx_data[0]; data < &pq->tx_data[MTW_TX_RING_COUNT]; data++) { data->sc = sc; STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); } pq->tx_nfree = MTW_TX_RING_COUNT; } static void mtw_unsetup_tx_list(struct mtw_softc *sc, struct mtw_endpoint_queue *pq) { struct mtw_tx_data *data; /* make sure any subsequent use of the queues will fail */ pq->tx_nfree = 0; STAILQ_INIT(&pq->tx_fh); STAILQ_INIT(&pq->tx_qh); /* free up all node references and mbufs */ for (data = &pq->tx_data[0]; data < &pq->tx_data[MTW_TX_RING_COUNT]; data++) { if (data->m != NULL) { m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int mtw_write_ivb(struct mtw_softc *sc, void *buf, uint16_t len) { usb_device_request_t req; uint16_t actlen; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = MTW_RESET; USETW(req.wValue, 0x12); USETW(req.wIndex, 0); USETW(req.wLength, len); int error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, buf, 0, &actlen, 1000); return (error); } static int mtw_write_cfg(struct mtw_softc *sc, uint16_t reg, uint32_t val) { usb_device_request_t req; int error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = MTW_WRITE_CFG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 4); val = htole32(val); error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &val); return (error); } static int mtw_usb_dma_write(struct mtw_softc *sc, uint32_t val) { // if (sc->asic_ver == 0x7612) // return mtw_write_cfg(sc, MTW_USB_U3DMA_CFG, val); // else return (mtw_write(sc, MTW_USB_DMA_CFG, val)); } static void mtw_ucode_setup(struct mtw_softc *sc) { mtw_usb_dma_write(sc, (MTW_USB_TX_EN | MTW_USB_RX_EN)); mtw_write(sc, MTW_FCE_PSE_CTRL, 1); mtw_write(sc, MTW_TX_CPU_FCE_BASE, 0x400230); mtw_write(sc, MTW_TX_CPU_FCE_MAX_COUNT, 1); mtw_write(sc, MTW_MCU_FW_IDX, 1); mtw_write(sc, MTW_FCE_PDMA, 0x44); mtw_write(sc, MTW_FCE_SKIP_FS, 3); } static int mtw_ucode_write(struct mtw_softc *sc, const uint8_t *fw, const uint8_t *ivb, int32_t len, uint32_t offset) { // struct usb_attach_arg *uaa = device_get_ivars(sc->sc_dev); #if 0 // firmware not tested if (sc->asic_ver == 0x7612 && offset >= 0x90000) blksz = 0x800; /* MT7612 ROM Patch */ xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == NULL) { error = ENOMEM; goto fail; } buf = usbd_alloc_buffer(xfer, blksz + 12); if (buf == NULL) { error = ENOMEM; goto fail; } #endif int mlen; int idx = 0; mlen = 0x2c44; while (len > 0) { if (len < 0x2c44 && len > 0) { mlen = len; } sc->txd_fw[idx]->len = htole16(mlen); sc->txd_fw[idx]->flags = htole16(MTW_TXD_DATA | MTW_TXD_MCU); memcpy(&sc->txd_fw[idx]->fw, fw, mlen); // memcpy(&txd[1], fw, mlen); // memset(&txd[1] + mlen, 0, MTW_DMA_PAD); // mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, offset //+sent); 1mtw_write_cfg(sc, MTW_MCU_DMA_LEN, (mlen << 16)); // sc->sc_fw_data[idx]->len=htole16(mlen); // memcpy(tmpbuf,fw,mlen); // memset(tmpbuf+mlen,0,MTW_DMA_PAD); // memcpy(sc->sc_fw_data[idx].buf, fw, mlen); fw += mlen; len -= mlen; // sent+=mlen; idx++; } sc->sc_sent = 0; memcpy(sc->sc_ivb_1, ivb, MTW_MCU_IVB_LEN); usbd_transfer_start(sc->sc_xfer[7]); return (0); } static void mtw_load_microcode(void *arg) { struct mtw_softc *sc = (struct mtw_softc *)arg; const struct mtw_ucode_hdr *hdr; // onst struct mtw_ucode *fw = NULL; const char *fwname; size_t size; int error = 0; uint32_t tmp, iofs = 0x40; // int ntries; int dlen, ilen; device_printf(sc->sc_dev, "version:0x%hx\n", sc->asic_ver); /* is firmware already running? */ mtw_read_cfg(sc, MTW_MCU_DMA_ADDR, &tmp); if (tmp == MTW_MCU_READY) { return; } if (sc->asic_ver == 0x7612) { fwname = "mtw-mt7662u_rom_patch"; const struct firmware *firmware = firmware_get_flags(fwname,FIRMWARE_GET_NOWARN); if (firmware == NULL) { device_printf(sc->sc_dev, "failed loadfirmware of file %s (error %d)\n", fwname, error); return; } size = firmware->datasize; const struct mtw_ucode *fw = (const struct mtw_ucode *) firmware->data; hdr = (const struct mtw_ucode_hdr *)&fw->hdr; // memcpy(fw,(const unsigned char*)firmware->data + // 0x1e,size-0x1e); ilen = size - 0x1e; mtw_ucode_setup(sc); if ((error = mtw_ucode_write(sc, firmware->data, fw->ivb, ilen, 0x90000)) != 0) { goto fail; } mtw_usb_dma_write(sc, 0x00e41814); } fwname = "/mediatek/mt7601u.bin"; iofs = 0x40; // dofs = 0; if (sc->asic_ver == 0x7612) { fwname = "mtw-mt7662u"; iofs = 0x80040; // dofs = 0x110800; } else if (sc->asic_ver == 0x7610) { fwname = "mt7610u"; // dofs = 0x80000; } MTW_UNLOCK(sc); const struct firmware *firmware = firmware_get_flags(fwname, FIRMWARE_GET_NOWARN); if (firmware == NULL) { device_printf(sc->sc_dev, "failed loadfirmware of file %s (error %d)\n", fwname, error); MTW_LOCK(sc); return; } MTW_LOCK(sc); size = firmware->datasize; MTW_DPRINTF(sc, MTW_DEBUG_FIRMWARE, "firmware size:%zu\n", size); const struct mtw_ucode *fw = (const struct mtw_ucode *)firmware->data; if (size < sizeof(struct mtw_ucode_hdr)) { device_printf(sc->sc_dev, "firmware header too short\n"); goto fail; } hdr = (const struct mtw_ucode_hdr *)&fw->hdr; if (size < sizeof(struct mtw_ucode_hdr) + le32toh(hdr->ilm_len) + le32toh(hdr->dlm_len)) { device_printf(sc->sc_dev, "firmware payload too short\n"); goto fail; } ilen = le32toh(hdr->ilm_len) - MTW_MCU_IVB_LEN; dlen = le32toh(hdr->dlm_len); if (ilen > size || dlen > size) { device_printf(sc->sc_dev, "firmware payload too large\n"); goto fail; } mtw_write(sc, MTW_FCE_PDMA, 0); mtw_write(sc, MTW_FCE_PSE_CTRL, 0); mtw_ucode_setup(sc); if ((error = mtw_ucode_write(sc, fw->data, fw->ivb, ilen, iofs)) != 0) device_printf(sc->sc_dev, "Could not write ucode errro=%d\n", error); device_printf(sc->sc_dev, "loaded firmware ver %.8x %.8x %s\n", le32toh(hdr->fw_ver), le32toh(hdr->build_ver), hdr->build_time); return; fail: return; } static usb_error_t mtw_do_request(struct mtw_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 5; MTW_LOCK_ASSERT(sc, MA_OWNED); while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 2000); // ms seconds if (err == 0) break; MTW_DPRINTF(sc, MTW_DEBUG_USB, "Control request failed, %s (retrying)\n", usbd_errstr(err)); mtw_delay(sc, 10); } return (err); } static int mtw_read(struct mtw_softc *sc, uint16_t reg, uint32_t *val) { uint32_t tmp; int error; error = mtw_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp); if (error == 0) *val = le32toh(tmp); else *val = 0xffffffff; return (error); } static int mtw_read_region_1(struct mtw_softc *sc, uint16_t reg, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = MTW_READ_REGION_1; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); return (mtw_do_request(sc, &req, buf)); } static int mtw_write_2(struct mtw_softc *sc, uint16_t reg, uint16_t val) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = MTW_WRITE_2; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); } static int mtw_write(struct mtw_softc *sc, uint16_t reg, uint32_t val) { int error; if ((error = mtw_write_2(sc, reg, val & 0xffff)) == 0) { error = mtw_write_2(sc, reg + 2, val >> 16); } return (error); } static int mtw_write_region_1(struct mtw_softc *sc, uint16_t reg, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = MTW_WRITE_REGION_1; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, buf)); } static int mtw_set_region_4(struct mtw_softc *sc, uint16_t reg, uint32_t val, int count) { int i, error = 0; KASSERT((count & 3) == 0, ("mte_set_region_4: Invalid data length.\n")); for (i = 0; i < count && error == 0; i += 4) error = mtw_write(sc, reg + i, val); return (error); } static int mtw_efuse_read_2(struct mtw_softc *sc, uint16_t addr, uint16_t *val) { uint32_t tmp; uint16_t reg; int error, ntries; if ((error = mtw_read(sc, MTW_EFUSE_CTRL, &tmp)) != 0) return (error); addr *= 2; /* * Read one 16-byte block into registers EFUSE_DATA[0-3]: * DATA0: 3 2 1 0 * DATA1: 7 6 5 4 * DATA2: B A 9 8 * DATA3: F E D C */ tmp &= ~(MTW_EFSROM_MODE_MASK | MTW_EFSROM_AIN_MASK); tmp |= (addr & ~0xf) << MTW_EFSROM_AIN_SHIFT | MTW_EFSROM_KICK; mtw_write(sc, MTW_EFUSE_CTRL, tmp); for (ntries = 0; ntries < 100; ntries++) { if ((error = mtw_read(sc, MTW_EFUSE_CTRL, &tmp)) != 0) return (error); if (!(tmp & MTW_EFSROM_KICK)) break; DELAY(2); } if (ntries == 100) return (ETIMEDOUT); if ((tmp & MTW_EFUSE_AOUT_MASK) == MTW_EFUSE_AOUT_MASK) { *val = 0xffff; // address not found return (0); } // determine to which 32-bit register our 16-bit word belongs reg = MTW_EFUSE_DATA0 + (addr & 0xc); if ((error = mtw_read(sc, reg, &tmp)) != 0) return (error); *val = (addr & 2) ? tmp >> 16 : tmp & 0xffff; return (0); } static __inline int mtw_srom_read(struct mtw_softc *sc, uint16_t addr, uint16_t *val) { /* either eFUSE ROM or EEPROM */ return (sc->sc_srom_read(sc, addr, val)); } static int mtw_bbp_read(struct mtw_softc *sc, uint8_t reg, uint8_t *val) { uint32_t tmp; int ntries, error; for (ntries = 0; ntries < 10; ntries++) { if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0) return (error); if (!(tmp & MTW_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); tmp = MTW_BBP_CSR_READ | MTW_BBP_CSR_KICK | reg << 8; if ((error = mtw_write(sc, MTW_BBP_CSR, tmp)) != 0) return (error); for (ntries = 0; ntries < 10; ntries++) { if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0) return (error); if (!(tmp & MTW_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); *val = tmp & 0xff; return (0); } static int mtw_bbp_write(struct mtw_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; int ntries, error; for (ntries = 0; ntries < 10; ntries++) { if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0) return (error); if (!(tmp & MTW_BBP_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); tmp = MTW_BBP_CSR_KICK | reg << 8 | val; return (mtw_write(sc, MTW_BBP_CSR, tmp)); } static int mtw_mcu_cmd(struct mtw_softc *sc, u_int8_t cmd, void *buf, int len) { sc->sc_idx = 0; sc->txd_fw[sc->sc_idx]->len = htole16( len + 8); sc->txd_fw[sc->sc_idx]->flags = htole16(MTW_TXD_CMD | MTW_TXD_MCU | (cmd & 0x1f) << MTW_TXD_CMD_SHIFT | (0 & 0xf)); memset(&sc->txd_fw[sc->sc_idx]->fw, 0, 2004); memcpy(&sc->txd_fw[sc->sc_idx]->fw, buf, len); usbd_transfer_start(sc->sc_xfer[7]); return (0); } /* * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. * Used to adjust per-rate Tx power registers. */ static __inline uint32_t b4inc(uint32_t b32, int8_t delta) { int8_t i, b4; for (i = 0; i < 8; i++) { b4 = b32 & 0xf; b4 += delta; if (b4 < 0) b4 = 0; else if (b4 > 0xf) b4 = 0xf; b32 = b32 >> 4 | b4 << 28; } return (b32); } static void mtw_get_txpower(struct mtw_softc *sc) { uint16_t val; int i; /* Read power settings for 2GHz channels. */ for (i = 0; i < 14; i += 2) { mtw_srom_read(sc, MTW_EEPROM_PWR2GHZ_BASE1 + i / 2, &val); sc->txpow1[i + 0] = (int8_t)(val & 0xff); sc->txpow1[i + 1] = (int8_t)(val >> 8); mtw_srom_read(sc, MTW_EEPROM_PWR2GHZ_BASE2 + i / 2, &val); sc->txpow2[i + 0] = (int8_t)(val & 0xff); sc->txpow2[i + 1] = (int8_t)(val >> 8); } /* Fix broken Tx power entries. */ for (i = 0; i < 14; i++) { if (sc->txpow1[i] < 0 || sc->txpow1[i] > 27) sc->txpow1[i] = 5; if (sc->txpow2[i] < 0 || sc->txpow2[i] > 27) sc->txpow2[i] = 5; MTW_DPRINTF(sc, MTW_DEBUG_TXPWR, "chan %d: power1=%d, power2=%d\n", mt7601_rf_chan[i].chan, sc->txpow1[i], sc->txpow2[i]); } } struct ieee80211_node * mtw_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { return (malloc(sizeof(struct mtw_node), M_80211_NODE, M_NOWAIT | M_ZERO)); } static int mtw_read_eeprom(struct mtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; int8_t delta_2ghz, delta_5ghz; uint16_t val; int ridx, ant; sc->sc_srom_read = mtw_efuse_read_2; /* read RF information */ mtw_srom_read(sc, MTW_EEPROM_CHIPID, &val); sc->rf_rev = val; mtw_srom_read(sc, MTW_EEPROM_ANTENNA, &val); sc->ntxchains = (val >> 4) & 0xf; sc->nrxchains = val & 0xf; MTW_DPRINTF(sc, MTW_DEBUG_ROM, "EEPROM RF rev=0x%02x chains=%dT%dR\n", sc->rf_rev, sc->ntxchains, sc->nrxchains); /* read ROM version */ mtw_srom_read(sc, MTW_EEPROM_VERSION, &val); MTW_DPRINTF(sc, MTW_DEBUG_ROM, "EEPROM rev=%d, FAE=%d\n", val & 0xff, val >> 8); /* read MAC address */ mtw_srom_read(sc, MTW_EEPROM_MAC01, &val); ic->ic_macaddr[0] = val & 0xff; ic->ic_macaddr[1] = val >> 8; mtw_srom_read(sc, MTW_EEPROM_MAC23, &val); ic->ic_macaddr[2] = val & 0xff; ic->ic_macaddr[3] = val >> 8; mtw_srom_read(sc, MTW_EEPROM_MAC45, &val); ic->ic_macaddr[4] = val & 0xff; ic->ic_macaddr[5] = val >> 8; #if 0 printf("eFUSE ROM\n00: "); for (int i = 0; i < 256; i++) { if (((i % 8) == 0) && i > 0) printf("\n%02x: ", i); mtw_srom_read(sc, i, &val); printf(" %04x", val); } printf("\n"); #endif /* check if RF supports automatic Tx access gain control */ mtw_srom_read(sc, MTW_EEPROM_CONFIG, &val); device_printf(sc->sc_dev, "EEPROM CFG 0x%04x\n", val); if ((val & 0xff) != 0xff) { sc->ext_5ghz_lna = (val >> 3) & 1; sc->ext_2ghz_lna = (val >> 2) & 1; /* check if RF supports automatic Tx access gain control */ sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1; /* check if we have a hardware radio switch */ sc->rfswitch = val & 1; } /* read RF frequency offset from EEPROM */ mtw_srom_read(sc, MTW_EEPROM_FREQ_OFFSET, &val); if ((val & 0xff) != 0xff) sc->rf_freq_offset = val; else sc->rf_freq_offset = 0; MTW_DPRINTF(sc, MTW_DEBUG_ROM, "frequency offset 0x%x\n", sc->rf_freq_offset); /* Read Tx power settings. */ mtw_get_txpower(sc); /* read Tx power compensation for each Tx rate */ mtw_srom_read(sc, MTW_EEPROM_DELTAPWR, &val); delta_2ghz = delta_5ghz = 0; if ((val & 0xff) != 0xff && (val & 0x80)) { delta_2ghz = val & 0xf; if (!(val & 0x40)) /* negative number */ delta_2ghz = -delta_2ghz; } val >>= 8; if ((val & 0xff) != 0xff && (val & 0x80)) { delta_5ghz = val & 0xf; if (!(val & 0x40)) /* negative number */ delta_5ghz = -delta_5ghz; } MTW_DPRINTF(sc, MTW_DEBUG_ROM | MTW_DEBUG_TXPWR, "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz, delta_5ghz); for (ridx = 0; ridx < 5; ridx++) { uint32_t reg; mtw_srom_read(sc, MTW_EEPROM_RPWR + ridx * 2, &val); reg = val; mtw_srom_read(sc, MTW_EEPROM_RPWR + ridx * 2 + 1, &val); reg |= (uint32_t)val << 16; sc->txpow20mhz[ridx] = reg; sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); MTW_DPRINTF(sc, MTW_DEBUG_ROM | MTW_DEBUG_TXPWR, "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx], sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]); } /* read RSSI offsets and LNA gains from EEPROM */ val = 0; mtw_srom_read(sc, MTW_EEPROM_RSSI1_2GHZ, &val); sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ sc->rssi_2ghz[1] = val >> 8; /* Ant B */ mtw_srom_read(sc, MTW_EEPROM_RSSI2_2GHZ, &val); /* * On RT3070 chips (limited to 2 Rx chains), this ROM * field contains the Tx mixer gain for the 2GHz band. */ if ((val & 0xff) != 0xff) sc->txmixgain_2ghz = val & 0x7; MTW_DPRINTF(sc, MTW_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n", sc->txmixgain_2ghz); sc->lna[2] = val >> 8; /* channel group 2 */ mtw_srom_read(sc, MTW_EEPROM_RSSI1_5GHZ, &val); sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ sc->rssi_5ghz[1] = val >> 8; /* Ant B */ mtw_srom_read(sc, MTW_EEPROM_RSSI2_5GHZ, &val); sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ sc->lna[3] = val >> 8; /* channel group 3 */ mtw_srom_read(sc, MTW_EEPROM_LNA, &val); sc->lna[0] = val & 0xff; /* channel group 0 */ sc->lna[1] = val >> 8; /* channel group 1 */ MTW_DPRINTF(sc, MTW_DEBUG_ROM, "LNA0 0x%x\n", sc->lna[0]); /* fix broken 5GHz LNA entries */ if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { MTW_DPRINTF(sc, MTW_DEBUG_ROM, "invalid LNA for channel group %d\n", 2); sc->lna[2] = sc->lna[1]; } if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { MTW_DPRINTF(sc, MTW_DEBUG_ROM, "invalid LNA for channel group %d\n", 3); sc->lna[3] = sc->lna[1]; } /* fix broken RSSI offset entries */ for (ant = 0; ant < 3; ant++) { if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { MTW_DPRINTF(sc, MTW_DEBUG_ROM, "invalid RSSI%d offset: %d (2GHz)\n", ant + 1, sc->rssi_2ghz[ant]); sc->rssi_2ghz[ant] = 0; } if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { MTW_DPRINTF(sc, MTW_DEBUG_ROM, "invalid RSSI%d offset: %d (5GHz)\n", ant + 1, sc->rssi_5ghz[ant]); sc->rssi_5ghz[ant] = 0; } } return (0); } static int mtw_media_change(if_t ifp) { struct ieee80211vap *vap = if_getsoftc(ifp); struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_txparam *tp; struct mtw_softc *sc = ic->ic_softc; uint8_t rate, ridx; MTW_LOCK(sc); ieee80211_media_change(ifp); //tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; tp = &vap->iv_txparms[ic->ic_curmode]; if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { struct ieee80211_node *ni; struct mtw_node *rn; /* XXX TODO: methodize with MCS rates */ rate = ic->ic_sup_rates[ic->ic_curmode].rs_rates[tp->ucastrate] & IEEE80211_RATE_VAL; for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) { if (rt2860_rates[ridx].rate == rate) break; } ni = ieee80211_ref_node(vap->iv_bss); rn = MTW_NODE(ni); rn->fix_ridx = ridx; MTW_DPRINTF(sc, MTW_DEBUG_RATE, "rate=%d, fix_ridx=%d\n", rate, rn->fix_ridx); ieee80211_free_node(ni); } MTW_UNLOCK(sc); return (0); } void mtw_set_leds(struct mtw_softc *sc, uint16_t which) { struct mtw_mcu_cmd_8 cmd; cmd.func = htole32(0x1); cmd.val = htole32(which); mtw_mcu_cmd(sc, CMD_LED_MODE, &cmd, sizeof(struct mtw_mcu_cmd_8)); } static void mtw_abort_tsf_sync(struct mtw_softc *sc) { uint32_t tmp; mtw_read(sc, MTW_BCN_TIME_CFG, &tmp); tmp &= ~(MTW_BCN_TX_EN | MTW_TSF_TIMER_EN | MTW_TBTT_TIMER_EN); mtw_write(sc, MTW_BCN_TIME_CFG, tmp); } static int mtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { const struct ieee80211_txparam *tp; struct ieee80211com *ic = vap->iv_ic; struct mtw_softc *sc = ic->ic_softc; struct mtw_vap *rvp = MTW_VAP(vap); enum ieee80211_state ostate; uint32_t sta[3]; uint8_t ratectl = 0; uint8_t restart_ratectl = 0; uint8_t bid = 1 << rvp->rvp_id; ostate = vap->iv_state; MTW_DPRINTF(sc, MTW_DEBUG_STATE, "%s -> %s\n", ieee80211_state_name[ostate], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); MTW_LOCK(sc); ratectl = sc->ratectl_run; /* remember current state */ usb_callout_stop(&sc->ratectl_ch); sc->ratectl_run = MTW_RATECTL_OFF; if (ostate == IEEE80211_S_RUN) { /* turn link LED off */ } switch (nstate) { case IEEE80211_S_INIT: restart_ratectl = 1; if (ostate != IEEE80211_S_RUN) break; ratectl &= ~bid; sc->runbmap &= ~bid; /* abort TSF synchronization if there is no vap running */ if (--sc->running == 0) mtw_abort_tsf_sync(sc); break; case IEEE80211_S_RUN: if (!(sc->runbmap & bid)) { if (sc->running++) restart_ratectl = 1; sc->runbmap |= bid; } m_freem(rvp->beacon_mbuf); rvp->beacon_mbuf = NULL; switch (vap->iv_opmode) { case IEEE80211_M_HOSTAP: case IEEE80211_M_MBSS: sc->ap_running |= bid; ic->ic_opmode = vap->iv_opmode; mtw_update_beacon_cb(vap); break; case IEEE80211_M_IBSS: sc->adhoc_running |= bid; if (!sc->ap_running) ic->ic_opmode = vap->iv_opmode; mtw_update_beacon_cb(vap); break; case IEEE80211_M_STA: sc->sta_running |= bid; if (!sc->ap_running && !sc->adhoc_running) ic->ic_opmode = vap->iv_opmode; /* read statistic counters (clear on read) */ mtw_read_region_1(sc, MTW_TX_STA_CNT0, (uint8_t *)sta, sizeof sta); break; default: ic->ic_opmode = vap->iv_opmode; break; } if (vap->iv_opmode != IEEE80211_M_MONITOR) { struct ieee80211_node *ni; if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { MTW_UNLOCK(sc); IEEE80211_LOCK(ic); return (-1); } mtw_updateslot(ic); mtw_enable_mrr(sc); mtw_set_txpreamble(sc); mtw_set_basicrates(sc); ni = ieee80211_ref_node(vap->iv_bss); IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); mtw_set_bssid(sc, sc->sc_bssid); ieee80211_free_node(ni); mtw_enable_tsf_sync(sc); /* enable automatic rate adaptation */ tp = &vap->iv_txparms[ieee80211_chan2mode( ic->ic_curchan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) ratectl |= bid; } else { mtw_enable_tsf_sync(sc); } break; default: MTW_DPRINTF(sc, MTW_DEBUG_STATE, "undefined state\n"); break; } /* restart amrr for running VAPs */ if ((sc->ratectl_run = ratectl) && restart_ratectl) { usb_callout_reset(&sc->ratectl_ch, hz, mtw_ratectl_to, sc); } MTW_UNLOCK(sc); IEEE80211_LOCK(ic); return (rvp->newstate(vap, nstate, arg)); } static int mtw_wme_update(struct ieee80211com *ic) { struct chanAccParams chp; struct mtw_softc *sc = ic->ic_softc; const struct wmeParams *ac; int aci, error = 0; ieee80211_wme_ic_getparams(ic, &chp); ac = chp.cap_wmeParams; MTW_LOCK(sc); /* update MAC TX configuration registers */ for (aci = 0; aci < WME_NUM_AC; aci++) { error = mtw_write(sc, MTW_EDCA_AC_CFG(aci), ac[aci].wmep_logcwmax << 16 | ac[aci].wmep_logcwmin << 12 | ac[aci].wmep_aifsn << 8 | ac[aci].wmep_txopLimit); if (error) goto err; } /* update SCH/DMA registers too */ error = mtw_write(sc, MTW_WMM_AIFSN_CFG, ac[WME_AC_VO].wmep_aifsn << 12 | ac[WME_AC_VI].wmep_aifsn << 8 | ac[WME_AC_BK].wmep_aifsn << 4 | ac[WME_AC_BE].wmep_aifsn); if (error) goto err; error = mtw_write(sc, MTW_WMM_CWMIN_CFG, ac[WME_AC_VO].wmep_logcwmin << 12 | ac[WME_AC_VI].wmep_logcwmin << 8 | ac[WME_AC_BK].wmep_logcwmin << 4 | ac[WME_AC_BE].wmep_logcwmin); if (error) goto err; error = mtw_write(sc, MTW_WMM_CWMAX_CFG, ac[WME_AC_VO].wmep_logcwmax << 12 | ac[WME_AC_VI].wmep_logcwmax << 8 | ac[WME_AC_BK].wmep_logcwmax << 4 | ac[WME_AC_BE].wmep_logcwmax); if (error) goto err; error = mtw_write(sc, MTW_WMM_TXOP0_CFG, ac[WME_AC_BK].wmep_txopLimit << 16 | ac[WME_AC_BE].wmep_txopLimit); if (error) goto err; error = mtw_write(sc, MTW_WMM_TXOP1_CFG, ac[WME_AC_VO].wmep_txopLimit << 16 | ac[WME_AC_VI].wmep_txopLimit); err: MTW_UNLOCK(sc); if (error) MTW_DPRINTF(sc, MTW_DEBUG_USB, "WME update failed\n"); return (error); } static int mtw_key_set(struct ieee80211vap *vap, struct ieee80211_key *k) { struct ieee80211com *ic = vap->iv_ic; struct mtw_softc *sc = ic->ic_softc; uint32_t i; i = MTW_CMDQ_GET(&sc->cmdq_store); MTW_DPRINTF(sc, MTW_DEBUG_KEY, "cmdq_store=%d\n", i); sc->cmdq[i].func = mtw_key_set_cb; sc->cmdq[i].arg0 = NULL; sc->cmdq[i].arg1 = vap; sc->cmdq[i].k = k; IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr); ieee80211_runtask(ic, &sc->cmdq_task); /* * To make sure key will be set when hostapd * calls iv_key_set() before if_init(). */ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { MTW_LOCK(sc); sc->cmdq_key_set = MTW_CMDQ_GO; MTW_UNLOCK(sc); } return (1); } static void mtw_key_set_cb(void *arg) { struct mtw_cmdq *cmdq = arg; struct ieee80211vap *vap = cmdq->arg1; struct ieee80211_key *k = cmdq->k; struct ieee80211com *ic = vap->iv_ic; struct mtw_softc *sc = ic->ic_softc; struct ieee80211_node *ni; u_int cipher = k->wk_cipher->ic_cipher; uint32_t attr; uint16_t base; uint8_t mode, wcid, iv[8]; MTW_LOCK_ASSERT(sc, MA_OWNED); if (vap->iv_opmode == IEEE80211_M_HOSTAP) ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac); else ni = vap->iv_bss; /* map net80211 cipher to RT2860 security mode */ switch (cipher) { case IEEE80211_CIPHER_WEP: if (k->wk_keylen < 8) mode = MTW_MODE_WEP40; else mode = MTW_MODE_WEP104; break; case IEEE80211_CIPHER_TKIP: mode = MTW_MODE_TKIP; break; case IEEE80211_CIPHER_AES_CCM: mode = MTW_MODE_AES_CCMP; break; default: MTW_DPRINTF(sc, MTW_DEBUG_KEY, "undefined case\n"); return; } if (k->wk_flags & IEEE80211_KEY_GROUP) { wcid = 0; /* NB: update WCID0 for group keys */ base = MTW_SKEY(0, k->wk_keyix); } else { wcid = (ni != NULL) ? MTW_AID2WCID(ni->ni_associd) : 0; base = MTW_PKEY(wcid); } if (cipher == IEEE80211_CIPHER_TKIP) { mtw_write_region_1(sc, base, k->wk_key, 16); mtw_write_region_1(sc, base + 16, &k->wk_key[24], 8); mtw_write_region_1(sc, base + 24, &k->wk_key[16], 8); } else { /* roundup len to 16-bit: XXX fix write_region_1() instead */ mtw_write_region_1(sc, base, k->wk_key, (k->wk_keylen + 1) & ~1); } if (!(k->wk_flags & IEEE80211_KEY_GROUP) || (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) { /* set initial packet number in IV+EIV */ if (cipher == IEEE80211_CIPHER_WEP) { memset(iv, 0, sizeof iv); iv[3] = vap->iv_def_txkey << 6; } else { if (cipher == IEEE80211_CIPHER_TKIP) { iv[0] = k->wk_keytsc >> 8; iv[1] = (iv[0] | 0x20) & 0x7f; iv[2] = k->wk_keytsc; } else { //CCMP iv[0] = k->wk_keytsc; iv[1] = k->wk_keytsc >> 8; iv[2] = 0; } iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV; iv[4] = k->wk_keytsc >> 16; iv[5] = k->wk_keytsc >> 24; iv[6] = k->wk_keytsc >> 32; iv[7] = k->wk_keytsc >> 40; } mtw_write_region_1(sc, MTW_IVEIV(wcid), iv, 8); } if (k->wk_flags & IEEE80211_KEY_GROUP) { /* install group key */ mtw_read(sc, MTW_SKEY_MODE_0_7, &attr); attr &= ~(0xf << (k->wk_keyix * 4)); attr |= mode << (k->wk_keyix * 4); mtw_write(sc, MTW_SKEY_MODE_0_7, attr); if (cipher & (IEEE80211_CIPHER_WEP)) { mtw_read(sc, MTW_WCID_ATTR(wcid + 1), &attr); attr = (attr & ~0xf) | (mode << 1); mtw_write(sc, MTW_WCID_ATTR(wcid + 1), attr); mtw_set_region_4(sc, MTW_IVEIV(0), 0, 4); mtw_read(sc, MTW_WCID_ATTR(wcid), &attr); attr = (attr & ~0xf) | (mode << 1); mtw_write(sc, MTW_WCID_ATTR(wcid), attr); } } else { /* install pairwise key */ mtw_read(sc, MTW_WCID_ATTR(wcid), &attr); attr = (attr & ~0xf) | (mode << 1) | MTW_RX_PKEY_EN; mtw_write(sc, MTW_WCID_ATTR(wcid), attr); } k->wk_pad = wcid; } /* * If wlan is destroyed without being brought down i.e. without * wlan down or wpa_cli terminate, this function is called after * vap is gone. Don't refer it. */ static void mtw_key_delete_cb(void *arg) { struct mtw_cmdq *cmdq = arg; struct mtw_softc *sc = cmdq->arg1; struct ieee80211_key *k = &cmdq->key; uint32_t attr; uint8_t wcid; MTW_LOCK_ASSERT(sc, MA_OWNED); if (k->wk_flags & IEEE80211_KEY_GROUP) { /* remove group key */ MTW_DPRINTF(sc, MTW_DEBUG_KEY, "removing group key\n"); mtw_read(sc, MTW_SKEY_MODE_0_7, &attr); attr &= ~(0xf << (k->wk_keyix * 4)); mtw_write(sc, MTW_SKEY_MODE_0_7, attr); } else { /* remove pairwise key */ MTW_DPRINTF(sc, MTW_DEBUG_KEY, "removing key for wcid %x\n", k->wk_pad); /* matching wcid was written to wk_pad in mtw_key_set() */ wcid = k->wk_pad; mtw_read(sc, MTW_WCID_ATTR(wcid), &attr); attr &= ~0xf; mtw_write(sc, MTW_WCID_ATTR(wcid), attr); } k->wk_pad = 0; } /* * return 0 on error */ static int mtw_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k) { struct ieee80211com *ic = vap->iv_ic; struct mtw_softc *sc = ic->ic_softc; struct ieee80211_key *k0; uint32_t i; if (sc->sc_flags & MTW_RUNNING) return (1); /* * When called back, key might be gone. So, make a copy * of some values need to delete keys before deferring. * But, because of LOR with node lock, cannot use lock here. * So, use atomic instead. */ i = MTW_CMDQ_GET(&sc->cmdq_store); MTW_DPRINTF(sc, MTW_DEBUG_KEY, "cmdq_store=%d\n", i); sc->cmdq[i].func = mtw_key_delete_cb; sc->cmdq[i].arg0 = NULL; sc->cmdq[i].arg1 = sc; k0 = &sc->cmdq[i].key; k0->wk_flags = k->wk_flags; k0->wk_keyix = k->wk_keyix; /* matching wcid was written to wk_pad in mtw_key_set() */ k0->wk_pad = k->wk_pad; ieee80211_runtask(ic, &sc->cmdq_task); return (1); /* return fake success */ } static void mtw_ratectl_to(void *arg) { struct mtw_softc *sc = arg; /* do it in a process context, so it can go sleep */ ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task); /* next timeout will be rescheduled in the callback task */ } /* ARGSUSED */ static void mtw_ratectl_cb(void *arg, int pending) { struct mtw_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap == NULL) return; ieee80211_iterate_nodes(&ic->ic_sta, mtw_iter_func, sc); usb_callout_reset(&sc->ratectl_ch, hz, mtw_ratectl_to, sc); } static void mtw_drain_fifo(void *arg) { struct mtw_softc *sc = arg; uint32_t stat; uint16_t(*wstat)[3]; uint8_t wcid, mcs, pid; int8_t retry; MTW_LOCK_ASSERT(sc, MA_OWNED); for (;;) { /* drain Tx status FIFO (maxsize = 16) */ mtw_read(sc, MTW_TX_STAT_FIFO, &stat); MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx stat 0x%08x\n", stat); if (!(stat & MTW_TXQ_VLD)) break; wcid = (stat >> MTW_TXQ_WCID_SHIFT) & 0xff; /* if no ACK was requested, no feedback is available */ if (!(stat & MTW_TXQ_ACKREQ) || wcid > MTW_WCID_MAX || wcid == 0) continue; /* * Even though each stat is Tx-complete-status like format, * the device can poll stats. Because there is no guarantee * that the referring node is still around when read the stats. * So that, if we use ieee80211_ratectl_tx_update(), we will * have hard time not to refer already freed node. * * To eliminate such page faults, we poll stats in softc. * Then, update the rates later with * ieee80211_ratectl_tx_update(). */ wstat = &(sc->wcid_stats[wcid]); (*wstat)[MTW_TXCNT]++; if (stat & MTW_TXQ_OK) (*wstat)[MTW_SUCCESS]++; else counter_u64_add(sc->sc_ic.ic_oerrors, 1); /* * Check if there were retries, ie if the Tx success rate is * different from the requested rate. Note that it works only * because we do not allow rate fallback from OFDM to CCK. */ mcs = (stat >> MTW_TXQ_MCS_SHIFT) & 0x7f; pid = (stat >> MTW_TXQ_PID_SHIFT) & 0xf; if ((retry = pid - 1 - mcs) > 0) { (*wstat)[MTW_TXCNT] += retry; (*wstat)[MTW_RETRY] += retry; } } MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt); sc->fifo_cnt = 0; } static void mtw_iter_func(void *arg, struct ieee80211_node *ni) { struct mtw_softc *sc = arg; MTW_LOCK(sc); struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; struct ieee80211vap *vap = ni->ni_vap; struct mtw_node *rn = MTW_NODE(ni); uint32_t sta[3]; uint16_t(*wstat)[3]; int error, ridx; uint8_t txrate = 0; /* Check for special case */ if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA && ni != vap->iv_bss) goto fail; txs->flags = IEEE80211_RATECTL_TX_STATS_NODE | IEEE80211_RATECTL_TX_STATS_RETRIES; txs->ni = ni; if (sc->rvp_cnt <= 1 && (vap->iv_opmode == IEEE80211_M_IBSS || vap->iv_opmode == IEEE80211_M_STA)) { /* * read statistic counters (clear on read) and update AMRR state */ error = mtw_read_region_1(sc, MTW_TX_STA_CNT0, (uint8_t *)sta, sizeof sta); MTW_DPRINTF(sc, MTW_DEBUG_RATE, "error:%d\n", error); if (error != 0) goto fail; /* count failed TX as errors */ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, le32toh(sta[0]) & 0xffff); txs->nretries = (le32toh(sta[1]) >> 16); txs->nsuccess = (le32toh(sta[1]) & 0xffff); /* nretries??? */ txs->nframes = txs->nsuccess + (le32toh(sta[0]) & 0xffff); MTW_DPRINTF(sc, MTW_DEBUG_RATE, "retrycnt=%d success=%d failcnt=%d\n", txs->nretries, txs->nsuccess, le32toh(sta[0]) & 0xffff); } else { wstat = &(sc->wcid_stats[MTW_AID2WCID(ni->ni_associd)]); if (wstat == &(sc->wcid_stats[0]) || wstat > &(sc->wcid_stats[MTW_WCID_MAX])) goto fail; txs->nretries = (*wstat)[MTW_RETRY]; txs->nsuccess = (*wstat)[MTW_SUCCESS]; txs->nframes = (*wstat)[MTW_TXCNT]; MTW_DPRINTF(sc, MTW_DEBUG_RATE, "wstat retrycnt=%d txcnt=%d success=%d\n", txs->nretries, txs->nframes, txs->nsuccess); memset(wstat, 0, sizeof(*wstat)); } ieee80211_ratectl_tx_update(vap, txs); ieee80211_ratectl_rate(ni, NULL, 0); txrate = ieee80211_node_get_txrate_dot11rate(ni); /* XXX TODO: methodize with MCS rates */ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) { MTW_DPRINTF(sc, MTW_DEBUG_RATE, "ni_txrate=0x%x\n", txrate); if (rt2860_rates[ridx].rate == txrate) { break; } } rn->amrr_ridx = ridx; fail: MTW_UNLOCK(sc); MTW_DPRINTF(sc, MTW_DEBUG_RATE, "rate=%d, ridx=%d\n", txrate, rn->amrr_ridx); } static void mtw_newassoc_cb(void *arg) { struct mtw_cmdq *cmdq = arg; struct ieee80211_node *ni = cmdq->arg1; struct mtw_softc *sc = ni->ni_vap->iv_ic->ic_softc; uint8_t wcid = cmdq->wcid; MTW_LOCK_ASSERT(sc, MA_OWNED); mtw_write_region_1(sc, MTW_WCID_ENTRY(wcid), ni->ni_macaddr, IEEE80211_ADDR_LEN); memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid])); } static void mtw_newassoc(struct ieee80211_node *ni, int isnew) { struct mtw_node *mn = MTW_NODE(ni); struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = vap->iv_ic; struct mtw_softc *sc = ic->ic_softc; uint8_t rate; uint8_t ridx; uint8_t wcid; //int i; // int i,j; wcid = MTW_AID2WCID(ni->ni_associd); if (wcid > MTW_WCID_MAX) { device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid); return; } /* only interested in true associations */ if (isnew && ni->ni_associd != 0) { /* * This function could is called though timeout function. * Need to deferggxr. */ uint32_t cnt = MTW_CMDQ_GET(&sc->cmdq_store); MTW_DPRINTF(sc, MTW_DEBUG_STATE, "cmdq_store=%d\n", cnt); sc->cmdq[cnt].func = mtw_newassoc_cb; sc->cmdq[cnt].arg0 = NULL; sc->cmdq[cnt].arg1 = ni; sc->cmdq[cnt].wcid = wcid; ieee80211_runtask(ic, &sc->cmdq_task); } MTW_DPRINTF(sc, MTW_DEBUG_STATE, "new assoc isnew=%d associd=%x addr=%s\n", isnew, ni->ni_associd, ether_sprintf(ni->ni_macaddr)); rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate; /* XXX TODO: methodize with MCS rates */ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; mn->mgt_ridx = ridx; MTW_DPRINTF(sc, MTW_DEBUG_STATE | MTW_DEBUG_RATE, "rate=%d, ctl_ridx=%d\n", rate, ridx); MTW_LOCK(sc); if (sc->ratectl_run != MTW_RATECTL_OFF) { usb_callout_reset(&sc->ratectl_ch, hz, &mtw_ratectl_to, sc); } MTW_UNLOCK(sc); } /* * Return the Rx chain with the highest RSSI for a given frame. */ static __inline uint8_t mtw_maxrssi_chain(struct mtw_softc *sc, const struct mtw_rxwi *rxwi) { uint8_t rxchain = 0; if (sc->nrxchains > 1) { if (rxwi->rssi[1] > rxwi->rssi[rxchain]) rxchain = 1; if (sc->nrxchains > 2) if (rxwi->rssi[2] > rxwi->rssi[rxchain]) rxchain = 2; } return (rxchain); } static void mtw_get_tsf(struct mtw_softc *sc, uint64_t *buf) { mtw_read_region_1(sc, MTW_TSF_TIMER_DW0, (uint8_t *)buf, sizeof(*buf)); } static void mtw_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) { struct ieee80211vap *vap = ni->ni_vap; struct mtw_softc *sc = vap->iv_ic->ic_softc; struct mtw_vap *rvp = MTW_VAP(vap); uint64_t ni_tstamp, rx_tstamp; rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); if (vap->iv_state == IEEE80211_S_RUN && (subtype == IEEE80211_FC0_SUBTYPE_BEACON || subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { ni_tstamp = le64toh(ni->ni_tstamp.tsf); MTW_LOCK(sc); mtw_get_tsf(sc, &rx_tstamp); MTW_UNLOCK(sc); rx_tstamp = le64toh(rx_tstamp); if (ni_tstamp >= rx_tstamp) { MTW_DPRINTF(sc, MTW_DEBUG_RECV | MTW_DEBUG_BEACON, "ibss merge, tsf %ju tstamp %ju\n", (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); (void)ieee80211_ibss_merge(ni); } } } static void mtw_rx_frame(struct mtw_softc *sc, struct mbuf *m, uint32_t dmalen) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct epoch_tracker et; struct mtw_rxwi *rxwi; uint32_t flags; uint16_t len, rxwisize; uint8_t ant, rssi; int8_t nf; rxwisize = sizeof(struct mtw_rxwi); if (__predict_false( dmalen < rxwisize + sizeof(struct ieee80211_frame_ack))) { MTW_DPRINTF(sc, MTW_DEBUG_RECV, "payload is too short: dma length %u < %zu\n", dmalen, rxwisize + sizeof(struct ieee80211_frame_ack)); goto fail; } rxwi = mtod(m, struct mtw_rxwi *); len = le16toh(rxwi->len) & 0xfff; flags = le32toh(rxwi->flags); if (__predict_false(len > dmalen - rxwisize)) { MTW_DPRINTF(sc, MTW_DEBUG_RECV, "bad RXWI length %u > %u\n", len, dmalen); goto fail; } if (__predict_false(flags & (MTW_RX_CRCERR | MTW_RX_ICVERR))) { MTW_DPRINTF(sc, MTW_DEBUG_RECV, "%s error.\n", (flags & MTW_RX_CRCERR) ? "CRC" : "ICV"); goto fail; } if (flags & MTW_RX_L2PAD) { MTW_DPRINTF(sc, MTW_DEBUG_RECV, "received RT2860_RX_L2PAD frame\n"); len += 2; } m->m_data += rxwisize; m->m_pkthdr.len = m->m_len = len; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; m->m_flags |= M_WEP; } if (len >= sizeof(struct ieee80211_frame_min)) { ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); } else ni = NULL; if (ni && ni->ni_flags & IEEE80211_NODE_HT) { m->m_flags |= M_AMPDU; } if (__predict_false(flags & MTW_RX_MICERR)) { /* report MIC failures to net80211 for TKIP */ if (ni != NULL) ieee80211_notify_michael_failure(ni->ni_vap, wh, rxwi->keyidx); MTW_DPRINTF(sc, MTW_DEBUG_RECV, "MIC error. Someone is lying.\n"); goto fail; } ant = mtw_maxrssi_chain(sc, rxwi); rssi = rxwi->rssi[ant]; nf = mtw_rssi2dbm(sc, rssi, ant); if (__predict_false(ieee80211_radiotap_active(ic))) { struct mtw_rx_radiotap_header *tap = &sc->sc_rxtap; uint16_t phy; tap->wr_flags = 0; if (flags & MTW_RX_L2PAD) tap->wr_flags |= IEEE80211_RADIOTAP_F_DATAPAD; tap->wr_antsignal = rssi; tap->wr_antenna = ant; tap->wr_dbm_antsignal = mtw_rssi2dbm(sc, rssi, ant); tap->wr_rate = 2; /* in case it can't be found below */ //MTW_LOCK(sc); // MTW_UNLOCK(sc); phy = le16toh(rxwi->phy); switch (phy >> MT7601_PHY_SHIFT) { case MTW_PHY_CCK: switch ((phy & MTW_PHY_MCS) & ~MTW_PHY_SHPRE) { case 0: tap->wr_rate = 2; break; case 1: tap->wr_rate = 4; break; case 2: tap->wr_rate = 11; break; case 3: tap->wr_rate = 22; break; } if (phy & MTW_PHY_SHPRE) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; break; case MTW_PHY_OFDM: switch (phy & MTW_PHY_MCS) { case 0: tap->wr_rate = 12; break; case 1: tap->wr_rate = 18; break; case 2: tap->wr_rate = 24; break; case 3: tap->wr_rate = 36; break; case 4: tap->wr_rate = 48; break; case 5: tap->wr_rate = 72; break; case 6: tap->wr_rate = 96; break; case 7: tap->wr_rate = 108; break; } break; } } NET_EPOCH_ENTER(et); if (ni != NULL) { (void)ieee80211_input(ni, m, rssi, nf); ieee80211_free_node(ni); } else { (void)ieee80211_input_all(ic, m, rssi, nf); } NET_EPOCH_EXIT(et); return; fail: m_freem(m); counter_u64_add(ic->ic_ierrors, 1); } static void mtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct mtw_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct mbuf *m = NULL; struct mbuf *m0; uint32_t dmalen, mbuf_len; uint16_t rxwisize; int xferlen; rxwisize = sizeof(struct mtw_rxwi); usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: MTW_DPRINTF(sc, MTW_DEBUG_RECV, "rx done, actlen=%d\n", xferlen); if (xferlen < (int)(sizeof(uint32_t) + rxwisize + sizeof(struct mtw_rxd))) { MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, "xfer too short %d %d\n", xferlen, (int)(sizeof(uint32_t) + rxwisize + sizeof(struct mtw_rxd))); goto tr_setup; } m = sc->rx_m; sc->rx_m = NULL; /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: if (sc->rx_m == NULL) { sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MTW_MAX_RXSZ); } if (sc->rx_m == NULL) { MTW_DPRINTF(sc, MTW_DEBUG_RECV | MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, "could not allocate mbuf - idle with stall\n"); counter_u64_add(ic->ic_ierrors, 1); usbd_xfer_set_stall(xfer); usbd_xfer_set_frames(xfer, 0); } else { /* * Directly loading a mbuf cluster into DMA to * save some data copying. This works because * there is only one cluster. */ usbd_xfer_set_frame_data(xfer, 0, mtod(sc->rx_m, caddr_t), MTW_MAX_RXSZ); usbd_xfer_set_frames(xfer, 1); } usbd_transfer_submit(xfer); break; default: /* Error */ MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, "USB transfer error, %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); if (error == USB_ERR_TIMEOUT) device_printf(sc->sc_dev, "device timeout %s\n", __func__); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } if (sc->rx_m != NULL) { m_freem(sc->rx_m); sc->rx_m = NULL; } break; } if (m == NULL) return; /* inputting all the frames must be last */ MTW_UNLOCK(sc); m->m_pkthdr.len = m->m_len = xferlen; /* HW can aggregate multiple 802.11 frames in a single USB xfer */ for (;;) { dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff; if ((dmalen >= (uint32_t)-8) || (dmalen == 0) || ((dmalen & 3) != 0)) { MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, "bad DMA length %u\n", dmalen); break; } if ((dmalen + 8) > (uint32_t)xferlen) { MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, "bad DMA length %u > %d\n", dmalen + 8, xferlen); break; } /* If it is the last one or a single frame, we won't copy. */ if ((xferlen -= dmalen + 8) <= 8) { /* trim 32-bit DMA-len header */ m->m_data += 4; m->m_pkthdr.len = m->m_len -= 4; mtw_rx_frame(sc, m, dmalen); m = NULL; /* don't free source buffer */ break; } mbuf_len = dmalen + sizeof(struct mtw_rxd); if (__predict_false(mbuf_len > MCLBYTES)) { MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, "payload is too big: mbuf_len %u\n", mbuf_len); counter_u64_add(ic->ic_ierrors, 1); break; } /* copy aggregated frames to another mbuf */ m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m0 == NULL)) { MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC, "could not allocate mbuf\n"); counter_u64_add(ic->ic_ierrors, 1); break; } m_copydata(m, 4 /* skip 32-bit DMA-len header */, mbuf_len, mtod(m0, caddr_t)); m0->m_pkthdr.len = m0->m_len = mbuf_len; mtw_rx_frame(sc, m0, dmalen); /* update data ptr */ m->m_data += mbuf_len + 4; m->m_pkthdr.len = m->m_len -= mbuf_len + 4; } /* make sure we free the source buffer, if any */ m_freem(m); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_ff_age_all(ic, 100); #endif MTW_LOCK(sc); } static void mtw_tx_free(struct mtw_endpoint_queue *pq, struct mtw_tx_data *data, int txerr) { ieee80211_tx_complete(data->ni, data->m, txerr); data->m = NULL; data->ni = NULL; STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); pq->tx_nfree++; } static void mtw_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index) { struct mtw_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct mtw_tx_data *data; struct ieee80211vap *vap = NULL; struct usb_page_cache *pc; struct mtw_endpoint_queue *pq = &sc->sc_epq[index]; struct mbuf *m; usb_frlength_t size; int actlen; int sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, "transfer complete: %d bytes @ index %d\n", actlen, index); data = usbd_xfer_get_priv(xfer); mtw_tx_free(pq, data, 0); usbd_xfer_set_priv(xfer, NULL); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&pq->tx_qh); if (data == NULL) break; STAILQ_REMOVE_HEAD(&pq->tx_qh, next); m = data->m; size = sizeof(data->desc); if ((m->m_pkthdr.len + size + 3 + 8) > MTW_MAX_TXSZ) { MTW_DPRINTF(sc, MTW_DEBUG_XMIT_DESC | MTW_DEBUG_USB, "data overflow, %u bytes\n", m->m_pkthdr.len); mtw_tx_free(pq, data, 1); goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &data->desc, size); usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len); size += m->m_pkthdr.len; /* * Align end on a 4-byte boundary, pad 8 bytes (CRC + * 4-byte padding), and be sure to zero those trailing * bytes: */ usbd_frame_zero(pc, size, ((-size) & 3) + MTW_DMA_PAD); size += ((-size) & 3) + MTW_DMA_PAD; vap = data->ni->ni_vap; if (ieee80211_radiotap_active_vap(vap)) { const struct ieee80211_frame *wh; struct mtw_tx_radiotap_header *tap = &sc->sc_txtap; struct mtw_txwi *txwi = (struct mtw_txwi *)(&data->desc + sizeof(struct mtw_txd)); int has_l2pad; wh = mtod(m, struct ieee80211_frame *); has_l2pad = IEEE80211_HAS_ADDR4(wh) != IEEE80211_QOS_HAS_SEQ(wh); tap->wt_flags = 0; tap->wt_rate = rt2860_rates[data->ridx].rate; tap->wt_hwqueue = index; if (le16toh(txwi->phy) & MTW_PHY_SHPRE) tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; if (has_l2pad) tap->wt_flags |= IEEE80211_RADIOTAP_F_DATAPAD; ieee80211_radiotap_tx(vap, m); } MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, "sending frame len=%u/%u @ index %d\n", m->m_pkthdr.len, size, index); usbd_xfer_set_frame_len(xfer, 0, size); usbd_xfer_set_priv(xfer, data); usbd_transfer_submit(xfer); mtw_start(sc); break; default: MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, "USB transfer error, %s\n", usbd_errstr(error)); data = usbd_xfer_get_priv(xfer); if (data != NULL) { if (data->ni != NULL) vap = data->ni->ni_vap; mtw_tx_free(pq, data, error); usbd_xfer_set_priv(xfer, NULL); } if (vap == NULL) vap = TAILQ_FIRST(&ic->ic_vaps); if (error != USB_ERR_CANCELLED) { if (error == USB_ERR_TIMEOUT) { device_printf(sc->sc_dev, "device timeout %s\n", __func__); uint32_t i = MTW_CMDQ_GET(&sc->cmdq_store); MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, "cmdq_store=%d\n", i); sc->cmdq[i].func = mtw_usb_timeout_cb; sc->cmdq[i].arg0 = vap; ieee80211_runtask(ic, &sc->cmdq_task); } /* * Try to clear stall first, also if other * errors occur, hence clearing stall * introduces a 50 ms delay: */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } #ifdef IEEE80211_SUPPORT_SUPERG /* XXX TODO: make this deferred rather than unlock/relock */ /* XXX TODO: should only do the QoS AC this belongs to */ if (pq->tx_nfree >= MTW_TX_RING_COUNT) { MTW_UNLOCK(sc); ieee80211_ff_flush_all(ic); MTW_LOCK(sc); } #endif } static void mtw_fw_callback(struct usb_xfer *xfer, usb_error_t error) { struct mtw_softc *sc = usbd_xfer_softc(xfer); int actlen; int ntries, tmp; // struct mtw_txd *data; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); // data = usbd_xfer_get_priv(xfer); usbd_xfer_set_priv(xfer, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->sc_sent += actlen; memset(sc->txd_fw[sc->sc_idx], 0, actlen); if (actlen < 0x2c44 && sc->sc_idx == 0) { return; } if (sc->sc_idx == 3) { if ((error = mtw_write_ivb(sc, sc->sc_ivb_1, MTW_MCU_IVB_LEN)) != 0) { device_printf(sc->sc_dev, "Could not write ivb error: %d\n", error); } mtw_delay(sc, 10); for (ntries = 0; ntries < 100; ntries++) { if ((error = mtw_read_cfg(sc, MTW_MCU_DMA_ADDR, &tmp)) != 0) { device_printf(sc->sc_dev, "Could not read cfg error: %d\n", error); } if (tmp == MTW_MCU_READY) { MTW_DPRINTF(sc, MTW_DEBUG_FIRMWARE, "mcu reaady %d\n", tmp); sc->fwloading = 1; break; } mtw_delay(sc, 10); } if (ntries == 100) sc->fwloading = 0; wakeup(&sc->fwloading); return; } if (actlen == 0x2c44) { sc->sc_idx++; DELAY(1000); } case USB_ST_SETUP: { int dlen = 0; dlen = sc->txd_fw[sc->sc_idx]->len; mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, 0x40 + sc->sc_sent); mtw_write_cfg(sc, MTW_MCU_DMA_LEN, (dlen << 16)); usbd_xfer_set_frame_len(xfer, 0, dlen); usbd_xfer_set_frame_data(xfer, 0, sc->txd_fw[sc->sc_idx], dlen); // usbd_xfer_set_priv(xfer,sc->txd[sc->sc_idx]); usbd_transfer_submit(xfer); break; default: /* Error */ device_printf(sc->sc_dev, "%s:%d %s\n", __FILE__, __LINE__, usbd_errstr(error)); sc->fwloading = 0; wakeup(&sc->fwloading); /* * Print error message and clear stall * for example. */ break; } /* * Here it is safe to do something without the private * USB mutex locked. */ } return; } static void mtw_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error) { mtw_bulk_tx_callbackN(xfer, error, 0); } static void mtw_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error) { mtw_bulk_tx_callbackN(xfer, error, 1); } static void mtw_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error) { mtw_bulk_tx_callbackN(xfer, error, 2); } static void mtw_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error) { mtw_bulk_tx_callbackN(xfer, error, 3); } static void mtw_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error) { mtw_bulk_tx_callbackN(xfer, error, 4); } static void mtw_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error) { mtw_bulk_tx_callbackN(xfer, error, 5); } static void mtw_set_tx_desc(struct mtw_softc *sc, struct mtw_tx_data *data) { struct mbuf *m = data->m; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = data->ni->ni_vap; struct ieee80211_frame *wh; struct mtw_txd *txd; struct mtw_txwi *txwi; uint16_t xferlen, txwisize; uint16_t mcs; uint8_t ridx = data->ridx; uint8_t pad; /* get MCS code from rate index */ mcs = rt2860_rates[ridx].mcs; txwisize = sizeof(*txwi); xferlen = txwisize + m->m_pkthdr.len; /* roundup to 32-bit alignment */ xferlen = (xferlen + 3) & ~3; txd = (struct mtw_txd *)&data->desc; txd->len = htole16(xferlen); wh = mtod(m, struct ieee80211_frame *); /* * Ether both are true or both are false, the header * are nicely aligned to 32-bit. So, no L2 padding. */ if (IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh)) pad = 0; else pad = 2; /* setup TX Wireless Information */ txwi = (struct mtw_txwi *)(txd + 1); txwi->len = htole16(m->m_pkthdr.len - pad); if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { mcs |= MTW_PHY_CCK; if (ridx != MTW_RIDX_CCK1 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) mcs |= MTW_PHY_SHPRE; } else if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) { mcs |= MTW_PHY_OFDM; } else if (rt2860_rates[ridx].phy == IEEE80211_T_HT) { /* XXX TODO: [adrian] set short preamble for MCS? */ mcs |= MTW_PHY_HT; /* Mixed, not greenfield */ } txwi->phy = htole16(mcs); /* check if RTS/CTS or CTS-to-self protection is required */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && ((m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) || ((ic->ic_flags & IEEE80211_F_USEPROT) && rt2860_rates[ridx].phy == IEEE80211_T_OFDM) || ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) && rt2860_rates[ridx].phy == IEEE80211_T_HT))) txwi->txop |= MTW_TX_TXOP_HT; else txwi->txop |= MTW_TX_TXOP_BACKOFF; } /* This function must be called locked */ static int mtw_tx(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; //const struct ieee80211_txparam *tp = ni->ni_txparms; struct mtw_node *rn = MTW_NODE(ni); struct mtw_tx_data *data; struct mtw_txd *txd; struct mtw_txwi *txwi; uint16_t qos; uint16_t dur; uint16_t qid; uint8_t type; uint8_t tid; uint16_t ridx; uint8_t ctl_ridx; uint16_t qflags; uint8_t xflags = 0; int hasqos; MTW_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m, struct ieee80211_frame *); const struct ieee80211_txparam *tp = ni->ni_txparms; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; qflags = htole16(MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_HCCA); if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) { uint8_t *frm; frm = ieee80211_getqos(wh); //device_printf(sc->sc_dev,"JSS:frm:%d",*frm); qos = le16toh(*(const uint16_t *)frm); tid = ieee80211_gettid(wh); qid = TID_TO_WME_AC(tid); qflags |= MTW_TXD_QSEL_EDCA; } else { qos = 0; tid = 0; qid = WME_AC_BE; } if (type & IEEE80211_FC0_TYPE_MGT) { qid = 0; } if (type != IEEE80211_FC0_TYPE_DATA) qflags |= htole16(MTW_TXD_WIV); if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) { /* XXX TODO: methodize for 11n; use MCS0 for 11NA/11NG */ ridx = (ic->ic_curmode == IEEE80211_MODE_11A || ic->ic_curmode == IEEE80211_MODE_11NA) ? MTW_RIDX_OFDM6 : MTW_RIDX_CCK1; if (type == IEEE80211_MODE_11NG) { ridx = 12; } ctl_ridx = rt2860_rates[ridx].ctl_ridx; } else { if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { ridx = rn->fix_ridx; } else { ridx = rn->amrr_ridx; ctl_ridx = rt2860_rates[ridx].ctl_ridx; } } if (hasqos) xflags = 0; else xflags = MTW_TX_NSEQ; if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK)) { xflags |= MTW_TX_ACK; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) dur = rt2860_rates[ctl_ridx].sp_ack_dur; else dur = rt2860_rates[ctl_ridx].lp_ack_dur; USETW(wh->i_dur, dur); } /* reserve slots for mgmt packets, just in case */ if (sc->sc_epq[qid].tx_nfree < 3) { MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx ring %d is full\n", qid); return (-1); } data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next); sc->sc_epq[qid].tx_nfree--; txd = (struct mtw_txd *)&data->desc; txd->flags = qflags; txwi = (struct mtw_txwi *)(txd + 1); txwi->xflags = xflags; txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ? MTW_AID2WCID(ni->ni_associd) : 0xff; /* clear leftover garbage bits */ txwi->flags = 0; txwi->txop = 0; data->m = m; data->ni = ni; data->ridx = ridx; + ieee80211_output_seqno_assign(ni, -1, m); + mtw_set_tx_desc(sc, data); /* * The chip keeps track of 2 kind of Tx stats, * * TX_STAT_FIFO, for per WCID stats, and * * TX_STA_CNT0 for all-TX-in-one stats. * * To use FIFO stats, we need to store MCS into the driver-private * PacketID field. So that, we can tell whose stats when we read them. * We add 1 to the MCS because setting the PacketID field to 0 means * that we don't want feedback in TX_STAT_FIFO. * And, that's what we want for STA mode, since TX_STA_CNT0 does the * job. * * FIFO stats doesn't count Tx with WCID 0xff, so we do this in * run_tx(). */ if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_MBSS) { /* * Unlike PCI based devices, we don't get any interrupt from * USB devices, so we simulate FIFO-is-full interrupt here. * Ralink recommends to drain FIFO stats every 100 ms, but 16 * slots quickly get fulled. To prevent overflow, increment a * counter on every FIFO stat request, so we know how many slots * are left. We do this only in HOSTAP or multiple vap mode * since FIFO stats are used only in those modes. We just drain * stats. AMRR gets updated every 1 sec by run_ratectl_cb() via * callout. Call it early. Otherwise overflow. */ if (sc->fifo_cnt++ == 10) { /* * With multiple vaps or if_bridge, if_start() is called * with a non-sleepable lock, tcpinp. So, need to defer. */ uint32_t i = MTW_CMDQ_GET(&sc->cmdq_store); MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "cmdq_store=%d\n", i); sc->cmdq[i].func = mtw_drain_fifo; sc->cmdq[i].arg0 = sc; ieee80211_runtask(ic, &sc->cmdq_task); } } STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[mtw_wme_ac_xfer_map[qid]]); MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending data frame len=%d rate=%d qid=%d\n", m->m_pkthdr.len + (int)(sizeof(struct mtw_txd) + sizeof(struct mtw_txwi)), rt2860_rates[ridx].rate, qid); return (0); } static int mtw_tx_mgt(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct mtw_node *rn = MTW_NODE(ni); struct mtw_tx_data *data; struct ieee80211_frame *wh; struct mtw_txd *txd; struct mtw_txwi *txwi; uint8_t type; uint16_t dur; uint8_t ridx = rn->mgt_ridx; uint8_t xflags = 0; uint8_t wflags = 0; MTW_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m, struct ieee80211_frame *); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) wflags |= MTW_TX_TS; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { xflags |= MTW_TX_ACK; dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); } type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; if (sc->sc_epq[0].tx_nfree == 0) /* let caller free mbuf */ return (EIO); data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct mtw_txd *)&data->desc; txd->flags = htole16( MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_EDCA); if (type != IEEE80211_FC0_TYPE_DATA) txd->flags |= htole16(MTW_TXD_WIV); txwi = (struct mtw_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->xflags = xflags; txwi->flags = wflags; txwi->txop = 0; /* clear leftover garbage bits */ data->m = m; data->ni = ni; data->ridx = ridx; MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n", m->m_pkthdr.len + (int)(sizeof(struct mtw_txd) + sizeof(struct mtw_txwi)), rt2860_rates[ridx].rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[MTW_BULK_TX_BE]); return (0); } static int mtw_sendprot(struct mtw_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; struct mtw_tx_data *data; struct mtw_txd *txd; struct mtw_txwi *txwi; struct mbuf *mprot; int ridx; int protrate; uint8_t wflags = 0; uint8_t xflags = 0; MTW_LOCK_ASSERT(sc, MA_OWNED); /* check that there are free slots before allocating the mbuf */ if (sc->sc_epq[0].tx_nfree == 0) /* let caller free mbuf */ return (ENOBUFS); mprot = ieee80211_alloc_prot(ni, m, rate, prot); if (mprot == NULL) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "could not allocate mbuf\n"); return (ENOBUFS); } protrate = ieee80211_ctl_rate(ic->ic_rt, rate); wflags = MTW_TX_FRAG; xflags = 0; if (prot == IEEE80211_PROT_RTSCTS) xflags |= MTW_TX_ACK; data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct mtw_txd *)&data->desc; txd->flags = RT2860_TX_QSEL_EDCA; txwi = (struct mtw_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->flags = wflags; txwi->xflags = xflags; txwi->txop = 0; /* clear leftover garbage bits */ data->m = mprot; data->ni = ieee80211_ref_node(ni); /* XXX TODO: methodize with MCS rates */ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == protrate) break; data->ridx = ridx; mtw_set_tx_desc(sc, data); MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending prot len=%u rate=%u\n", m->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[0]); return (0); } static int mtw_tx_param(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct mtw_tx_data *data; struct mtw_txd *txd; struct mtw_txwi *txwi; uint8_t ridx; uint8_t rate; uint8_t opflags = 0; uint8_t xflags = 0; int error; MTW_LOCK_ASSERT(sc, MA_OWNED); KASSERT(params != NULL, ("no raw xmit params")); rate = params->ibp_rate0; if (!ieee80211_isratevalid(ic->ic_rt, rate)) { /* let caller free mbuf */ return (EINVAL); } if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) xflags |= MTW_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) { error = mtw_sendprot(sc, m, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error) { device_printf(sc->sc_dev, "%s:%d %d\n", __FILE__, __LINE__, error); return (error); } opflags |= MTW_TX_TXOP_SIFS; } if (sc->sc_epq[0].tx_nfree == 0) { /* let caller free mbuf */ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending raw frame, but tx ring is full\n"); return (EIO); } data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); sc->sc_epq[0].tx_nfree--; txd = (struct mtw_txd *)&data->desc; txd->flags = htole16( MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_EDCA); // txd->flags = htole16(MTW_TXD_QSEL_EDCA); txwi = (struct mtw_txwi *)(txd + 1); txwi->wcid = 0xff; txwi->xflags = xflags; txwi->txop = opflags; txwi->flags = 0; /* clear leftover garbage bits */ data->m = m; data->ni = ni; /* XXX TODO: methodize with MCS rates */ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) break; data->ridx = ridx; + ieee80211_output_seqno_assign(ni, -1, m); + mtw_set_tx_desc(sc, data); MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending raw frame len=%u rate=%u\n", m->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); usbd_transfer_start(sc->sc_xfer[MTW_BULK_RAW_TX]); return (0); } static int mtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct mtw_softc *sc = ni->ni_ic->ic_softc; int error = 0; MTW_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!(sc->sc_flags & MTW_RUNNING)) { error = ENETDOWN; goto done; } if (params == NULL) { /* tx mgt packet */ if ((error = mtw_tx_mgt(sc, m, ni)) != 0) { MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "mgt tx failed\n"); goto done; } } else { /* tx raw packet with param */ if ((error = mtw_tx_param(sc, m, ni, params)) != 0) { MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx with param failed\n"); goto done; } } done: MTW_UNLOCK(sc); if (error != 0) { if (m != NULL) m_freem(m); } return (error); } static int mtw_transmit(struct ieee80211com *ic, struct mbuf *m) { struct mtw_softc *sc = ic->ic_softc; int error; MTW_LOCK(sc); if ((sc->sc_flags & MTW_RUNNING) == 0) { MTW_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { MTW_UNLOCK(sc); return (error); } mtw_start(sc); MTW_UNLOCK(sc); return (0); } static void mtw_start(struct mtw_softc *sc) { struct ieee80211_node *ni; struct mbuf *m; MTW_LOCK_ASSERT(sc, MA_OWNED); if ((sc->sc_flags & MTW_RUNNING) == 0) { return; } while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (mtw_tx(sc, m, ni) != 0) { mbufq_prepend(&sc->sc_snd, m); break; } } } static void mtw_parent(struct ieee80211com *ic) { struct mtw_softc *sc = ic->ic_softc; MTW_LOCK(sc); if (sc->sc_detached) { MTW_UNLOCK(sc); return; } if (!(sc->sc_flags & MTW_RUNNING) && ic->ic_nrunning > 0) { mtw_init_locked(sc); MTW_UNLOCK(sc); ieee80211_start_all(ic); return; } if (!(sc->sc_flags & MTW_RUNNING) && ic->ic_nrunning > 0) { mtw_update_promisc_locked(sc); MTW_UNLOCK(sc); return; } if ((sc->sc_flags & MTW_RUNNING) && sc->rvp_cnt <= 1 && ic->ic_nrunning == 0) { mtw_stop(sc); MTW_UNLOCK(sc); return; } return; } static void mt7601_set_agc(struct mtw_softc *sc, uint8_t agc) { uint8_t bbp; mtw_bbp_write(sc, 66, agc); mtw_bbp_write(sc, 195, 0x87); bbp = (agc & 0xf0) | 0x08; mtw_bbp_write(sc, 196, bbp); } static int mtw_mcu_calibrate(struct mtw_softc *sc, int func, uint32_t val) { struct mtw_mcu_cmd_8 cmd; cmd.func = htole32(func); cmd.val = htole32(val); return (mtw_mcu_cmd(sc, 31, &cmd, sizeof(struct mtw_mcu_cmd_8))); } static int mtw_rf_write(struct mtw_softc *sc, uint8_t bank, uint8_t reg, uint8_t val) { uint32_t tmp; int error, ntries, shift; for (ntries = 0; ntries < 10; ntries++) { if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0) return (error); if (!(tmp & MTW_RF_CSR_KICK)) break; } if (ntries == 10) return (ETIMEDOUT); if (sc->asic_ver == 0x7601) shift = MT7601_BANK_SHIFT; else shift = MT7610_BANK_SHIFT; tmp = MTW_RF_CSR_WRITE | MTW_RF_CSR_KICK | (bank & 0xf) << shift | reg << 8 | val; return (mtw_write(sc, MTW_RF_CSR, tmp)); } void mtw_select_chan_group(struct mtw_softc *sc, int group) { uint32_t tmp; uint8_t bbp; /* Tx band 20MHz 2G */ mtw_read(sc, MTW_TX_BAND_CFG, &tmp); tmp &= ~( MTW_TX_BAND_SEL_2G | MTW_TX_BAND_SEL_5G | MTW_TX_BAND_UPPER_40M); tmp |= (group == 0) ? MTW_TX_BAND_SEL_2G : MTW_TX_BAND_SEL_5G; mtw_write(sc, MTW_TX_BAND_CFG, tmp); /* select 20 MHz bandwidth */ mtw_bbp_read(sc, 4, &bbp); bbp &= ~0x18; bbp |= 0x40; mtw_bbp_write(sc, 4, bbp); /* calibrate BBP */ mtw_bbp_write(sc, 69, 0x12); mtw_bbp_write(sc, 91, 0x07); mtw_bbp_write(sc, 195, 0x23); mtw_bbp_write(sc, 196, 0x17); mtw_bbp_write(sc, 195, 0x24); mtw_bbp_write(sc, 196, 0x06); mtw_bbp_write(sc, 195, 0x81); mtw_bbp_write(sc, 196, 0x12); mtw_bbp_write(sc, 195, 0x83); mtw_bbp_write(sc, 196, 0x17); mtw_rf_write(sc, 5, 8, 0x00); // mtw_mcu_calibrate(sc, 0x6, 0x10001); /* set initial AGC value */ mt7601_set_agc(sc, 0x14); } static int mtw_rf_read(struct mtw_softc *sc, uint8_t bank, uint8_t reg, uint8_t *val) { uint32_t tmp; int error, ntries, shift; for (ntries = 0; ntries < 100; ntries++) { if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0) return (error); if (!(tmp & MTW_RF_CSR_KICK)) break; } if (ntries == 100) return (ETIMEDOUT); if (sc->asic_ver == 0x7601) shift = MT7601_BANK_SHIFT; else shift = MT7610_BANK_SHIFT; tmp = MTW_RF_CSR_KICK | (bank & 0xf) << shift | reg << 8; if ((error = mtw_write(sc, MTW_RF_CSR, tmp)) != 0) return (error); for (ntries = 0; ntries < 100; ntries++) { if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0) return (error); if (!(tmp & MTW_RF_CSR_KICK)) break; } if (ntries == 100) return (ETIMEDOUT); *val = tmp & 0xff; return (0); } static void mt7601_set_chan(struct mtw_softc *sc, u_int chan) { uint32_t tmp; uint8_t bbp, rf, txpow1; int i; /* find the settings for this channel */ for (i = 0; mt7601_rf_chan[i].chan != chan; i++) ; mtw_rf_write(sc, 0, 17, mt7601_rf_chan[i].r17); mtw_rf_write(sc, 0, 18, mt7601_rf_chan[i].r18); mtw_rf_write(sc, 0, 19, mt7601_rf_chan[i].r19); mtw_rf_write(sc, 0, 20, mt7601_rf_chan[i].r20); /* use Tx power values from EEPROM */ txpow1 = sc->txpow1[i]; /* Tx automatic level control */ mtw_read(sc, MTW_TX_ALC_CFG0, &tmp); tmp &= ~0x3f3f; tmp |= (txpow1 & 0x3f); mtw_write(sc, MTW_TX_ALC_CFG0, tmp); /* LNA */ mtw_bbp_write(sc, 62, 0x37 - sc->lna[0]); mtw_bbp_write(sc, 63, 0x37 - sc->lna[0]); mtw_bbp_write(sc, 64, 0x37 - sc->lna[0]); /* VCO calibration */ mtw_rf_write(sc, 0, 4, 0x0a); mtw_rf_write(sc, 0, 5, 0x20); mtw_rf_read(sc, 0, 4, &rf); mtw_rf_write(sc, 0, 4, rf | 0x80); /* select 20 MHz bandwidth */ mtw_bbp_read(sc, 4, &bbp); bbp &= ~0x18; bbp |= 0x40; mtw_bbp_write(sc, 4, bbp); mtw_bbp_write(sc, 178, 0xff); } static int mtw_set_chan(struct mtw_softc *sc, struct ieee80211_channel *c) { struct ieee80211com *ic = &sc->sc_ic; u_int chan, group; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return (EINVAL); /* determine channel group */ if (chan <= 14) group = 0; else if (chan <= 64) group = 1; else if (chan <= 128) group = 2; else group = 3; if (group != sc->sc_chan_group || !sc->sc_bw_calibrated) mtw_select_chan_group(sc, group); sc->sc_chan_group = group; /* chipset specific */ if (sc->asic_ver == 0x7601) mt7601_set_chan(sc, chan); DELAY(1000); return (0); } static void mtw_set_channel(struct ieee80211com *ic) { struct mtw_softc *sc = ic->ic_softc; MTW_LOCK(sc); mtw_set_chan(sc, ic->ic_curchan); MTW_UNLOCK(sc); return; } static void mtw_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { // struct mtw_softc *sc = ic->ic_softc; uint8_t bands[IEEE80211_MODE_BYTES]; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); setbit(bands, IEEE80211_MODE_11NG); /* Note: for now, only support HT20 channels */ ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); } static void mtw_scan_start(struct ieee80211com *ic) { struct mtw_softc *sc = ic->ic_softc; MTW_LOCK(sc); /* abort TSF synchronization */ mtw_abort_tsf_sync(sc); mtw_set_bssid(sc, ieee80211broadcastaddr); MTW_UNLOCK(sc); return; } static void mtw_scan_end(struct ieee80211com *ic) { struct mtw_softc *sc = ic->ic_softc; MTW_LOCK(sc); mtw_enable_tsf_sync(sc); mtw_set_bssid(sc, sc->sc_bssid); MTW_UNLOCK(sc); return; } /* * Could be called from ieee80211_node_timeout() * (non-sleepable thread) */ static void mtw_update_beacon(struct ieee80211vap *vap, int item) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; struct ieee80211_node *ni = vap->iv_bss; struct mtw_softc *sc = ic->ic_softc; struct mtw_vap *rvp = MTW_VAP(vap); int mcast = 0; uint32_t i; switch (item) { case IEEE80211_BEACON_ERP: mtw_updateslot(ic); break; case IEEE80211_BEACON_HTINFO: mtw_updateprot(ic); break; case IEEE80211_BEACON_TIM: mcast = 1; /*TODO*/ break; default: break; } setbit(bo->bo_flags, item); if (rvp->beacon_mbuf == NULL) { rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); if (rvp->beacon_mbuf == NULL) return; } ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast); i = MTW_CMDQ_GET(&sc->cmdq_store); MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "cmdq_store=%d\n", i); sc->cmdq[i].func = mtw_update_beacon_cb; sc->cmdq[i].arg0 = vap; ieee80211_runtask(ic, &sc->cmdq_task); return; } static void mtw_update_beacon_cb(void *arg) { struct ieee80211vap *vap = arg; struct ieee80211_node *ni = vap->iv_bss; struct mtw_vap *rvp = MTW_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct mtw_softc *sc = ic->ic_softc; struct mtw_txwi txwi; struct mbuf *m; uint16_t txwisize; uint8_t ridx; if (ni->ni_chan == IEEE80211_CHAN_ANYC) return; if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) return; /* * No need to call ieee80211_beacon_update(), mtw_update_beacon() * is taking care of appropriate calls. */ if (rvp->beacon_mbuf == NULL) { rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); if (rvp->beacon_mbuf == NULL) return; } m = rvp->beacon_mbuf; memset(&txwi, 0, sizeof(txwi)); txwi.wcid = 0xff; txwi.len = htole16(m->m_pkthdr.len); /* send beacons at the lowest available rate */ ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? MTW_RIDX_OFDM6 : MTW_RIDX_CCK1; txwi.phy = htole16(rt2860_rates[ridx].mcs); if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) txwi.phy |= htole16(MTW_PHY_OFDM); txwi.txop = MTW_TX_TXOP_HT; txwi.flags = MTW_TX_TS; txwi.xflags = MTW_TX_NSEQ; txwisize = sizeof(txwi); mtw_write_region_1(sc, MTW_BCN_BASE, (uint8_t *)&txwi, txwisize); mtw_write_region_1(sc, MTW_BCN_BASE + txwisize, mtod(m, uint8_t *), (m->m_pkthdr.len + 1) & ~1); } static void mtw_updateprot(struct ieee80211com *ic) { struct mtw_softc *sc = ic->ic_softc; uint32_t i; i = MTW_CMDQ_GET(&sc->cmdq_store); MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "test cmdq_store=%d\n", i); sc->cmdq[i].func = mtw_updateprot_cb; sc->cmdq[i].arg0 = ic; ieee80211_runtask(ic, &sc->cmdq_task); } static void mtw_updateprot_cb(void *arg) { struct ieee80211com *ic = arg; struct mtw_softc *sc = ic->ic_softc; uint32_t tmp; tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; /* setup protection frame rate (MCS code) */ tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? rt2860_rates[MTW_RIDX_OFDM6].mcs | MTW_PHY_OFDM : rt2860_rates[MTW_RIDX_CCK11].mcs; /* CCK frames don't require protection */ mtw_write(sc, MTW_CCK_PROT_CFG, tmp); if (ic->ic_flags & IEEE80211_F_USEPROT) { if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) tmp |= RT2860_PROT_CTRL_RTS_CTS; else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) tmp |= RT2860_PROT_CTRL_CTS; } mtw_write(sc, MTW_OFDM_PROT_CFG, tmp); } static void mtw_usb_timeout_cb(void *arg) { struct ieee80211vap *vap = arg; struct mtw_softc *sc = vap->iv_ic->ic_softc; MTW_LOCK_ASSERT(sc, MA_OWNED); if (vap->iv_state == IEEE80211_S_SCAN) { MTW_DPRINTF(sc, MTW_DEBUG_USB | MTW_DEBUG_STATE, "timeout caused by scan\n"); /* cancel bgscan */ ieee80211_cancel_scan(vap); } else { MTW_DPRINTF(sc, MTW_DEBUG_USB | MTW_DEBUG_STATE, "timeout by unknown cause\n"); } } static int mtw_reset(struct mtw_softc *sc) { usb_device_request_t req; uint16_t tmp; uint16_t actlen; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = MTW_RESET; USETW(req.wValue, 1); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, &tmp, 0, &actlen, 1000)); } static void mtw_update_promisc_locked(struct mtw_softc *sc) { uint32_t tmp; mtw_read(sc, MTW_RX_FILTR_CFG, &tmp); tmp |= MTW_DROP_UC_NOME; if (sc->sc_ic.ic_promisc > 0) tmp &= ~MTW_DROP_UC_NOME; mtw_write(sc, MTW_RX_FILTR_CFG, tmp); MTW_DPRINTF(sc, MTW_DEBUG_RECV, "%s promiscuous mode\n", (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving"); } static void mtw_update_promisc(struct ieee80211com *ic) { struct mtw_softc *sc = ic->ic_softc; if ((sc->sc_flags & MTW_RUNNING) == 0) return; MTW_LOCK(sc); mtw_update_promisc_locked(sc); MTW_UNLOCK(sc); } static void mtw_enable_tsf_sync(struct mtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; int error; mtw_read(sc, MTW_BCN_TIME_CFG, &tmp); tmp &= ~0x1fffff; tmp |= vap->iv_bss->ni_intval * 16; tmp |= MTW_TSF_TIMER_EN | MTW_TBTT_TIMER_EN; /* local TSF is always updated with remote TSF on beacon reception */ tmp |= 1 << MTW_TSF_SYNC_MODE_SHIFT; error = mtw_write(sc, MTW_BCN_TIME_CFG, tmp); if (error != 0) { device_printf(sc->sc_dev, "enable_tsf_sync failed error:%d\n", error); } return; } static void mtw_enable_mrr(struct mtw_softc *sc) { #define CCK(mcs) (mcs) #define OFDM(mcs) (1 << 3 | (mcs)) mtw_write(sc, MTW_LG_FBK_CFG0, OFDM(6) << 28 | /* 54->48 */ OFDM(5) << 24 | /* 48->36 */ OFDM(4) << 20 | /* 36->24 */ OFDM(3) << 16 | /* 24->18 */ OFDM(2) << 12 | /* 18->12 */ OFDM(1) << 8 | /* 12-> 9 */ OFDM(0) << 4 | /* 9-> 6 */ OFDM(0)); /* 6-> 6 */ mtw_write(sc, MTW_LG_FBK_CFG1, CCK(2) << 12 | /* 11->5.5 */ CCK(1) << 8 | /* 5.5-> 2 */ CCK(0) << 4 | /* 2-> 1 */ CCK(0)); /* 1-> 1 */ #undef OFDM #undef CCK } static void mtw_set_txpreamble(struct mtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t tmp; mtw_read(sc, MTW_AUTO_RSP_CFG, &tmp); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= MTW_CCK_SHORT_EN; else tmp &= ~MTW_CCK_SHORT_EN; mtw_write(sc, MTW_AUTO_RSP_CFG, tmp); } static void mtw_set_basicrates(struct mtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; /* set basic rates mask */ if (ic->ic_curmode == IEEE80211_MODE_11B) mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x003); else if (ic->ic_curmode == IEEE80211_MODE_11A) mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x150); else /* 11g */ mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x17f); } static void mtw_set_bssid(struct mtw_softc *sc, const uint8_t *bssid) { mtw_write(sc, MTW_MAC_BSSID_DW0, bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); mtw_write(sc, MTW_MAC_BSSID_DW1, bssid[4] | bssid[5] << 8); } static void mtw_set_macaddr(struct mtw_softc *sc, const uint8_t *addr) { mtw_write(sc, MTW_MAC_ADDR_DW0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); mtw_write(sc, MTW_MAC_ADDR_DW1, addr[4] | addr[5] << 8 | 0xff << 16); } static void mtw_updateslot(struct ieee80211com *ic) { struct mtw_softc *sc = ic->ic_softc; uint32_t i; i = MTW_CMDQ_GET(&sc->cmdq_store); MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "cmdq_store=%d\n", i); sc->cmdq[i].func = mtw_updateslot_cb; sc->cmdq[i].arg0 = ic; ieee80211_runtask(ic, &sc->cmdq_task); return; } /* ARGSUSED */ static void mtw_updateslot_cb(void *arg) { struct ieee80211com *ic = arg; struct mtw_softc *sc = ic->ic_softc; uint32_t tmp; mtw_read(sc, MTW_BKOFF_SLOT_CFG, &tmp); tmp &= ~0xff; tmp |= IEEE80211_GET_SLOTTIME(ic); mtw_write(sc, MTW_BKOFF_SLOT_CFG, tmp); } static void mtw_update_mcast(struct ieee80211com *ic) { } static int8_t mtw_rssi2dbm(struct mtw_softc *sc, uint8_t rssi, uint8_t rxchain) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_channel *c = ic->ic_curchan; int delta; if (IEEE80211_IS_CHAN_5GHZ(c)) { u_int chan = ieee80211_chan2ieee(ic, c); delta = sc->rssi_5ghz[rxchain]; /* determine channel group */ if (chan <= 64) delta -= sc->lna[1]; else if (chan <= 128) delta -= sc->lna[2]; else delta -= sc->lna[3]; } else delta = sc->rssi_2ghz[rxchain] - sc->lna[0]; return (-12 - delta - rssi); } static int mt7601_bbp_init(struct mtw_softc *sc) { uint8_t bbp; int i, error, ntries; /* wait for BBP to wake up */ for (ntries = 0; ntries < 20; ntries++) { if ((error = mtw_bbp_read(sc, 0, &bbp)) != 0) return (error); if (bbp != 0 && bbp != 0xff) break; } if (ntries == 20) return (ETIMEDOUT); mtw_bbp_read(sc, 3, &bbp); mtw_bbp_write(sc, 3, 0); mtw_bbp_read(sc, 105, &bbp); mtw_bbp_write(sc, 105, 0); /* initialize BBP registers to default values */ for (i = 0; i < nitems(mt7601_def_bbp); i++) { if ((error = mtw_bbp_write(sc, mt7601_def_bbp[i].reg, mt7601_def_bbp[i].val)) != 0) return (error); } sc->sc_bw_calibrated = 0; return (0); } static int mt7601_rf_init(struct mtw_softc *sc) { int i, error; /* RF bank 0 */ for (i = 0; i < nitems(mt7601_rf_bank0); i++) { error = mtw_rf_write(sc, 0, mt7601_rf_bank0[i].reg, mt7601_rf_bank0[i].val); if (error != 0) return (error); } /* RF bank 4 */ for (i = 0; i < nitems(mt7601_rf_bank4); i++) { error = mtw_rf_write(sc, 4, mt7601_rf_bank4[i].reg, mt7601_rf_bank4[i].val); if (error != 0) return (error); } /* RF bank 5 */ for (i = 0; i < nitems(mt7601_rf_bank5); i++) { error = mtw_rf_write(sc, 5, mt7601_rf_bank5[i].reg, mt7601_rf_bank5[i].val); if (error != 0) return (error); } return (0); } static int mtw_txrx_enable(struct mtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t tmp; int error, ntries; mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_TX_EN); for (ntries = 0; ntries < 200; ntries++) { if ((error = mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp)) != 0) { return (error); } if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0) break; mtw_delay(sc, 50); } if (ntries == 200) { return (ETIMEDOUT); } DELAY(50); tmp |= MTW_RX_DMA_EN | MTW_TX_DMA_EN | MTW_TX_WB_DDONE; mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp); /* enable Rx bulk aggregation (set timeout and limit) */ tmp = MTW_USB_TX_EN | MTW_USB_RX_EN | MTW_USB_RX_AGG_EN | MTW_USB_RX_AGG_TO(128) | MTW_USB_RX_AGG_LMT(2); mtw_write(sc, MTW_USB_DMA_CFG, tmp); /* set Rx filter */ tmp = MTW_DROP_CRC_ERR | MTW_DROP_PHY_ERR; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= MTW_DROP_UC_NOME | MTW_DROP_DUPL | MTW_DROP_CTS | MTW_DROP_BA | MTW_DROP_ACK | MTW_DROP_VER_ERR | MTW_DROP_CTRL_RSV | MTW_DROP_CFACK | MTW_DROP_CFEND; if (ic->ic_opmode == IEEE80211_M_STA) tmp |= MTW_DROP_RTS | MTW_DROP_PSPOLL; } mtw_write(sc, MTW_RX_FILTR_CFG, tmp); mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_RX_EN | MTW_MAC_TX_EN); return (0); } static int mt7601_rxdc_cal(struct mtw_softc *sc) { uint32_t tmp; uint8_t bbp; int ntries; mtw_read(sc, MTW_MAC_SYS_CTRL, &tmp); mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_RX_EN); mtw_bbp_write(sc, 158, 0x8d); mtw_bbp_write(sc, 159, 0xfc); mtw_bbp_write(sc, 158, 0x8c); mtw_bbp_write(sc, 159, 0x4c); for (ntries = 0; ntries < 20; ntries++) { DELAY(300); mtw_bbp_write(sc, 158, 0x8c); mtw_bbp_read(sc, 159, &bbp); if (bbp == 0x0c) break; } if (ntries == 20) return (ETIMEDOUT); mtw_write(sc, MTW_MAC_SYS_CTRL, 0); mtw_bbp_write(sc, 158, 0x8d); mtw_bbp_write(sc, 159, 0xe0); mtw_write(sc, MTW_MAC_SYS_CTRL, tmp); return (0); } static int mt7601_r49_read(struct mtw_softc *sc, uint8_t flag, int8_t *val) { uint8_t bbp; mtw_bbp_read(sc, 47, &bbp); bbp = 0x90; mtw_bbp_write(sc, 47, bbp); bbp &= ~0x0f; bbp |= flag; mtw_bbp_write(sc, 47, bbp); return (mtw_bbp_read(sc, 49, val)); } static int mt7601_rf_temperature(struct mtw_softc *sc, int8_t *val) { uint32_t rfb, rfs; uint8_t bbp; int ntries; mtw_read(sc, MTW_RF_BYPASS0, &rfb); mtw_read(sc, MTW_RF_SETTING0, &rfs); mtw_write(sc, MTW_RF_BYPASS0, 0); mtw_write(sc, MTW_RF_SETTING0, 0x10); mtw_write(sc, MTW_RF_BYPASS0, 0x10); mtw_bbp_read(sc, 47, &bbp); bbp &= ~0x7f; bbp |= 0x10; mtw_bbp_write(sc, 47, bbp); mtw_bbp_write(sc, 22, 0x40); for (ntries = 0; ntries < 10; ntries++) { mtw_bbp_read(sc, 47, &bbp); if ((bbp & 0x10) == 0) break; } if (ntries == 10) return (ETIMEDOUT); mt7601_r49_read(sc, MT7601_R47_TEMP, val); mtw_bbp_write(sc, 22, 0); mtw_bbp_read(sc, 21, &bbp); bbp |= 0x02; mtw_bbp_write(sc, 21, bbp); bbp &= ~0x02; mtw_bbp_write(sc, 21, bbp); mtw_write(sc, MTW_RF_BYPASS0, 0); mtw_write(sc, MTW_RF_SETTING0, rfs); mtw_write(sc, MTW_RF_BYPASS0, rfb); return (0); } static int mt7601_rf_setup(struct mtw_softc *sc) { uint32_t tmp; uint8_t rf; int error; if (sc->sc_rf_calibrated) return (0); /* init RF registers */ if ((error = mt7601_rf_init(sc)) != 0) return (error); /* init frequency offset */ mtw_rf_write(sc, 0, 12, sc->rf_freq_offset); mtw_rf_read(sc, 0, 12, &rf); /* read temperature */ mt7601_rf_temperature(sc, &rf); sc->bbp_temp = rf; device_printf(sc->sc_dev, "BBP temp 0x%x\n", rf); mtw_rf_read(sc, 0, 7, &rf); if ((error = mtw_mcu_calibrate(sc, 0x1, 0)) != 0) return (error); mtw_delay(sc, 100); mtw_rf_read(sc, 0, 7, &rf); /* Calibrate VCO RF 0/4 */ mtw_rf_write(sc, 0, 4, 0x0a); mtw_rf_write(sc, 0, 4, 0x20); mtw_rf_read(sc, 0, 4, &rf); mtw_rf_write(sc, 0, 4, rf | 0x80); if ((error = mtw_mcu_calibrate(sc, 0x9, 0)) != 0) return (error); if ((error = mt7601_rxdc_cal(sc)) != 0) return (error); if ((error = mtw_mcu_calibrate(sc, 0x6, 1)) != 0) return (error); if ((error = mtw_mcu_calibrate(sc, 0x6, 0)) != 0) return (error); if ((error = mtw_mcu_calibrate(sc, 0x4, 0)) != 0) return (error); if ((error = mtw_mcu_calibrate(sc, 0x5, 0)) != 0) return (error); mtw_read(sc, MTW_LDO_CFG0, &tmp); tmp &= ~(1 << 4); tmp |= (1 << 2); mtw_write(sc, MTW_LDO_CFG0, tmp); if ((error = mtw_mcu_calibrate(sc, 0x8, 0)) != 0) return (error); if ((error = mt7601_rxdc_cal(sc)) != 0) return (error); sc->sc_rf_calibrated = 1; return (0); } static void mtw_set_txrts(struct mtw_softc *sc) { uint32_t tmp; /* set RTS threshold */ mtw_read(sc, MTW_TX_RTS_CFG, &tmp); tmp &= ~0xffff00; tmp |= 0x1000 << MTW_RTS_THRES_SHIFT; mtw_write(sc, MTW_TX_RTS_CFG, tmp); } static int mtw_mcu_radio(struct mtw_softc *sc, int func, uint32_t val) { struct mtw_mcu_cmd_16 cmd; cmd.r1 = htole32(func); cmd.r2 = htole32(val); cmd.r3 = 0; cmd.r4 = 0; return (mtw_mcu_cmd(sc, 20, &cmd, sizeof(struct mtw_mcu_cmd_16))); } static void mtw_init_locked(struct mtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; int i, error, ridx, ntries; if (ic->ic_nrunning > 1) return; mtw_stop(sc); for (i = 0; i != MTW_EP_QUEUES; i++) mtw_setup_tx_list(sc, &sc->sc_epq[i]); for (ntries = 0; ntries < 100; ntries++) { if ((error = mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp)) != 0) goto fail; if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0) break; DELAY(1000); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); error = ETIMEDOUT; goto fail; } tmp &= 0xff0; tmp |= MTW_TX_WB_DDONE; mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp); mtw_set_leds(sc, MTW_LED_MODE_ON); /* reset MAC and baseband */ mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_BBP_HRST | MTW_MAC_SRST); mtw_write(sc, MTW_USB_DMA_CFG, 0); mtw_write(sc, MTW_MAC_SYS_CTRL, 0); /* init MAC values */ if (sc->asic_ver == 0x7601) { for (i = 0; i < nitems(mt7601_def_mac); i++) mtw_write(sc, mt7601_def_mac[i].reg, mt7601_def_mac[i].val); } /* wait while MAC is busy */ for (ntries = 0; ntries < 100; ntries++) { if ((error = mtw_read(sc, MTW_MAC_STATUS_REG, &tmp)) != 0) goto fail; if (!(tmp & (MTW_RX_STATUS_BUSY | MTW_TX_STATUS_BUSY))) break; DELAY(1000); } if (ntries == 100) { error = ETIMEDOUT; goto fail; } /* set MAC address */ mtw_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); /* clear WCID attribute table */ mtw_set_region_4(sc, MTW_WCID_ATTR(0), 1, 8 * 32); mtw_write(sc, 0x1648, 0x00830083); mtw_read(sc, MTW_FCE_L2_STUFF, &tmp); tmp &= ~MTW_L2S_WR_MPDU_LEN_EN; mtw_write(sc, MTW_FCE_L2_STUFF, tmp); /* RTS config */ mtw_set_txrts(sc); /* clear Host to MCU mailbox */ mtw_write(sc, MTW_BBP_CSR, 0); mtw_write(sc, MTW_H2M_MAILBOX, 0); /* clear RX WCID search table */ mtw_set_region_4(sc, MTW_WCID_ENTRY(0), 0xffffffff, 512); /* abort TSF synchronization */ mtw_abort_tsf_sync(sc); mtw_read(sc, MTW_US_CYC_CNT, &tmp); tmp = (tmp & ~0xff); if (sc->asic_ver == 0x7601) tmp |= 0x1e; mtw_write(sc, MTW_US_CYC_CNT, tmp); /* clear shared key table */ mtw_set_region_4(sc, MTW_SKEY(0, 0), 0, 8 * 32); /* clear IV/EIV table */ mtw_set_region_4(sc, MTW_IVEIV(0), 0, 8 * 32); /* clear shared key mode */ mtw_write(sc, MTW_SKEY_MODE_0_7, 0); mtw_write(sc, MTW_SKEY_MODE_8_15, 0); /* txop truncation */ mtw_write(sc, MTW_TXOP_CTRL_CFG, 0x0000583f); /* init Tx power for all Tx rates */ for (ridx = 0; ridx < 5; ridx++) { if (sc->txpow20mhz[ridx] == 0xffffffff) continue; mtw_write(sc, MTW_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); } mtw_write(sc, MTW_TX_PWR_CFG7, 0); mtw_write(sc, MTW_TX_PWR_CFG9, 0); mtw_read(sc, MTW_CMB_CTRL, &tmp); tmp &= ~(1 << 18 | 1 << 14); mtw_write(sc, MTW_CMB_CTRL, tmp); /* clear USB DMA */ mtw_write(sc, MTW_USB_DMA_CFG, MTW_USB_TX_EN | MTW_USB_RX_EN | MTW_USB_RX_AGG_EN | MTW_USB_TX_CLEAR | MTW_USB_TXOP_HALT | MTW_USB_RX_WL_DROP); mtw_delay(sc, 50); mtw_read(sc, MTW_USB_DMA_CFG, &tmp); tmp &= ~(MTW_USB_TX_CLEAR | MTW_USB_TXOP_HALT | MTW_USB_RX_WL_DROP); mtw_write(sc, MTW_USB_DMA_CFG, tmp); /* enable radio */ mtw_mcu_radio(sc, 0x31, 0); /* init RF registers */ if (sc->asic_ver == 0x7601) mt7601_rf_init(sc); /* init baseband registers */ if (sc->asic_ver == 0x7601) error = mt7601_bbp_init(sc); if (error != 0) { device_printf(sc->sc_dev, "could not initialize BBP\n"); goto fail; } /* setup and calibrate RF */ error = mt7601_rf_setup(sc); if (error != 0) { device_printf(sc->sc_dev, "could not initialize RF\n"); goto fail; } /* select default channel */ mtw_set_chan(sc, ic->ic_curchan); /* setup initial protection mode */ mtw_updateprot_cb(ic); sc->sc_flags |= MTW_RUNNING; sc->cmdq_run = MTW_CMDQ_GO; for (i = 0; i != MTW_N_XFER; i++) usbd_xfer_set_stall(sc->sc_xfer[i]); usbd_transfer_start(sc->sc_xfer[MTW_BULK_RX]); error = mtw_txrx_enable(sc); if (error != 0) { goto fail; } return; fail: mtw_stop(sc); return; } static void mtw_stop(void *arg) { struct mtw_softc *sc = (struct mtw_softc *)arg; uint32_t tmp; int i, ntries, error; MTW_LOCK_ASSERT(sc, MA_OWNED); sc->sc_flags &= ~MTW_RUNNING; sc->ratectl_run = MTW_RATECTL_OFF; sc->cmdq_run = sc->cmdq_key_set; MTW_UNLOCK(sc); for (i = 0; i < MTW_N_XFER; i++) usbd_transfer_drain(sc->sc_xfer[i]); MTW_LOCK(sc); mtw_drain_mbufq(sc); if (sc->rx_m != NULL) { m_free(sc->rx_m); sc->rx_m = NULL; } /* Disable Tx/Rx DMA. */ mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp); tmp &= ~(MTW_RX_DMA_EN | MTW_TX_DMA_EN); mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp); // mtw_usb_dma_write(sc, 0); for (ntries = 0; ntries < 100; ntries++) { if (mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp) != 0) break; if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0) break; DELAY(10); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); } /* stop MAC Tx/Rx */ mtw_read(sc, MTW_MAC_SYS_CTRL, &tmp); tmp &= ~(MTW_MAC_RX_EN | MTW_MAC_TX_EN); mtw_write(sc, MTW_MAC_SYS_CTRL, tmp); /* disable RTS retry */ mtw_read(sc, MTW_TX_RTS_CFG, &tmp); tmp &= ~0xff; mtw_write(sc, MTW_TX_RTS_CFG, tmp); /* US_CYC_CFG */ mtw_read(sc, MTW_US_CYC_CNT, &tmp); tmp = (tmp & ~0xff); mtw_write(sc, MTW_US_CYC_CNT, tmp); /* stop PBF */ mtw_read(sc, MTW_PBF_CFG, &tmp); tmp &= ~0x3; mtw_write(sc, MTW_PBF_CFG, tmp); /* wait for pending Tx to complete */ for (ntries = 0; ntries < 100; ntries++) { if ((error = mtw_read(sc, MTW_TXRXQ_PCNT, &tmp)) != 0) break; if ((tmp & MTW_TX2Q_PCNT_MASK) == 0) break; } } static void mtw_delay(struct mtw_softc *sc, u_int ms) { usb_pause_mtx(mtx_owned(&sc->sc_mtx) ? &sc->sc_mtx : NULL, USB_MS_TO_TICKS(ms)); } static void mtw_update_chw(struct ieee80211com *ic) { printf("%s: TODO\n", __func__); } static int mtw_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { /* For now, no A-MPDU TX support in the driver */ return (0); } static device_method_t mtw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mtw_match), DEVMETHOD(device_attach, mtw_attach), DEVMETHOD(device_detach, mtw_detach), DEVMETHOD_END }; static driver_t mtw_driver = { .name = "mtw", .methods = mtw_methods, .size = sizeof(struct mtw_softc) }; DRIVER_MODULE(mtw, uhub, mtw_driver, mtw_driver_loaded, NULL); MODULE_DEPEND(mtw, wlan, 1, 1, 1); MODULE_DEPEND(mtw, usb, 1, 1, 1); MODULE_DEPEND(mtw, firmware, 1, 1, 1); MODULE_VERSION(mtw, 1); USB_PNP_HOST_INFO(mtw_devs); diff --git a/sys/dev/usb/wlan/if_uath.c b/sys/dev/usb/wlan/if_uath.c index b49c75032d77..cc303e565bca 100644 --- a/sys/dev/usb/wlan/if_uath.c +++ b/sys/dev/usb/wlan/if_uath.c @@ -1,2886 +1,2890 @@ /*- * SPDX-License-Identifier: (BSD-2-Clause AND BSD-1-Clause) * * Copyright (c) 2006 Sam Leffler, Errno Consulting * Copyright (c) 2008-2009 Weongyo Jeong * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ /* * This driver is distantly derived from a driver of the same name * by Damien Bergamini. The original copyright is included below: * * Copyright (c) 2006 * Damien Bergamini * * 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 AR5523 USB parts. * * The driver requires firmware to be loaded into the device. This * is done on device discovery from a user application (uathload) * that is launched by devd when a device with suitable product ID * is recognized. Once firmware has been loaded the device will * reset the USB port and re-attach with the original product ID+1 * and this driver will be attached. The firmware is licensed for * general use (royalty free) and may be incorporated in products. * Note that the firmware normally packaged with the NDIS drivers * for these devices does not work in this way and so does not work * with this driver. */ #include "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 #ifdef INET #include #include #include #include #include #endif #include #include #include #include #include #include #include "usbdevs.h" #include #include static SYSCTL_NODE(_hw_usb, OID_AUTO, uath, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB Atheros"); static int uath_countrycode = CTRY_DEFAULT; /* country code */ SYSCTL_INT(_hw_usb_uath, OID_AUTO, countrycode, CTLFLAG_RWTUN, &uath_countrycode, 0, "country code"); static int uath_regdomain = 0; /* regulatory domain */ SYSCTL_INT(_hw_usb_uath, OID_AUTO, regdomain, CTLFLAG_RD, &uath_regdomain, 0, "regulatory domain"); #ifdef UATH_DEBUG int uath_debug = 0; SYSCTL_INT(_hw_usb_uath, OID_AUTO, debug, CTLFLAG_RWTUN, &uath_debug, 0, "uath debug level"); enum { UATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ UATH_DEBUG_XMIT_DUMP = 0x00000002, /* xmit dump */ UATH_DEBUG_RECV = 0x00000004, /* basic recv operation */ UATH_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */ UATH_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */ UATH_DEBUG_RECV_ALL = 0x00000020, /* trace all frames (beacons) */ UATH_DEBUG_INIT = 0x00000040, /* initialization of dev */ UATH_DEBUG_DEVCAP = 0x00000080, /* dev caps */ UATH_DEBUG_CMDS = 0x00000100, /* commands */ UATH_DEBUG_CMDS_DUMP = 0x00000200, /* command buffer dump */ UATH_DEBUG_RESET = 0x00000400, /* reset processing */ UATH_DEBUG_STATE = 0x00000800, /* 802.11 state transitions */ UATH_DEBUG_MULTICAST = 0x00001000, /* multicast */ UATH_DEBUG_WME = 0x00002000, /* WME */ UATH_DEBUG_CHANNEL = 0x00004000, /* channel */ UATH_DEBUG_RATES = 0x00008000, /* rates */ UATH_DEBUG_CRYPTO = 0x00010000, /* crypto */ UATH_DEBUG_LED = 0x00020000, /* LED */ UATH_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) #endif /* recognized device vendors/products */ static const STRUCT_USB_HOST_ID uath_devs[] = { #define UATH_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } UATH_DEV(ACCTON, SMCWUSBTG2), UATH_DEV(ATHEROS, AR5523), UATH_DEV(ATHEROS2, AR5523_1), UATH_DEV(ATHEROS2, AR5523_2), UATH_DEV(ATHEROS2, AR5523_3), UATH_DEV(CONCEPTRONIC, AR5523_1), UATH_DEV(CONCEPTRONIC, AR5523_2), UATH_DEV(DLINK, DWLAG122), UATH_DEV(DLINK, DWLAG132), UATH_DEV(DLINK, DWLG132), UATH_DEV(DLINK2, DWA120), UATH_DEV(GIGASET, AR5523), UATH_DEV(GIGASET, SMCWUSBTG), UATH_DEV(GLOBALSUN, AR5523_1), UATH_DEV(GLOBALSUN, AR5523_2), UATH_DEV(NETGEAR, WG111U), UATH_DEV(NETGEAR3, WG111T), UATH_DEV(NETGEAR3, WPN111), UATH_DEV(NETGEAR3, WPN111_2), UATH_DEV(UMEDIA, TEW444UBEU), UATH_DEV(UMEDIA, AR5523_2), UATH_DEV(WISTRONNEWEB, AR5523_1), UATH_DEV(WISTRONNEWEB, AR5523_2), UATH_DEV(WISTRONNEWEB, AR5523_2_ALT), UATH_DEV(ZCOM, AR5523) #undef UATH_DEV }; static usb_callback_t uath_intr_rx_callback; static usb_callback_t uath_intr_tx_callback; static usb_callback_t uath_bulk_rx_callback; static usb_callback_t uath_bulk_tx_callback; static const struct usb_config uath_usbconfig[UATH_N_XFERS] = { [UATH_INTR_RX] = { .type = UE_BULK, .endpoint = 0x1, .direction = UE_DIR_IN, .bufsize = UATH_MAX_CMDSZ, .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = uath_intr_rx_callback }, [UATH_INTR_TX] = { .type = UE_BULK, .endpoint = 0x1, .direction = UE_DIR_OUT, .bufsize = UATH_MAX_CMDSZ * UATH_CMD_LIST_COUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1, }, .callback = uath_intr_tx_callback, .timeout = UATH_CMD_TIMEOUT }, [UATH_BULK_RX] = { .type = UE_BULK, .endpoint = 0x2, .direction = UE_DIR_IN, .bufsize = MCLBYTES, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = uath_bulk_rx_callback }, [UATH_BULK_TX] = { .type = UE_BULK, .endpoint = 0x2, .direction = UE_DIR_OUT, .bufsize = UATH_MAX_TXBUFSZ * UATH_TX_DATA_LIST_COUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1 }, .callback = uath_bulk_tx_callback, .timeout = UATH_DATA_TIMEOUT } }; static struct ieee80211vap *uath_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void uath_vap_delete(struct ieee80211vap *); static int uath_alloc_cmd_list(struct uath_softc *, struct uath_cmd []); static void uath_free_cmd_list(struct uath_softc *, struct uath_cmd []); static int uath_host_available(struct uath_softc *); static int uath_get_capability(struct uath_softc *, uint32_t, uint32_t *); static int uath_get_devcap(struct uath_softc *); static struct uath_cmd * uath_get_cmdbuf(struct uath_softc *); static int uath_cmd_read(struct uath_softc *, uint32_t, const void *, int, void *, int, int); static int uath_cmd_write(struct uath_softc *, uint32_t, const void *, int, int); static void uath_stat(void *); #ifdef UATH_DEBUG static void uath_dump_cmd(const uint8_t *, int, char); static const char * uath_codename(int); #endif static int uath_get_devstatus(struct uath_softc *, uint8_t macaddr[IEEE80211_ADDR_LEN]); static int uath_get_status(struct uath_softc *, uint32_t, void *, int); static int uath_alloc_rx_data_list(struct uath_softc *); static int uath_alloc_tx_data_list(struct uath_softc *); static void uath_free_rx_data_list(struct uath_softc *); static void uath_free_tx_data_list(struct uath_softc *); static int uath_init(struct uath_softc *); static void uath_stop(struct uath_softc *); static void uath_parent(struct ieee80211com *); static int uath_transmit(struct ieee80211com *, struct mbuf *); static void uath_start(struct uath_softc *); static int uath_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void uath_scan_start(struct ieee80211com *); static void uath_scan_end(struct ieee80211com *); static void uath_set_channel(struct ieee80211com *); static void uath_update_mcast(struct ieee80211com *); static void uath_update_promisc(struct ieee80211com *); static int uath_config(struct uath_softc *, uint32_t, uint32_t); static int uath_config_multi(struct uath_softc *, uint32_t, const void *, int); static int uath_switch_channel(struct uath_softc *, struct ieee80211_channel *); static int uath_set_rxfilter(struct uath_softc *, uint32_t, uint32_t); static void uath_watchdog(void *); static void uath_abort_xfers(struct uath_softc *); static int uath_dataflush(struct uath_softc *); static int uath_cmdflush(struct uath_softc *); static int uath_flush(struct uath_softc *); static int uath_set_ledstate(struct uath_softc *, int); static int uath_set_chan(struct uath_softc *, struct ieee80211_channel *); static int uath_reset_tx_queues(struct uath_softc *); static int uath_wme_init(struct uath_softc *); static struct uath_data * uath_getbuf(struct uath_softc *); static int uath_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int uath_set_key(struct uath_softc *, const struct ieee80211_key *, int); static int uath_set_keys(struct uath_softc *, struct ieee80211vap *); static void uath_sysctl_node(struct uath_softc *); static int uath_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != UATH_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != UATH_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(uath_devs, sizeof(uath_devs), uaa)); } static int uath_attach(device_t dev) { struct uath_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct ieee80211com *ic = &sc->sc_ic; uint8_t bands[IEEE80211_MODE_BYTES]; uint8_t iface_index = UATH_IFACE_INDEX; /* XXX */ usb_error_t error; sc->sc_dev = dev; sc->sc_udev = uaa->device; #ifdef UATH_DEBUG sc->sc_debug = uath_debug; #endif device_set_usb_desc(dev); /* * Only post-firmware devices here. */ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init(&sc->stat_ch, 0); callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); mbufq_init(&sc->sc_snd, ifqmaxlen); error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, uath_usbconfig, UATH_N_XFERS, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); goto fail; } sc->sc_cmd_dma_buf = usbd_xfer_get_frame_buffer(sc->sc_xfer[UATH_INTR_TX], 0); sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer(sc->sc_xfer[UATH_BULK_TX], 0); /* * Setup buffers for firmware commands. */ error = uath_alloc_cmd_list(sc, sc->sc_cmd); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx command list\n"); goto fail1; } /* * We're now ready to send+receive firmware commands. */ UATH_LOCK(sc); error = uath_host_available(sc); if (error != 0) { device_printf(sc->sc_dev, "could not initialize adapter\n"); goto fail2; } error = uath_get_devcap(sc); if (error != 0) { device_printf(sc->sc_dev, "could not get device capabilities\n"); goto fail2; } UATH_UNLOCK(sc); /* Create device sysctl node. */ uath_sysctl_node(sc); UATH_LOCK(sc); error = uath_get_devstatus(sc, ic->ic_macaddr); if (error != 0) { device_printf(sc->sc_dev, "could not get device status\n"); goto fail2; } /* * Allocate xfers for Rx/Tx data pipes. */ error = uath_alloc_rx_data_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Rx data list\n"); goto fail2; } error = uath_alloc_tx_data_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx data list\n"); goto fail2; } UATH_UNLOCK(sc); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(dev); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA | /* station mode */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_WPA | /* 802.11i */ IEEE80211_C_BGSCAN | /* capable of bg scanning */ IEEE80211_C_TXFRAG; /* handle tx frags */ /* put a regulatory domain to reveal informations. */ uath_regdomain = sc->sc_devcap.regDomain; + ic->ic_flags_ext |= IEEE80211_FEXT_SEQNO_OFFLOAD; + memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); if ((sc->sc_devcap.analog5GhzRevision & 0xf0) == 0x30) setbit(bands, IEEE80211_MODE_11A); /* XXX turbo */ ieee80211_init_channels(ic, NULL, bands); ieee80211_ifattach(ic); /* Note: this has to happen AFTER ieee80211_ifattach() */ ieee80211_set_software_ciphers(ic, IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_AES_CCM | IEEE80211_CRYPTO_AES_GCM_128); ic->ic_raw_xmit = uath_raw_xmit; ic->ic_scan_start = uath_scan_start; ic->ic_scan_end = uath_scan_end; ic->ic_set_channel = uath_set_channel; ic->ic_vap_create = uath_vap_create; ic->ic_vap_delete = uath_vap_delete; ic->ic_update_mcast = uath_update_mcast; ic->ic_update_promisc = uath_update_promisc; ic->ic_transmit = uath_transmit; ic->ic_parent = uath_parent; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), UATH_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), UATH_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return (0); fail2: UATH_UNLOCK(sc); uath_free_cmd_list(sc, sc->sc_cmd); fail1: usbd_transfer_unsetup(sc->sc_xfer, UATH_N_XFERS); fail: return (error); } static int uath_detach(device_t dev) { struct uath_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; unsigned x; /* * Prevent further allocations from RX/TX/CMD * data lists and ioctls */ UATH_LOCK(sc); sc->sc_flags |= UATH_FLAG_INVALID; STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); STAILQ_INIT(&sc->sc_cmd_active); STAILQ_INIT(&sc->sc_cmd_pending); STAILQ_INIT(&sc->sc_cmd_waiting); STAILQ_INIT(&sc->sc_cmd_inactive); uath_stop(sc); UATH_UNLOCK(sc); callout_drain(&sc->stat_ch); callout_drain(&sc->watchdog_ch); /* drain USB transfers */ for (x = 0; x != UATH_N_XFERS; x++) usbd_transfer_drain(sc->sc_xfer[x]); /* free data buffers */ UATH_LOCK(sc); uath_free_rx_data_list(sc); uath_free_tx_data_list(sc); uath_free_cmd_list(sc, sc->sc_cmd); UATH_UNLOCK(sc); /* free USB transfers and some data buffers */ usbd_transfer_unsetup(sc->sc_xfer, UATH_N_XFERS); ieee80211_ifdetach(ic); mbufq_drain(&sc->sc_snd); mtx_destroy(&sc->sc_mtx); return (0); } static void uath_free_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[]) { int i; for (i = 0; i != UATH_CMD_LIST_COUNT; i++) cmds[i].buf = NULL; } static int uath_alloc_cmd_list(struct uath_softc *sc, struct uath_cmd cmds[]) { int i; STAILQ_INIT(&sc->sc_cmd_active); STAILQ_INIT(&sc->sc_cmd_pending); STAILQ_INIT(&sc->sc_cmd_waiting); STAILQ_INIT(&sc->sc_cmd_inactive); for (i = 0; i != UATH_CMD_LIST_COUNT; i++) { struct uath_cmd *cmd = &cmds[i]; cmd->sc = sc; /* backpointer for callbacks */ cmd->msgid = i; cmd->buf = ((uint8_t *)sc->sc_cmd_dma_buf) + (i * UATH_MAX_CMDSZ); STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next); UATH_STAT_INC(sc, st_cmd_inactive); } return (0); } static int uath_host_available(struct uath_softc *sc) { struct uath_cmd_host_available setup; UATH_ASSERT_LOCKED(sc); /* inform target the host is available */ setup.sw_ver_major = htobe32(ATH_SW_VER_MAJOR); setup.sw_ver_minor = htobe32(ATH_SW_VER_MINOR); setup.sw_ver_patch = htobe32(ATH_SW_VER_PATCH); setup.sw_ver_build = htobe32(ATH_SW_VER_BUILD); return uath_cmd_read(sc, WDCMSG_HOST_AVAILABLE, &setup, sizeof setup, NULL, 0, 0); } #ifdef UATH_DEBUG static void uath_dump_cmd(const uint8_t *buf, int len, char prefix) { const char *sep = ""; int i; for (i = 0; i < len; i++) { if ((i % 16) == 0) { printf("%s%c ", sep, prefix); sep = "\n"; } else if ((i % 4) == 0) printf(" "); printf("%02x", buf[i]); } printf("\n"); } static const char * uath_codename(int code) { static const char *names[] = { "0x00", "HOST_AVAILABLE", "BIND", "TARGET_RESET", "TARGET_GET_CAPABILITY", "TARGET_SET_CONFIG", "TARGET_GET_STATUS", "TARGET_GET_STATS", "TARGET_START", "TARGET_STOP", "TARGET_ENABLE", "TARGET_DISABLE", "CREATE_CONNECTION", "UPDATE_CONNECT_ATTR", "DELETE_CONNECT", "SEND", "FLUSH", "STATS_UPDATE", "BMISS", "DEVICE_AVAIL", "SEND_COMPLETE", "DATA_AVAIL", "SET_PWR_MODE", "BMISS_ACK", "SET_LED_STEADY", "SET_LED_BLINK", "SETUP_BEACON_DESC", "BEACON_INIT", "RESET_KEY_CACHE", "RESET_KEY_CACHE_ENTRY", "SET_KEY_CACHE_ENTRY", "SET_DECOMP_MASK", "SET_REGULATORY_DOMAIN", "SET_LED_STATE", "WRITE_ASSOCID", "SET_STA_BEACON_TIMERS", "GET_TSF", "RESET_TSF", "SET_ADHOC_MODE", "SET_BASIC_RATE", "MIB_CONTROL", "GET_CHANNEL_DATA", "GET_CUR_RSSI", "SET_ANTENNA_SWITCH", "0x2c", "0x2d", "0x2e", "USE_SHORT_SLOT_TIME", "SET_POWER_MODE", "SETUP_PSPOLL_DESC", "SET_RX_MULTICAST_FILTER", "RX_FILTER", "PER_CALIBRATION", "RESET", "DISABLE", "PHY_DISABLE", "SET_TX_POWER_LIMIT", "SET_TX_QUEUE_PARAMS", "SETUP_TX_QUEUE", "RELEASE_TX_QUEUE", }; static char buf[8]; if (code < nitems(names)) return names[code]; if (code == WDCMSG_SET_DEFAULT_KEY) return "SET_DEFAULT_KEY"; snprintf(buf, sizeof(buf), "0x%02x", code); return buf; } #endif /* * Low-level function to send read or write commands to the firmware. */ static int uath_cmdsend(struct uath_softc *sc, uint32_t code, const void *idata, int ilen, void *odata, int olen, int flags) { struct uath_cmd_hdr *hdr; struct uath_cmd *cmd; int error; UATH_ASSERT_LOCKED(sc); /* grab a xfer */ cmd = uath_get_cmdbuf(sc); if (cmd == NULL) { device_printf(sc->sc_dev, "%s: empty inactive queue\n", __func__); return (ENOBUFS); } cmd->flags = flags; /* always bulk-out a multiple of 4 bytes */ cmd->buflen = roundup2(sizeof(struct uath_cmd_hdr) + ilen, 4); hdr = (struct uath_cmd_hdr *)cmd->buf; memset(hdr, 0, sizeof(struct uath_cmd_hdr)); hdr->len = htobe32(cmd->buflen); hdr->code = htobe32(code); hdr->msgid = cmd->msgid; /* don't care about endianness */ hdr->magic = htobe32((cmd->flags & UATH_CMD_FLAG_MAGIC) ? 1 << 24 : 0); memcpy((uint8_t *)(hdr + 1), idata, ilen); #ifdef UATH_DEBUG if (sc->sc_debug & UATH_DEBUG_CMDS) { printf("%s: send %s [flags 0x%x] olen %d\n", __func__, uath_codename(code), cmd->flags, olen); if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) uath_dump_cmd(cmd->buf, cmd->buflen, '+'); } #endif cmd->odata = odata; KASSERT(odata == NULL || olen < UATH_MAX_CMDSZ - sizeof(*hdr) + sizeof(uint32_t), ("odata %p olen %u", odata, olen)); cmd->olen = olen; STAILQ_INSERT_TAIL(&sc->sc_cmd_pending, cmd, next); UATH_STAT_INC(sc, st_cmd_pending); usbd_transfer_start(sc->sc_xfer[UATH_INTR_TX]); if (cmd->flags & UATH_CMD_FLAG_READ) { usbd_transfer_start(sc->sc_xfer[UATH_INTR_RX]); /* wait at most two seconds for command reply */ error = mtx_sleep(cmd, &sc->sc_mtx, 0, "uathcmd", 2 * hz); cmd->odata = NULL; /* in case reply comes too late */ if (error != 0) { device_printf(sc->sc_dev, "timeout waiting for reply " "to cmd 0x%x (%u)\n", code, code); } else if (cmd->olen != olen) { device_printf(sc->sc_dev, "unexpected reply data count " "to cmd 0x%x (%u), got %u, expected %u\n", code, code, cmd->olen, olen); error = EINVAL; } return (error); } return (0); } static int uath_cmd_read(struct uath_softc *sc, uint32_t code, const void *idata, int ilen, void *odata, int olen, int flags) { flags |= UATH_CMD_FLAG_READ; return uath_cmdsend(sc, code, idata, ilen, odata, olen, flags); } static int uath_cmd_write(struct uath_softc *sc, uint32_t code, const void *data, int len, int flags) { flags &= ~UATH_CMD_FLAG_READ; return uath_cmdsend(sc, code, data, len, NULL, 0, flags); } static struct uath_cmd * uath_get_cmdbuf(struct uath_softc *sc) { struct uath_cmd *uc; UATH_ASSERT_LOCKED(sc); uc = STAILQ_FIRST(&sc->sc_cmd_inactive); if (uc != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_cmd_inactive, next); UATH_STAT_DEC(sc, st_cmd_inactive); } else uc = NULL; if (uc == NULL) DPRINTF(sc, UATH_DEBUG_XMIT, "%s: %s\n", __func__, "out of command xmit buffers"); return (uc); } /* * This function is called periodically (every second) when associated to * query device statistics. */ static void uath_stat(void *arg) { struct uath_softc *sc = arg; int error; UATH_LOCK(sc); /* * Send request for statistics asynchronously. The timer will be * restarted when we'll get the stats notification. */ error = uath_cmd_write(sc, WDCMSG_TARGET_GET_STATS, NULL, 0, UATH_CMD_FLAG_ASYNC); if (error != 0) { device_printf(sc->sc_dev, "could not query stats, error %d\n", error); } UATH_UNLOCK(sc); } static int uath_get_capability(struct uath_softc *sc, uint32_t cap, uint32_t *val) { int error; cap = htobe32(cap); error = uath_cmd_read(sc, WDCMSG_TARGET_GET_CAPABILITY, &cap, sizeof cap, val, sizeof(uint32_t), UATH_CMD_FLAG_MAGIC); if (error != 0) { device_printf(sc->sc_dev, "could not read capability %u\n", be32toh(cap)); return (error); } *val = be32toh(*val); return (error); } static int uath_get_devcap(struct uath_softc *sc) { #define GETCAP(x, v) do { \ error = uath_get_capability(sc, x, &v); \ if (error != 0) \ return (error); \ DPRINTF(sc, UATH_DEBUG_DEVCAP, \ "%s: %s=0x%08x\n", __func__, #x, v); \ } while (0) struct uath_devcap *cap = &sc->sc_devcap; int error; /* collect device capabilities */ GETCAP(CAP_TARGET_VERSION, cap->targetVersion); GETCAP(CAP_TARGET_REVISION, cap->targetRevision); GETCAP(CAP_MAC_VERSION, cap->macVersion); GETCAP(CAP_MAC_REVISION, cap->macRevision); GETCAP(CAP_PHY_REVISION, cap->phyRevision); GETCAP(CAP_ANALOG_5GHz_REVISION, cap->analog5GhzRevision); GETCAP(CAP_ANALOG_2GHz_REVISION, cap->analog2GhzRevision); GETCAP(CAP_REG_DOMAIN, cap->regDomain); GETCAP(CAP_REG_CAP_BITS, cap->regCapBits); #if 0 /* NB: not supported in rev 1.5 */ GETCAP(CAP_COUNTRY_CODE, cap->countryCode); #endif GETCAP(CAP_WIRELESS_MODES, cap->wirelessModes); GETCAP(CAP_CHAN_SPREAD_SUPPORT, cap->chanSpreadSupport); GETCAP(CAP_COMPRESS_SUPPORT, cap->compressSupport); GETCAP(CAP_BURST_SUPPORT, cap->burstSupport); GETCAP(CAP_FAST_FRAMES_SUPPORT, cap->fastFramesSupport); GETCAP(CAP_CHAP_TUNING_SUPPORT, cap->chapTuningSupport); GETCAP(CAP_TURBOG_SUPPORT, cap->turboGSupport); GETCAP(CAP_TURBO_PRIME_SUPPORT, cap->turboPrimeSupport); GETCAP(CAP_DEVICE_TYPE, cap->deviceType); GETCAP(CAP_WME_SUPPORT, cap->wmeSupport); GETCAP(CAP_TOTAL_QUEUES, cap->numTxQueues); GETCAP(CAP_CONNECTION_ID_MAX, cap->connectionIdMax); GETCAP(CAP_LOW_5GHZ_CHAN, cap->low5GhzChan); GETCAP(CAP_HIGH_5GHZ_CHAN, cap->high5GhzChan); GETCAP(CAP_LOW_2GHZ_CHAN, cap->low2GhzChan); GETCAP(CAP_HIGH_2GHZ_CHAN, cap->high2GhzChan); GETCAP(CAP_TWICE_ANTENNAGAIN_5G, cap->twiceAntennaGain5G); GETCAP(CAP_TWICE_ANTENNAGAIN_2G, cap->twiceAntennaGain2G); GETCAP(CAP_CIPHER_AES_CCM, cap->supportCipherAES_CCM); GETCAP(CAP_CIPHER_TKIP, cap->supportCipherTKIP); GETCAP(CAP_MIC_TKIP, cap->supportMicTKIP); cap->supportCipherWEP = 1; /* NB: always available */ return (0); } static int uath_get_devstatus(struct uath_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) { int error; /* retrieve MAC address */ error = uath_get_status(sc, ST_MAC_ADDR, macaddr, IEEE80211_ADDR_LEN); if (error != 0) { device_printf(sc->sc_dev, "could not read MAC address\n"); return (error); } error = uath_get_status(sc, ST_SERIAL_NUMBER, &sc->sc_serial[0], sizeof(sc->sc_serial)); if (error != 0) { device_printf(sc->sc_dev, "could not read device serial number\n"); return (error); } return (0); } static int uath_get_status(struct uath_softc *sc, uint32_t which, void *odata, int olen) { int error; which = htobe32(which); error = uath_cmd_read(sc, WDCMSG_TARGET_GET_STATUS, &which, sizeof(which), odata, olen, UATH_CMD_FLAG_MAGIC); if (error != 0) device_printf(sc->sc_dev, "could not read EEPROM offset 0x%02x\n", be32toh(which)); return (error); } static void uath_free_data_list(struct uath_softc *sc, struct uath_data data[], int ndata, int fillmbuf) { int i; for (i = 0; i < ndata; i++) { struct uath_data *dp = &data[i]; if (fillmbuf == 1) { if (dp->m != NULL) { m_freem(dp->m); dp->m = NULL; dp->buf = NULL; } } else { dp->buf = NULL; } if (dp->ni != NULL) { ieee80211_free_node(dp->ni); dp->ni = NULL; } } } static int uath_alloc_data_list(struct uath_softc *sc, struct uath_data data[], int ndata, int maxsz, void *dma_buf) { int i, error; for (i = 0; i < ndata; i++) { struct uath_data *dp = &data[i]; dp->sc = sc; if (dma_buf == NULL) { /* XXX check maxsz */ dp->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (dp->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } dp->buf = mtod(dp->m, uint8_t *); } else { dp->m = NULL; dp->buf = ((uint8_t *)dma_buf) + (i * maxsz); } dp->ni = NULL; } return (0); fail: uath_free_data_list(sc, data, ndata, 1 /* free mbufs */); return (error); } static int uath_alloc_rx_data_list(struct uath_softc *sc) { int error, i; /* XXX is it enough to store the RX packet with MCLBYTES bytes? */ error = uath_alloc_data_list(sc, sc->sc_rx, UATH_RX_DATA_LIST_COUNT, MCLBYTES, NULL /* setup mbufs */); if (error != 0) return (error); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) { STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); UATH_STAT_INC(sc, st_rx_inactive); } return (0); } static int uath_alloc_tx_data_list(struct uath_softc *sc) { int error, i; error = uath_alloc_data_list(sc, sc->sc_tx, UATH_TX_DATA_LIST_COUNT, UATH_MAX_TXBUFSZ, sc->sc_tx_dma_buf); if (error != 0) return (error); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); for (i = 0; i < UATH_TX_DATA_LIST_COUNT; i++) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); UATH_STAT_INC(sc, st_tx_inactive); } return (0); } static void uath_free_rx_data_list(struct uath_softc *sc) { uath_free_data_list(sc, sc->sc_rx, UATH_RX_DATA_LIST_COUNT, 1 /* free mbufs */); } static void uath_free_tx_data_list(struct uath_softc *sc) { uath_free_data_list(sc, sc->sc_tx, UATH_TX_DATA_LIST_COUNT, 0 /* no mbufs */); } static struct ieee80211vap * uath_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct uath_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); uvp = malloc(sizeof(struct uath_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = uath_newstate; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void uath_vap_delete(struct ieee80211vap *vap) { struct uath_vap *uvp = UATH_VAP(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static int uath_init(struct uath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t val; int error; UATH_ASSERT_LOCKED(sc); if (sc->sc_flags & UATH_FLAG_INITDONE) uath_stop(sc); /* reset variables */ sc->sc_intrx_nextnum = sc->sc_msgid = 0; val = htobe32(0); uath_cmd_write(sc, WDCMSG_BIND, &val, sizeof val, 0); /* set MAC address */ uath_config_multi(sc, CFG_MAC_ADDR, vap ? vap->iv_myaddr : ic->ic_macaddr, IEEE80211_ADDR_LEN); /* XXX honor net80211 state */ uath_config(sc, CFG_RATE_CONTROL_ENABLE, 0x00000001); uath_config(sc, CFG_DIVERSITY_CTL, 0x00000001); uath_config(sc, CFG_ABOLT, 0x0000003f); uath_config(sc, CFG_WME_ENABLED, 0x00000001); uath_config(sc, CFG_SERVICE_TYPE, 1); uath_config(sc, CFG_TP_SCALE, 0x00000000); uath_config(sc, CFG_TPC_HALF_DBM5, 0x0000003c); uath_config(sc, CFG_TPC_HALF_DBM2, 0x0000003c); uath_config(sc, CFG_OVERRD_TX_POWER, 0x00000000); uath_config(sc, CFG_GMODE_PROTECTION, 0x00000000); uath_config(sc, CFG_GMODE_PROTECT_RATE_INDEX, 0x00000003); uath_config(sc, CFG_PROTECTION_TYPE, 0x00000000); uath_config(sc, CFG_MODE_CTS, 0x00000002); error = uath_cmd_read(sc, WDCMSG_TARGET_START, NULL, 0, &val, sizeof(val), UATH_CMD_FLAG_MAGIC); if (error) { device_printf(sc->sc_dev, "could not start target, error %d\n", error); goto fail; } DPRINTF(sc, UATH_DEBUG_INIT, "%s returns handle: 0x%x\n", uath_codename(WDCMSG_TARGET_START), be32toh(val)); /* set default channel */ error = uath_switch_channel(sc, ic->ic_curchan); if (error) { device_printf(sc->sc_dev, "could not switch channel, error %d\n", error); goto fail; } val = htobe32(TARGET_DEVICE_AWAKE); uath_cmd_write(sc, WDCMSG_SET_PWR_MODE, &val, sizeof val, 0); /* XXX? check */ uath_cmd_write(sc, WDCMSG_RESET_KEY_CACHE, NULL, 0, 0); usbd_transfer_start(sc->sc_xfer[UATH_BULK_RX]); /* enable Rx */ uath_set_rxfilter(sc, 0x0, UATH_FILTER_OP_INIT); uath_set_rxfilter(sc, UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST | UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON, UATH_FILTER_OP_SET); sc->sc_flags |= UATH_FLAG_INITDONE; callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc); return (0); fail: uath_stop(sc); return (error); } static void uath_stop(struct uath_softc *sc) { UATH_ASSERT_LOCKED(sc); sc->sc_flags &= ~UATH_FLAG_INITDONE; callout_stop(&sc->stat_ch); callout_stop(&sc->watchdog_ch); sc->sc_tx_timer = 0; /* abort pending transmits */ uath_abort_xfers(sc); /* flush data & control requests into the target */ (void)uath_flush(sc); /* set a LED status to the disconnected. */ uath_set_ledstate(sc, 0); /* stop the target */ uath_cmd_write(sc, WDCMSG_TARGET_STOP, NULL, 0, 0); } static int uath_config(struct uath_softc *sc, uint32_t reg, uint32_t val) { struct uath_write_mac write; int error; write.reg = htobe32(reg); write.len = htobe32(0); /* 0 = single write */ *(uint32_t *)write.data = htobe32(val); error = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write, 3 * sizeof (uint32_t), 0); if (error != 0) { device_printf(sc->sc_dev, "could not write register 0x%02x\n", reg); } return (error); } static int uath_config_multi(struct uath_softc *sc, uint32_t reg, const void *data, int len) { struct uath_write_mac write; int error; write.reg = htobe32(reg); write.len = htobe32(len); bcopy(data, write.data, len); /* properly handle the case where len is zero (reset) */ error = uath_cmd_write(sc, WDCMSG_TARGET_SET_CONFIG, &write, (len == 0) ? sizeof (uint32_t) : 2 * sizeof (uint32_t) + len, 0); if (error != 0) { device_printf(sc->sc_dev, "could not write %d bytes to register 0x%02x\n", len, reg); } return (error); } static int uath_switch_channel(struct uath_softc *sc, struct ieee80211_channel *c) { int error; UATH_ASSERT_LOCKED(sc); /* set radio frequency */ error = uath_set_chan(sc, c); if (error) { device_printf(sc->sc_dev, "could not set channel, error %d\n", error); goto failed; } /* reset Tx rings */ error = uath_reset_tx_queues(sc); if (error) { device_printf(sc->sc_dev, "could not reset Tx queues, error %d\n", error); goto failed; } /* set Tx rings WME properties */ error = uath_wme_init(sc); if (error) { device_printf(sc->sc_dev, "could not init Tx queues, error %d\n", error); goto failed; } error = uath_set_ledstate(sc, 0); if (error) { device_printf(sc->sc_dev, "could not set led state, error %d\n", error); goto failed; } error = uath_flush(sc); if (error) { device_printf(sc->sc_dev, "could not flush pipes, error %d\n", error); goto failed; } failed: return (error); } static int uath_set_rxfilter(struct uath_softc *sc, uint32_t bits, uint32_t op) { struct uath_cmd_rx_filter rxfilter; rxfilter.bits = htobe32(bits); rxfilter.op = htobe32(op); DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "setting Rx filter=0x%x flags=0x%x\n", bits, op); return uath_cmd_write(sc, WDCMSG_RX_FILTER, &rxfilter, sizeof rxfilter, 0); } static void uath_watchdog(void *arg) { struct uath_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); counter_u64_add(ic->ic_oerrors, 1); ieee80211_restart_all(ic); return; } callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc); } } static void uath_abort_xfers(struct uath_softc *sc) { int i; UATH_ASSERT_LOCKED(sc); /* abort any pending transfers */ for (i = 0; i < UATH_N_XFERS; i++) usbd_transfer_stop(sc->sc_xfer[i]); } static int uath_flush(struct uath_softc *sc) { int error; error = uath_dataflush(sc); if (error != 0) goto failed; error = uath_cmdflush(sc); if (error != 0) goto failed; failed: return (error); } static int uath_cmdflush(struct uath_softc *sc) { return uath_cmd_write(sc, WDCMSG_FLUSH, NULL, 0, 0); } static int uath_dataflush(struct uath_softc *sc) { struct uath_data *data; struct uath_chunk *chunk; struct uath_tx_desc *desc; UATH_ASSERT_LOCKED(sc); data = uath_getbuf(sc); if (data == NULL) return (ENOBUFS); data->buflen = sizeof(struct uath_chunk) + sizeof(struct uath_tx_desc); data->m = NULL; data->ni = NULL; chunk = (struct uath_chunk *)data->buf; desc = (struct uath_tx_desc *)(chunk + 1); /* one chunk only */ chunk->seqnum = 0; chunk->flags = UATH_CFLAGS_FINAL; chunk->length = htobe16(sizeof (struct uath_tx_desc)); memset(desc, 0, sizeof(struct uath_tx_desc)); desc->msglen = htobe32(sizeof(struct uath_tx_desc)); desc->msgid = (sc->sc_msgid++) + 1; /* don't care about endianness */ desc->type = htobe32(WDCMSG_FLUSH); desc->txqid = htobe32(0); desc->connid = htobe32(0); desc->flags = htobe32(0); #ifdef UATH_DEBUG if (sc->sc_debug & UATH_DEBUG_CMDS) { DPRINTF(sc, UATH_DEBUG_RESET, "send flush ix %d\n", desc->msgid); if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) uath_dump_cmd(data->buf, data->buflen, '+'); } #endif STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); UATH_STAT_INC(sc, st_tx_pending); sc->sc_tx_timer = 5; usbd_transfer_start(sc->sc_xfer[UATH_BULK_TX]); return (0); } static struct uath_data * _uath_getbuf(struct uath_softc *sc) { struct uath_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); UATH_STAT_DEC(sc, st_tx_inactive); } else bf = NULL; if (bf == NULL) DPRINTF(sc, UATH_DEBUG_XMIT, "%s: %s\n", __func__, "out of xmit buffers"); return (bf); } static struct uath_data * uath_getbuf(struct uath_softc *sc) { struct uath_data *bf; UATH_ASSERT_LOCKED(sc); bf = _uath_getbuf(sc); if (bf == NULL) DPRINTF(sc, UATH_DEBUG_XMIT, "%s: stop queue\n", __func__); return (bf); } static int uath_set_ledstate(struct uath_softc *sc, int connected) { DPRINTF(sc, UATH_DEBUG_LED, "set led state %sconnected\n", connected ? "" : "!"); connected = htobe32(connected); return uath_cmd_write(sc, WDCMSG_SET_LED_STATE, &connected, sizeof connected, 0); } static int uath_set_chan(struct uath_softc *sc, struct ieee80211_channel *c) { #ifdef UATH_DEBUG struct ieee80211com *ic = &sc->sc_ic; #endif struct uath_cmd_reset reset; memset(&reset, 0, sizeof(reset)); if (IEEE80211_IS_CHAN_2GHZ(c)) reset.flags |= htobe32(UATH_CHAN_2GHZ); if (IEEE80211_IS_CHAN_5GHZ(c)) reset.flags |= htobe32(UATH_CHAN_5GHZ); /* NB: 11g =>'s 11b so don't specify both OFDM and CCK */ if (IEEE80211_IS_CHAN_OFDM(c)) reset.flags |= htobe32(UATH_CHAN_OFDM); else if (IEEE80211_IS_CHAN_CCK(c)) reset.flags |= htobe32(UATH_CHAN_CCK); /* turbo can be used in either 2GHz or 5GHz */ if (c->ic_flags & IEEE80211_CHAN_TURBO) reset.flags |= htobe32(UATH_CHAN_TURBO); reset.freq = htobe32(c->ic_freq); reset.maxrdpower = htobe32(50); /* XXX */ reset.channelchange = htobe32(1); reset.keeprccontent = htobe32(0); DPRINTF(sc, UATH_DEBUG_CHANNEL, "set channel %d, flags 0x%x freq %u\n", ieee80211_chan2ieee(ic, c), be32toh(reset.flags), be32toh(reset.freq)); return uath_cmd_write(sc, WDCMSG_RESET, &reset, sizeof reset, 0); } static int uath_reset_tx_queues(struct uath_softc *sc) { int ac, error; DPRINTF(sc, UATH_DEBUG_RESET, "%s: reset Tx queues\n", __func__); for (ac = 0; ac < 4; ac++) { const uint32_t qid = htobe32(ac); error = uath_cmd_write(sc, WDCMSG_RELEASE_TX_QUEUE, &qid, sizeof qid, 0); if (error != 0) break; } return (error); } static int uath_wme_init(struct uath_softc *sc) { /* XXX get from net80211 */ static const struct uath_wme_settings uath_wme_11g[4] = { { 7, 4, 10, 0, 0 }, /* Background */ { 3, 4, 10, 0, 0 }, /* Best-Effort */ { 3, 3, 4, 26, 0 }, /* Video */ { 2, 2, 3, 47, 0 } /* Voice */ }; struct uath_cmd_txq_setup qinfo; int ac, error; DPRINTF(sc, UATH_DEBUG_WME, "%s: setup Tx queues\n", __func__); for (ac = 0; ac < 4; ac++) { qinfo.qid = htobe32(ac); qinfo.len = htobe32(sizeof(qinfo.attr)); qinfo.attr.priority = htobe32(ac); /* XXX */ qinfo.attr.aifs = htobe32(uath_wme_11g[ac].aifsn); qinfo.attr.logcwmin = htobe32(uath_wme_11g[ac].logcwmin); qinfo.attr.logcwmax = htobe32(uath_wme_11g[ac].logcwmax); qinfo.attr.bursttime = htobe32(IEEE80211_TXOP_TO_US( uath_wme_11g[ac].txop)); qinfo.attr.mode = htobe32(uath_wme_11g[ac].acm);/*XXX? */ qinfo.attr.qflags = htobe32(1); /* XXX? */ error = uath_cmd_write(sc, WDCMSG_SETUP_TX_QUEUE, &qinfo, sizeof qinfo, 0); if (error != 0) break; } return (error); } static void uath_parent(struct ieee80211com *ic) { struct uath_softc *sc = ic->ic_softc; int startall = 0; UATH_LOCK(sc); if (sc->sc_flags & UATH_FLAG_INVALID) { UATH_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if (!(sc->sc_flags & UATH_FLAG_INITDONE)) { uath_init(sc); startall = 1; } } else if (sc->sc_flags & UATH_FLAG_INITDONE) uath_stop(sc); UATH_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static int uath_tx_start(struct uath_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct uath_data *data) { struct ieee80211vap *vap = ni->ni_vap; struct uath_chunk *chunk; struct uath_tx_desc *desc; const struct ieee80211_frame *wh; struct ieee80211_key *k; int framelen, msglen; UATH_ASSERT_LOCKED(sc); data->ni = ni; data->m = m0; chunk = (struct uath_chunk *)data->buf; desc = (struct uath_tx_desc *)(chunk + 1); if (ieee80211_radiotap_active_vap(vap)) { struct uath_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; if (m0->m_flags & M_FRAG) tap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG; ieee80211_radiotap_tx(vap, m0); } + ieee80211_output_seqno_assign(ni, -1, m0); + wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return (ENOBUFS); } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(desc + 1)); framelen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; msglen = framelen + sizeof (struct uath_tx_desc); data->buflen = msglen + sizeof (struct uath_chunk); /* one chunk only for now */ chunk->seqnum = sc->sc_seqnum++; chunk->flags = (m0->m_flags & M_FRAG) ? 0 : UATH_CFLAGS_FINAL; if (m0->m_flags & M_LASTFRAG) chunk->flags |= UATH_CFLAGS_FINAL; chunk->flags = UATH_CFLAGS_FINAL; chunk->length = htobe16(msglen); /* fill Tx descriptor */ desc->msglen = htobe32(msglen); /* NB: to get UATH_TX_NOTIFY reply, `msgid' must be larger than 0 */ desc->msgid = (sc->sc_msgid++) + 1; /* don't care about endianness */ desc->type = htobe32(WDCMSG_SEND); switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: /* NB: force all management frames to highest queue */ if (ni->ni_flags & IEEE80211_NODE_QOS) { /* NB: force all management frames to highest queue */ desc->txqid = htobe32(WME_AC_VO | UATH_TXQID_MINRATE); } else desc->txqid = htobe32(WME_AC_BE | UATH_TXQID_MINRATE); break; case IEEE80211_FC0_TYPE_DATA: /* XXX multicast frames should honor mcastrate */ desc->txqid = htobe32(M_WME_GETAC(m0)); break; default: device_printf(sc->sc_dev, "bogus frame type 0x%x (%s)\n", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); m_freem(m0); return (EIO); } if (vap->iv_state == IEEE80211_S_AUTH || vap->iv_state == IEEE80211_S_ASSOC || vap->iv_state == IEEE80211_S_RUN) desc->connid = htobe32(UATH_ID_BSS); else desc->connid = htobe32(UATH_ID_INVALID); desc->flags = htobe32(0 /* no UATH_TX_NOTIFY */); desc->buflen = htobe32(m0->m_pkthdr.len); #ifdef UATH_DEBUG DPRINTF(sc, UATH_DEBUG_XMIT, "send frame ix %u framelen %d msglen %d connid 0x%x txqid 0x%x\n", desc->msgid, framelen, msglen, be32toh(desc->connid), be32toh(desc->txqid)); if (sc->sc_debug & UATH_DEBUG_XMIT_DUMP) uath_dump_cmd(data->buf, data->buflen, '+'); #endif STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); UATH_STAT_INC(sc, st_tx_pending); usbd_transfer_start(sc->sc_xfer[UATH_BULK_TX]); return (0); } /* * Cleanup driver resources when we run out of buffers while processing * fragments; return the tx buffers allocated and drop node references. */ static void uath_txfrag_cleanup(struct uath_softc *sc, uath_datahead *frags, struct ieee80211_node *ni) { struct uath_data *bf, *next; UATH_ASSERT_LOCKED(sc); STAILQ_FOREACH_SAFE(bf, frags, next, next) { /* NB: bf assumed clean */ STAILQ_REMOVE_HEAD(frags, next); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UATH_STAT_INC(sc, st_tx_inactive); ieee80211_node_decref(ni); } } /* * Setup xmit of a fragmented frame. Allocate a buffer for each frag and bump * the node reference count to reflect the held reference to be setup by * uath_tx_start. */ static int uath_txfrag_setup(struct uath_softc *sc, uath_datahead *frags, struct mbuf *m0, struct ieee80211_node *ni) { struct mbuf *m; struct uath_data *bf; UATH_ASSERT_LOCKED(sc); for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { bf = uath_getbuf(sc); if (bf == NULL) { /* out of buffers, cleanup */ uath_txfrag_cleanup(sc, frags, ni); break; } (void) ieee80211_ref_node(ni); STAILQ_INSERT_TAIL(frags, bf, next); } return !STAILQ_EMPTY(frags); } static int uath_transmit(struct ieee80211com *ic, struct mbuf *m) { struct uath_softc *sc = ic->ic_softc; int error; UATH_LOCK(sc); if ((sc->sc_flags & UATH_FLAG_INITDONE) == 0) { UATH_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { UATH_UNLOCK(sc); return (error); } uath_start(sc); UATH_UNLOCK(sc); return (0); } static void uath_start(struct uath_softc *sc) { struct uath_data *bf; struct ieee80211_node *ni; struct mbuf *m, *next; uath_datahead frags; UATH_ASSERT_LOCKED(sc); if ((sc->sc_flags & UATH_FLAG_INITDONE) == 0 || (sc->sc_flags & UATH_FLAG_INVALID)) return; while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { bf = uath_getbuf(sc); if (bf == NULL) { mbufq_prepend(&sc->sc_snd, m); break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; /* * Check for fragmentation. If this frame has been broken up * verify we have enough buffers to send all the fragments * so all go out or none... */ STAILQ_INIT(&frags); if ((m->m_flags & M_FRAG) && !uath_txfrag_setup(sc, &frags, m, ni)) { DPRINTF(sc, UATH_DEBUG_XMIT, "%s: out of txfrag buffers\n", __func__); ieee80211_free_mbuf(m); goto bad; } sc->sc_seqnum = 0; nextfrag: /* * Pass the frame to the h/w for transmission. * Fragmented frames have each frag chained together * with m_nextpkt. We know there are sufficient uath_data's * to send all the frags because of work done by * uath_txfrag_setup. */ next = m->m_nextpkt; if (uath_tx_start(sc, m, ni, bf) != 0) { bad: if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); reclaim: STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UATH_STAT_INC(sc, st_tx_inactive); uath_txfrag_cleanup(sc, &frags, ni); ieee80211_free_node(ni); continue; } if (next != NULL) { /* * Beware of state changing between frags. XXX check sta power-save state? */ if (ni->ni_vap->iv_state != IEEE80211_S_RUN) { DPRINTF(sc, UATH_DEBUG_XMIT, "%s: flush fragmented packet, state %s\n", __func__, ieee80211_state_name[ni->ni_vap->iv_state]); ieee80211_free_mbuf(next); goto reclaim; } m = next; bf = STAILQ_FIRST(&frags); KASSERT(bf != NULL, ("no buf for txfrag")); STAILQ_REMOVE_HEAD(&frags, next); goto nextfrag; } sc->sc_tx_timer = 5; } } static int uath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct uath_data *bf; struct uath_softc *sc = ic->ic_softc; UATH_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if ((sc->sc_flags & UATH_FLAG_INVALID) || !(sc->sc_flags & UATH_FLAG_INITDONE)) { m_freem(m); UATH_UNLOCK(sc); return (ENETDOWN); } /* grab a TX buffer */ bf = uath_getbuf(sc); if (bf == NULL) { m_freem(m); UATH_UNLOCK(sc); return (ENOBUFS); } sc->sc_seqnum = 0; if (uath_tx_start(sc, m, ni, bf) != 0) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UATH_STAT_INC(sc, st_tx_inactive); UATH_UNLOCK(sc); return (EIO); } UATH_UNLOCK(sc); sc->sc_tx_timer = 5; return (0); } static void uath_scan_start(struct ieee80211com *ic) { /* do nothing */ } static void uath_scan_end(struct ieee80211com *ic) { /* do nothing */ } static void uath_set_channel(struct ieee80211com *ic) { struct uath_softc *sc = ic->ic_softc; UATH_LOCK(sc); if ((sc->sc_flags & UATH_FLAG_INVALID) || (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { UATH_UNLOCK(sc); return; } /* flush data & control requests into the target */ (void)uath_flush(sc); (void)uath_switch_channel(sc, ic->ic_curchan); UATH_UNLOCK(sc); } static int uath_set_rxmulti_filter(struct uath_softc *sc) { /* XXX broken */ return (0); } static void uath_update_mcast(struct ieee80211com *ic) { struct uath_softc *sc = ic->ic_softc; UATH_LOCK(sc); if ((sc->sc_flags & UATH_FLAG_INVALID) || (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { UATH_UNLOCK(sc); return; } /* * this is for avoiding the race condition when we're try to * connect to the AP with WPA. */ if (sc->sc_flags & UATH_FLAG_INITDONE) (void)uath_set_rxmulti_filter(sc); UATH_UNLOCK(sc); } static void uath_update_promisc(struct ieee80211com *ic) { struct uath_softc *sc = ic->ic_softc; UATH_LOCK(sc); if ((sc->sc_flags & UATH_FLAG_INVALID) || (sc->sc_flags & UATH_FLAG_INITDONE) == 0) { UATH_UNLOCK(sc); return; } if (sc->sc_flags & UATH_FLAG_INITDONE) { uath_set_rxfilter(sc, UATH_FILTER_RX_UCAST | UATH_FILTER_RX_MCAST | UATH_FILTER_RX_BCAST | UATH_FILTER_RX_BEACON | UATH_FILTER_RX_PROM, UATH_FILTER_OP_SET); } UATH_UNLOCK(sc); } static int uath_create_connection(struct uath_softc *sc, uint32_t connid) { const struct ieee80211_rateset *rs; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; struct uath_cmd_create_connection create; ni = ieee80211_ref_node(vap->iv_bss); memset(&create, 0, sizeof(create)); create.connid = htobe32(connid); create.bssid = htobe32(0); /* XXX packed or not? */ create.size = htobe32(sizeof(struct uath_cmd_rateset)); rs = &ni->ni_rates; create.connattr.rateset.length = rs->rs_nrates; bcopy(rs->rs_rates, &create.connattr.rateset.set[0], rs->rs_nrates); /* XXX turbo */ if (IEEE80211_IS_CHAN_A(ni->ni_chan)) create.connattr.wlanmode = htobe32(WLAN_MODE_11a); else if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) create.connattr.wlanmode = htobe32(WLAN_MODE_11g); else create.connattr.wlanmode = htobe32(WLAN_MODE_11b); ieee80211_free_node(ni); return uath_cmd_write(sc, WDCMSG_CREATE_CONNECTION, &create, sizeof create, 0); } static int uath_set_rates(struct uath_softc *sc, const struct ieee80211_rateset *rs) { struct uath_cmd_rates rates; memset(&rates, 0, sizeof(rates)); rates.connid = htobe32(UATH_ID_BSS); /* XXX */ rates.size = htobe32(sizeof(struct uath_cmd_rateset)); /* XXX bounds check rs->rs_nrates */ rates.rateset.length = rs->rs_nrates; bcopy(rs->rs_rates, &rates.rateset.set[0], rs->rs_nrates); DPRINTF(sc, UATH_DEBUG_RATES, "setting supported rates nrates=%d\n", rs->rs_nrates); return uath_cmd_write(sc, WDCMSG_SET_BASIC_RATE, &rates, sizeof rates, 0); } static int uath_write_associd(struct uath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; struct uath_cmd_set_associd associd; ni = ieee80211_ref_node(vap->iv_bss); memset(&associd, 0, sizeof(associd)); associd.defaultrateix = htobe32(1); /* XXX */ associd.associd = htobe32(ni->ni_associd); associd.timoffset = htobe32(0x3b); /* XXX */ IEEE80211_ADDR_COPY(associd.bssid, ni->ni_bssid); ieee80211_free_node(ni); return uath_cmd_write(sc, WDCMSG_WRITE_ASSOCID, &associd, sizeof associd, 0); } static int uath_set_ledsteady(struct uath_softc *sc, int lednum, int ledmode) { struct uath_cmd_ledsteady led; led.lednum = htobe32(lednum); led.ledmode = htobe32(ledmode); DPRINTF(sc, UATH_DEBUG_LED, "set %s led %s (steady)\n", (lednum == UATH_LED_LINK) ? "link" : "activity", ledmode ? "on" : "off"); return uath_cmd_write(sc, WDCMSG_SET_LED_STEADY, &led, sizeof led, 0); } static int uath_set_ledblink(struct uath_softc *sc, int lednum, int ledmode, int blinkrate, int slowmode) { struct uath_cmd_ledblink led; led.lednum = htobe32(lednum); led.ledmode = htobe32(ledmode); led.blinkrate = htobe32(blinkrate); led.slowmode = htobe32(slowmode); DPRINTF(sc, UATH_DEBUG_LED, "set %s led %s (blink)\n", (lednum == UATH_LED_LINK) ? "link" : "activity", ledmode ? "on" : "off"); return uath_cmd_write(sc, WDCMSG_SET_LED_BLINK, &led, sizeof led, 0); } static int uath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { enum ieee80211_state ostate = vap->iv_state; int error; struct ieee80211_node *ni; struct ieee80211com *ic = vap->iv_ic; struct uath_softc *sc = ic->ic_softc; struct uath_vap *uvp = UATH_VAP(vap); DPRINTF(sc, UATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); UATH_LOCK(sc); callout_stop(&sc->stat_ch); callout_stop(&sc->watchdog_ch); ni = ieee80211_ref_node(vap->iv_bss); switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_RUN) { /* turn link and activity LEDs off */ uath_set_ledstate(sc, 0); } break; case IEEE80211_S_SCAN: break; case IEEE80211_S_AUTH: /* flush data & control requests into the target */ (void)uath_flush(sc); /* XXX good place? set RTS threshold */ uath_config(sc, CFG_USER_RTS_THRESHOLD, vap->iv_rtsthreshold); /* XXX bad place */ error = uath_set_keys(sc, vap); if (error != 0) { device_printf(sc->sc_dev, "could not set crypto keys, error %d\n", error); break; } if (uath_switch_channel(sc, ni->ni_chan) != 0) { device_printf(sc->sc_dev, "could not switch channel\n"); break; } if (uath_create_connection(sc, UATH_ID_BSS) != 0) { device_printf(sc->sc_dev, "could not create connection\n"); break; } break; case IEEE80211_S_ASSOC: if (uath_set_rates(sc, &ni->ni_rates) != 0) { device_printf(sc->sc_dev, "could not set negotiated rate set\n"); break; } break; case IEEE80211_S_RUN: /* XXX monitor mode doesn't be tested */ if (ic->ic_opmode == IEEE80211_M_MONITOR) { uath_set_ledstate(sc, 1); break; } /* * Tx rate is controlled by firmware, report the maximum * negotiated rate in ifconfig output. */ ieee80211_node_set_txrate_dot11rate(ni, ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates-1]); if (uath_write_associd(sc) != 0) { device_printf(sc->sc_dev, "could not write association id\n"); break; } /* turn link LED on */ uath_set_ledsteady(sc, UATH_LED_LINK, UATH_LED_ON); /* make activity LED blink */ uath_set_ledblink(sc, UATH_LED_ACTIVITY, UATH_LED_ON, 1, 2); /* set state to associated */ uath_set_ledstate(sc, 1); /* start statistics timer */ callout_reset(&sc->stat_ch, hz, uath_stat, sc); break; default: break; } ieee80211_free_node(ni); UATH_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } static int uath_set_key(struct uath_softc *sc, const struct ieee80211_key *wk, int index) { #if 0 struct uath_cmd_crypto crypto; int i; memset(&crypto, 0, sizeof(crypto)); crypto.keyidx = htobe32(index); crypto.magic1 = htobe32(1); crypto.size = htobe32(368); crypto.mask = htobe32(0xffff); crypto.flags = htobe32(0x80000068); if (index != UATH_DEFAULT_KEY) crypto.flags |= htobe32(index << 16); memset(crypto.magic2, 0xff, sizeof(crypto.magic2)); /* * Each byte of the key must be XOR'ed with 10101010 before being * transmitted to the firmware. */ for (i = 0; i < wk->wk_keylen; i++) crypto.key[i] = wk->wk_key[i] ^ 0xaa; DPRINTF(sc, UATH_DEBUG_CRYPTO, "setting crypto key index=%d len=%d\n", index, wk->wk_keylen); return uath_cmd_write(sc, WDCMSG_SET_KEY_CACHE_ENTRY, &crypto, sizeof crypto, 0); #else /* XXX support H/W cryto */ return (0); #endif } static int uath_set_keys(struct uath_softc *sc, struct ieee80211vap *vap) { int i, error; error = 0; for (i = 0; i < IEEE80211_WEP_NKID; i++) { const struct ieee80211_key *wk = &vap->iv_nw_keys[i]; if (wk->wk_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV)) { error = uath_set_key(sc, wk, i); if (error) return (error); } } if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) { error = uath_set_key(sc, &vap->iv_nw_keys[vap->iv_def_txkey], UATH_DEFAULT_KEY); } return (error); } #define UATH_SYSCTL_STAT_ADD32(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) static void uath_sysctl_node(struct uath_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child; struct sysctl_oid *tree; struct uath_stat *stats; stats = &sc->sc_stat; ctx = device_get_sysctl_ctx(sc->sc_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "UATH statistics"); child = SYSCTL_CHILDREN(tree); UATH_SYSCTL_STAT_ADD32(ctx, child, "badchunkseqnum", &stats->st_badchunkseqnum, "Bad chunk sequence numbers"); UATH_SYSCTL_STAT_ADD32(ctx, child, "invalidlen", &stats->st_invalidlen, "Invalid length"); UATH_SYSCTL_STAT_ADD32(ctx, child, "multichunk", &stats->st_multichunk, "Multi chunks"); UATH_SYSCTL_STAT_ADD32(ctx, child, "toobigrxpkt", &stats->st_toobigrxpkt, "Too big rx packets"); UATH_SYSCTL_STAT_ADD32(ctx, child, "stopinprogress", &stats->st_stopinprogress, "Stop in progress"); UATH_SYSCTL_STAT_ADD32(ctx, child, "crcerrs", &stats->st_crcerr, "CRC errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "phyerr", &stats->st_phyerr, "PHY errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "decrypt_crcerr", &stats->st_decrypt_crcerr, "Decryption CRC errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "decrypt_micerr", &stats->st_decrypt_micerr, "Decryption Misc errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "decomperr", &stats->st_decomperr, "Decomp errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "keyerr", &stats->st_keyerr, "Key errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "err", &stats->st_err, "Unknown errors"); UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_active", &stats->st_cmd_active, "Active numbers in Command queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_inactive", &stats->st_cmd_inactive, "Inactive numbers in Command queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_pending", &stats->st_cmd_pending, "Pending numbers in Command queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "cmd_waiting", &stats->st_cmd_waiting, "Waiting numbers in Command queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "rx_active", &stats->st_rx_active, "Active numbers in RX queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "rx_inactive", &stats->st_rx_inactive, "Inactive numbers in RX queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_active", &stats->st_tx_active, "Active numbers in TX queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_inactive", &stats->st_tx_inactive, "Inactive numbers in TX queue"); UATH_SYSCTL_STAT_ADD32(ctx, child, "tx_pending", &stats->st_tx_pending, "Pending numbers in TX queue"); } #undef UATH_SYSCTL_STAT_ADD32 CTASSERT(sizeof(u_int) >= sizeof(uint32_t)); static void uath_cmdeof(struct uath_softc *sc, struct uath_cmd *cmd) { struct uath_cmd_hdr *hdr; uint32_t dlen; hdr = (struct uath_cmd_hdr *)cmd->buf; /* NB: msgid is passed thru w/o byte swapping */ #ifdef UATH_DEBUG if (sc->sc_debug & UATH_DEBUG_CMDS) { uint32_t len = be32toh(hdr->len); printf("%s: %s [ix %u] len %u status %u\n", __func__, uath_codename(be32toh(hdr->code)), hdr->msgid, len, be32toh(hdr->magic)); if (sc->sc_debug & UATH_DEBUG_CMDS_DUMP) uath_dump_cmd(cmd->buf, len > UATH_MAX_CMDSZ ? sizeof(*hdr) : len, '-'); } #endif hdr->code = be32toh(hdr->code); hdr->len = be32toh(hdr->len); hdr->magic = be32toh(hdr->magic); /* target status on return */ switch (hdr->code & 0xff) { /* reply to a read command */ default: DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, "%s: code %d hdr len %u\n", __func__, hdr->code & 0xff, hdr->len); /* * The first response from the target after the * HOST_AVAILABLE has an invalid msgid so we must * treat it specially. */ if (hdr->msgid < UATH_CMD_LIST_COUNT) { uint32_t *rp = (uint32_t *)(hdr+1); u_int olen; if (sizeof(*hdr) > hdr->len || hdr->len > UATH_MAX_CMDSZ) { device_printf(sc->sc_dev, "%s: invalid WDC msg length %u; " "msg ignored\n", __func__, hdr->len); return; } /* * Calculate return/receive payload size; the * first word, if present, always gives the * number of bytes--unless it's 0 in which * case a single 32-bit word should be present. */ dlen = hdr->len - sizeof(*hdr); if (dlen >= sizeof(uint32_t)) { olen = be32toh(rp[0]); dlen -= sizeof(uint32_t); if (olen == 0) { /* convention is 0 =>'s one word */ olen = sizeof(uint32_t); /* XXX KASSERT(olen == dlen ) */ } } else olen = 0; if (cmd->odata != NULL) { /* NB: cmd->olen validated in uath_cmd */ if (olen > (u_int)cmd->olen) { /* XXX complain? */ device_printf(sc->sc_dev, "%s: cmd 0x%x olen %u cmd olen %u\n", __func__, hdr->code, olen, cmd->olen); olen = cmd->olen; } if (olen > dlen) { /* XXX complain, shouldn't happen */ device_printf(sc->sc_dev, "%s: cmd 0x%x olen %u dlen %u\n", __func__, hdr->code, olen, dlen); olen = dlen; } /* XXX have submitter do this */ /* copy answer into caller's supplied buffer */ bcopy(&rp[1], cmd->odata, olen); cmd->olen = olen; } } wakeup_one(cmd); /* wake up caller */ break; case WDCMSG_TARGET_START: if (hdr->msgid >= UATH_CMD_LIST_COUNT) { /* XXX */ return; } dlen = hdr->len - sizeof(*hdr); if (dlen != sizeof(uint32_t)) { device_printf(sc->sc_dev, "%s: dlen (%u) != %zu!\n", __func__, dlen, sizeof(uint32_t)); return; } if (cmd->odata != NULL) { /* XXX have submitter do this */ /* copy answer into caller's supplied buffer */ bcopy(hdr+1, cmd->odata, sizeof(uint32_t)); cmd->olen = sizeof(uint32_t); } wakeup_one(cmd); /* wake up caller */ break; case WDCMSG_SEND_COMPLETE: /* this notification is sent when UATH_TX_NOTIFY is set */ DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, "%s: received Tx notification\n", __func__); break; case WDCMSG_TARGET_GET_STATS: DPRINTF(sc, UATH_DEBUG_RX_PROC | UATH_DEBUG_RECV_ALL, "%s: received device statistics\n", __func__); callout_reset(&sc->stat_ch, hz, uath_stat, sc); break; } } static void uath_intr_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct uath_cmd *cmd; struct uath_cmd_hdr *hdr; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); UATH_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: cmd = STAILQ_FIRST(&sc->sc_cmd_waiting); if (cmd == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_cmd_waiting, next); UATH_STAT_DEC(sc, st_cmd_waiting); STAILQ_INSERT_TAIL(&sc->sc_cmd_inactive, cmd, next); UATH_STAT_INC(sc, st_cmd_inactive); if (actlen < sizeof(struct uath_cmd_hdr)) { device_printf(sc->sc_dev, "%s: short xfer error (actlen %d)\n", __func__, actlen); goto setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, cmd->buf, actlen); hdr = (struct uath_cmd_hdr *)cmd->buf; if (be32toh(hdr->len) > (uint32_t)actlen) { device_printf(sc->sc_dev, "%s: truncated xfer (len %u, actlen %d)\n", __func__, be32toh(hdr->len), actlen); goto setup; } uath_cmdeof(sc, cmd); case USB_ST_SETUP: setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static void uath_intr_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct uath_cmd *cmd; UATH_ASSERT_LOCKED(sc); cmd = STAILQ_FIRST(&sc->sc_cmd_active); if (cmd != NULL && USB_GET_STATE(xfer) != USB_ST_SETUP) { STAILQ_REMOVE_HEAD(&sc->sc_cmd_active, next); UATH_STAT_DEC(sc, st_cmd_active); STAILQ_INSERT_TAIL((cmd->flags & UATH_CMD_FLAG_READ) ? &sc->sc_cmd_waiting : &sc->sc_cmd_inactive, cmd, next); if (cmd->flags & UATH_CMD_FLAG_READ) UATH_STAT_INC(sc, st_cmd_waiting); else UATH_STAT_INC(sc, st_cmd_inactive); } switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: setup: cmd = STAILQ_FIRST(&sc->sc_cmd_pending); if (cmd == NULL) { DPRINTF(sc, UATH_DEBUG_XMIT, "%s: empty pending queue\n", __func__); return; } STAILQ_REMOVE_HEAD(&sc->sc_cmd_pending, next); UATH_STAT_DEC(sc, st_cmd_pending); STAILQ_INSERT_TAIL((cmd->flags & UATH_CMD_FLAG_ASYNC) ? &sc->sc_cmd_inactive : &sc->sc_cmd_active, cmd, next); if (cmd->flags & UATH_CMD_FLAG_ASYNC) UATH_STAT_INC(sc, st_cmd_inactive); else UATH_STAT_INC(sc, st_cmd_active); usbd_xfer_set_frame_data(xfer, 0, cmd->buf, cmd->buflen); usbd_transfer_submit(xfer); break; default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static void uath_update_rxstat(struct uath_softc *sc, uint32_t status) { switch (status) { case UATH_STATUS_STOP_IN_PROGRESS: UATH_STAT_INC(sc, st_stopinprogress); break; case UATH_STATUS_CRC_ERR: UATH_STAT_INC(sc, st_crcerr); break; case UATH_STATUS_PHY_ERR: UATH_STAT_INC(sc, st_phyerr); break; case UATH_STATUS_DECRYPT_CRC_ERR: UATH_STAT_INC(sc, st_decrypt_crcerr); break; case UATH_STATUS_DECRYPT_MIC_ERR: UATH_STAT_INC(sc, st_decrypt_micerr); break; case UATH_STATUS_DECOMP_ERR: UATH_STAT_INC(sc, st_decomperr); break; case UATH_STATUS_KEY_ERR: UATH_STAT_INC(sc, st_keyerr); break; case UATH_STATUS_ERR: UATH_STAT_INC(sc, st_err); break; default: break; } } CTASSERT(UATH_MIN_RXBUFSZ >= sizeof(struct uath_chunk)); static struct mbuf * uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data, struct uath_rx_desc **pdesc) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct uath_chunk *chunk; struct uath_rx_desc *desc; struct mbuf *m = data->m, *mnew, *mp; uint16_t chunklen; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (actlen < (int)UATH_MIN_RXBUFSZ) { DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: wrong xfer size (len=%d)\n", __func__, actlen); counter_u64_add(ic->ic_ierrors, 1); return (NULL); } chunk = (struct uath_chunk *)data->buf; chunklen = be16toh(chunk->length); if (chunk->seqnum == 0 && chunk->flags == 0 && chunklen == 0) { device_printf(sc->sc_dev, "%s: strange response\n", __func__); counter_u64_add(ic->ic_ierrors, 1); UATH_RESET_INTRX(sc); return (NULL); } if (chunklen > actlen) { device_printf(sc->sc_dev, "%s: invalid chunk length (len %u > actlen %d)\n", __func__, chunklen, actlen); counter_u64_add(ic->ic_ierrors, 1); /* XXX cleanup? */ UATH_RESET_INTRX(sc); return (NULL); } if (chunk->seqnum != sc->sc_intrx_nextnum) { DPRINTF(sc, UATH_DEBUG_XMIT, "invalid seqnum %d, expected %d\n", chunk->seqnum, sc->sc_intrx_nextnum); UATH_STAT_INC(sc, st_badchunkseqnum); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } /* check multi-chunk frames */ if ((chunk->seqnum == 0 && !(chunk->flags & UATH_CFLAGS_FINAL)) || (chunk->seqnum != 0 && (chunk->flags & UATH_CFLAGS_FINAL)) || chunk->flags & UATH_CFLAGS_RXMSG) UATH_STAT_INC(sc, st_multichunk); if (chunk->flags & UATH_CFLAGS_FINAL) { if (chunklen < sizeof(struct uath_rx_desc)) { device_printf(sc->sc_dev, "%s: invalid chunk length %d\n", __func__, chunklen); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } chunklen -= sizeof(struct uath_rx_desc); } if (chunklen > 0 && (!(chunk->flags & UATH_CFLAGS_FINAL) || !(chunk->seqnum == 0))) { /* we should use intermediate RX buffer */ if (chunk->seqnum == 0) UATH_RESET_INTRX(sc); if ((sc->sc_intrx_len + sizeof(struct uath_rx_desc) + chunklen) > UATH_MAX_INTRX_SIZE) { UATH_STAT_INC(sc, st_invalidlen); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } m->m_len = chunklen; m->m_data += sizeof(struct uath_chunk); if (sc->sc_intrx_head == NULL) { sc->sc_intrx_head = m; sc->sc_intrx_tail = m; } else { m->m_flags &= ~M_PKTHDR; sc->sc_intrx_tail->m_next = m; sc->sc_intrx_tail = m; } } sc->sc_intrx_len += chunklen; mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: can't get new mbuf, drop frame\n", __func__); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } data->m = mnew; data->buf = mtod(mnew, uint8_t *); /* if the frame is not final continue the transfer */ if (!(chunk->flags & UATH_CFLAGS_FINAL)) { sc->sc_intrx_nextnum++; UATH_RESET_INTRX(sc); return (NULL); } /* * if the frame is not set UATH_CFLAGS_RXMSG, then rx descriptor is * located at the end, 32-bit aligned */ desc = (chunk->flags & UATH_CFLAGS_RXMSG) ? (struct uath_rx_desc *)(chunk + 1) : (struct uath_rx_desc *)(((uint8_t *)chunk) + sizeof(struct uath_chunk) + be16toh(chunk->length) - sizeof(struct uath_rx_desc)); if ((uint8_t *)chunk + actlen - sizeof(struct uath_rx_desc) < (uint8_t *)desc) { device_printf(sc->sc_dev, "%s: wrong Rx descriptor pointer " "(desc %p chunk %p actlen %d)\n", __func__, desc, chunk, actlen); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } *pdesc = desc; DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: frame len %u code %u status %u rate %u antenna %u " "rssi %d channel %u phyerror %u connix %u decrypterror %u " "keycachemiss %u\n", __func__, be32toh(desc->framelen) , be32toh(desc->code), be32toh(desc->status), be32toh(desc->rate) , be32toh(desc->antenna), be32toh(desc->rssi), be32toh(desc->channel) , be32toh(desc->phyerror), be32toh(desc->connix) , be32toh(desc->decrypterror), be32toh(desc->keycachemiss)); if (be32toh(desc->len) > MCLBYTES) { DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: bad descriptor (len=%d)\n", __func__, be32toh(desc->len)); counter_u64_add(ic->ic_ierrors, 1); UATH_STAT_INC(sc, st_toobigrxpkt); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } uath_update_rxstat(sc, be32toh(desc->status)); /* finalize mbuf */ if (sc->sc_intrx_head == NULL) { uint32_t framelen; if (be32toh(desc->framelen) < UATH_RX_DUMMYSIZE) { device_printf(sc->sc_dev, "%s: framelen too small (%u)\n", __func__, be32toh(desc->framelen)); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } framelen = be32toh(desc->framelen) - UATH_RX_DUMMYSIZE; if (framelen > actlen - sizeof(struct uath_chunk) || framelen < sizeof(struct ieee80211_frame_ack)) { device_printf(sc->sc_dev, "%s: wrong frame length (%u, actlen %d)!\n", __func__, framelen, actlen); counter_u64_add(ic->ic_ierrors, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); return (NULL); } m->m_pkthdr.len = m->m_len = framelen; m->m_data += sizeof(struct uath_chunk); } else { mp = sc->sc_intrx_head; mp->m_flags |= M_PKTHDR; mp->m_pkthdr.len = sc->sc_intrx_len; m = mp; } /* there are a lot more fields in the RX descriptor */ if ((sc->sc_flags & UATH_FLAG_INVALID) == 0 && ieee80211_radiotap_active(ic)) { struct uath_rx_radiotap_header *tap = &sc->sc_rxtap; uint32_t tsf_hi = be32toh(desc->tstamp_high); uint32_t tsf_lo = be32toh(desc->tstamp_low); /* XXX only get low order 24bits of tsf from h/w */ tap->wr_tsf = htole64(((uint64_t)tsf_hi << 32) | tsf_lo); tap->wr_flags = 0; if (be32toh(desc->status) == UATH_STATUS_CRC_ERR) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; /* XXX map other status to BADFCS? */ /* XXX ath h/w rate code, need to map */ tap->wr_rate = be32toh(desc->rate); tap->wr_antenna = be32toh(desc->antenna); tap->wr_antsignal = -95 + be32toh(desc->rssi); tap->wr_antnoise = -95; } UATH_RESET_INTRX(sc); return (m); } static void uath_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m = NULL; struct uath_data *data; struct uath_rx_desc *desc = NULL; int8_t nf; UATH_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_rx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); UATH_STAT_DEC(sc, st_rx_active); m = uath_data_rxeof(xfer, data, &desc); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); UATH_STAT_INC(sc, st_rx_inactive); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) return; STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); UATH_STAT_DEC(sc, st_rx_inactive); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); UATH_STAT_INC(sc, st_rx_active); usbd_xfer_set_frame_data(xfer, 0, data->buf, MCLBYTES); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ if (sc->sc_flags & UATH_FLAG_INVALID) { if (m != NULL) m_freem(m); return; } UATH_UNLOCK(sc); if (m != NULL && desc != NULL) { wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); nf = -95; /* XXX */ if (ni != NULL) { (void) ieee80211_input(ni, m, (int)be32toh(desc->rssi), nf); /* node is no longer needed */ ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, (int)be32toh(desc->rssi), nf); m = NULL; desc = NULL; } UATH_LOCK(sc); uath_start(sc); break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&sc->sc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); UATH_STAT_DEC(sc, st_rx_active); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); UATH_STAT_INC(sc, st_rx_inactive); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto setup; } break; } } static void uath_data_txeof(struct usb_xfer *xfer, struct uath_data *data) { struct uath_softc *sc = usbd_xfer_softc(xfer); UATH_ASSERT_LOCKED(sc); if (data->m) { /* XXX status? */ ieee80211_tx_complete(data->ni, data->m, 0); data->m = NULL; data->ni = NULL; } sc->sc_tx_timer = 0; } static void uath_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct uath_softc *sc = usbd_xfer_softc(xfer); struct uath_data *data; UATH_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); UATH_STAT_DEC(sc, st_tx_active); uath_data_txeof(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); UATH_STAT_INC(sc, st_tx_inactive); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_tx_pending); if (data == NULL) { DPRINTF(sc, UATH_DEBUG_XMIT, "%s: empty pending queue\n", __func__); return; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); UATH_STAT_DEC(sc, st_tx_pending); STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); UATH_STAT_INC(sc, st_tx_active); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(xfer); uath_start(sc); break; default: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; if (data->ni != NULL) { if_inc_counter(data->ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); if ((sc->sc_flags & UATH_FLAG_INVALID) == 0) ieee80211_free_node(data->ni); data->ni = NULL; } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static device_method_t uath_methods[] = { DEVMETHOD(device_probe, uath_match), DEVMETHOD(device_attach, uath_attach), DEVMETHOD(device_detach, uath_detach), DEVMETHOD_END }; static driver_t uath_driver = { .name = "uath", .methods = uath_methods, .size = sizeof(struct uath_softc) }; DRIVER_MODULE(uath, uhub, uath_driver, NULL, NULL); MODULE_DEPEND(uath, wlan, 1, 1, 1); MODULE_DEPEND(uath, usb, 1, 1, 1); MODULE_VERSION(uath, 1); USB_PNP_HOST_INFO(uath_devs); diff --git a/sys/dev/usb/wlan/if_upgt.c b/sys/dev/usb/wlan/if_upgt.c index 642631ae34b7..1ab833301b3c 100644 --- a/sys/dev/usb/wlan/if_upgt.c +++ b/sys/dev/usb/wlan/if_upgt.c @@ -1,2343 +1,2348 @@ /* $OpenBSD: if_upgt.c,v 1.35 2008/04/16 18:32:15 damien Exp $ */ /* * Copyright (c) 2007 Marcus Glocker * * 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 "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 "usbdevs.h" #include /* * Driver for the USB PrismGT devices. * * For now just USB 2.0 devices with the GW3887 chipset are supported. * The driver has been written based on the firmware version 2.13.1.0_LM87. * * TODO's: * - MONITOR mode test. * - Add HOSTAP mode. * - Add IBSS mode. * - Support the USB 1.0 devices (NET2280, ISL3880, ISL3886 chipsets). * * Parts of this driver has been influenced by reading the p54u driver * written by Jean-Baptiste Note and * Sebastien Bourdeauducq . */ static SYSCTL_NODE(_hw, OID_AUTO, upgt, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "USB PrismGT GW3887 driver parameters"); #ifdef UPGT_DEBUG int upgt_debug = 0; SYSCTL_INT(_hw_upgt, OID_AUTO, debug, CTLFLAG_RWTUN, &upgt_debug, 0, "control debugging printfs"); enum { UPGT_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ UPGT_DEBUG_RECV = 0x00000002, /* basic recv operation */ UPGT_DEBUG_RESET = 0x00000004, /* reset processing */ UPGT_DEBUG_INTR = 0x00000008, /* INTR */ UPGT_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */ UPGT_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */ UPGT_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */ UPGT_DEBUG_STAT = 0x00000080, /* statistic */ UPGT_DEBUG_FW = 0x00000100, /* firmware */ UPGT_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) #endif /* * Prototypes. */ static device_probe_t upgt_match; static device_attach_t upgt_attach; static device_detach_t upgt_detach; static int upgt_alloc_tx(struct upgt_softc *); static int upgt_alloc_rx(struct upgt_softc *); static int upgt_device_reset(struct upgt_softc *); static void upgt_bulk_tx(struct upgt_softc *, struct upgt_data *); static int upgt_fw_verify(struct upgt_softc *); static int upgt_mem_init(struct upgt_softc *); static int upgt_fw_load(struct upgt_softc *); static int upgt_fw_copy(const uint8_t *, char *, int); static uint32_t upgt_crc32_le(const void *, size_t); static struct mbuf * upgt_rxeof(struct usb_xfer *, struct upgt_data *, int *); static struct mbuf * upgt_rx(struct upgt_softc *, uint8_t *, int, int *); static void upgt_txeof(struct usb_xfer *, struct upgt_data *); static int upgt_eeprom_read(struct upgt_softc *); static int upgt_eeprom_parse(struct upgt_softc *); static void upgt_eeprom_parse_hwrx(struct upgt_softc *, uint8_t *); static void upgt_eeprom_parse_freq3(struct upgt_softc *, uint8_t *, int); static void upgt_eeprom_parse_freq4(struct upgt_softc *, uint8_t *, int); static void upgt_eeprom_parse_freq6(struct upgt_softc *, uint8_t *, int); static uint32_t upgt_chksum_le(const uint32_t *, size_t); static void upgt_tx_done(struct upgt_softc *, uint8_t *); static void upgt_init(struct upgt_softc *); static void upgt_parent(struct ieee80211com *); static int upgt_transmit(struct ieee80211com *, struct mbuf *); static void upgt_start(struct upgt_softc *); static int upgt_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void upgt_scan_start(struct ieee80211com *); static void upgt_scan_end(struct ieee80211com *); static void upgt_set_channel(struct ieee80211com *); static struct ieee80211vap *upgt_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void upgt_vap_delete(struct ieee80211vap *); static void upgt_update_mcast(struct ieee80211com *); static uint8_t upgt_rx_rate(struct upgt_softc *, const int); static void upgt_set_multi(void *); static void upgt_stop(struct upgt_softc *); static void upgt_setup_rates(struct ieee80211vap *, struct ieee80211com *); static int upgt_set_macfilter(struct upgt_softc *, uint8_t); static int upgt_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void upgt_set_chan(struct upgt_softc *, struct ieee80211_channel *); static void upgt_set_led(struct upgt_softc *, int); static void upgt_set_led_blink(void *); static void upgt_get_stats(struct upgt_softc *); static void upgt_mem_free(struct upgt_softc *, uint32_t); static uint32_t upgt_mem_alloc(struct upgt_softc *); static void upgt_free_tx(struct upgt_softc *); static void upgt_free_rx(struct upgt_softc *); static void upgt_watchdog(void *); static void upgt_abort_xfers(struct upgt_softc *); static void upgt_abort_xfers_locked(struct upgt_softc *); static void upgt_sysctl_node(struct upgt_softc *); static struct upgt_data * upgt_getbuf(struct upgt_softc *); static struct upgt_data * upgt_gettxbuf(struct upgt_softc *); static int upgt_tx_start(struct upgt_softc *, struct mbuf *, struct ieee80211_node *, struct upgt_data *); static const char *upgt_fwname = "upgt-gw3887"; static const STRUCT_USB_HOST_ID upgt_devs[] = { #define UPGT_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } /* version 2 devices */ UPGT_DEV(ACCTON, PRISM_GT), UPGT_DEV(BELKIN, F5D7050), UPGT_DEV(CISCOLINKSYS, WUSB54AG), UPGT_DEV(CONCEPTRONIC, PRISM_GT), UPGT_DEV(DELL, PRISM_GT_1), UPGT_DEV(DELL, PRISM_GT_2), UPGT_DEV(FSC, E5400), UPGT_DEV(GLOBESPAN, PRISM_GT_1), UPGT_DEV(GLOBESPAN, PRISM_GT_2), UPGT_DEV(NETGEAR, WG111V1_2), UPGT_DEV(INTERSIL, PRISM_GT), UPGT_DEV(SMC, 2862WG), UPGT_DEV(USR, USR5422), UPGT_DEV(WISTRONNEWEB, UR045G), UPGT_DEV(XYRATEX, PRISM_GT_1), UPGT_DEV(XYRATEX, PRISM_GT_2), UPGT_DEV(ZCOM, XG703A), UPGT_DEV(ZCOM, XM142) }; static usb_callback_t upgt_bulk_rx_callback; static usb_callback_t upgt_bulk_tx_callback; static const struct usb_config upgt_config[UPGT_N_XFERS] = { [UPGT_BULK_TX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = MCLBYTES * UPGT_TX_MAXCOUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1 }, .callback = upgt_bulk_tx_callback, .timeout = UPGT_USB_TIMEOUT, /* ms */ }, [UPGT_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = MCLBYTES * UPGT_RX_MAXCOUNT, .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = upgt_bulk_rx_callback, }, }; static int upgt_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != UPGT_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != UPGT_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(upgt_devs, sizeof(upgt_devs), uaa)); } static int upgt_attach(device_t dev) { struct upgt_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; struct usb_attach_arg *uaa = device_get_ivars(dev); uint8_t bands[IEEE80211_MODE_BYTES]; uint8_t iface_index = UPGT_IFACE_INDEX; int error; sc->sc_dev = dev; sc->sc_udev = uaa->device; #ifdef UPGT_DEBUG sc->sc_debug = upgt_debug; #endif device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init(&sc->sc_led_ch, 0); callout_init(&sc->sc_watchdog_ch, 0); mbufq_init(&sc->sc_snd, ifqmaxlen); error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, upgt_config, UPGT_N_XFERS, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); goto fail1; } sc->sc_rx_dma_buf = usbd_xfer_get_frame_buffer( sc->sc_xfer[UPGT_BULK_RX], 0); sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer( sc->sc_xfer[UPGT_BULK_TX], 0); /* Setup TX and RX buffers */ error = upgt_alloc_tx(sc); if (error) goto fail2; error = upgt_alloc_rx(sc); if (error) goto fail3; /* Initialize the device. */ error = upgt_device_reset(sc); if (error) goto fail4; /* Verify the firmware. */ error = upgt_fw_verify(sc); if (error) goto fail4; /* Calculate device memory space. */ if (sc->sc_memaddr_frame_start == 0 || sc->sc_memaddr_frame_end == 0) { device_printf(dev, "could not find memory space addresses on FW\n"); error = EIO; goto fail4; } sc->sc_memaddr_frame_end -= UPGT_MEMSIZE_RX + 1; sc->sc_memaddr_rx_start = sc->sc_memaddr_frame_end + 1; DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame start=0x%08x\n", sc->sc_memaddr_frame_start); DPRINTF(sc, UPGT_DEBUG_FW, "memory address frame end=0x%08x\n", sc->sc_memaddr_frame_end); DPRINTF(sc, UPGT_DEBUG_FW, "memory address rx start=0x%08x\n", sc->sc_memaddr_rx_start); upgt_mem_init(sc); /* Load the firmware. */ error = upgt_fw_load(sc); if (error) goto fail4; /* Read the whole EEPROM content and parse it. */ error = upgt_eeprom_read(sc); if (error) goto fail4; error = upgt_eeprom_parse(sc); if (error) goto fail4; /* all works related with the device have done here. */ upgt_abort_xfers(sc); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(dev); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_WPA /* 802.11i */ ; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); ieee80211_init_channels(ic, NULL, bands); ieee80211_ifattach(ic); ic->ic_raw_xmit = upgt_raw_xmit; ic->ic_scan_start = upgt_scan_start; ic->ic_scan_end = upgt_scan_end; ic->ic_set_channel = upgt_set_channel; ic->ic_vap_create = upgt_vap_create; ic->ic_vap_delete = upgt_vap_delete; ic->ic_update_mcast = upgt_update_mcast; ic->ic_transmit = upgt_transmit; ic->ic_parent = upgt_parent; + ic->ic_flags_ext |= IEEE80211_FEXT_SEQNO_OFFLOAD; + ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), UPGT_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), UPGT_RX_RADIOTAP_PRESENT); upgt_sysctl_node(sc); if (bootverbose) ieee80211_announce(ic); return (0); fail4: upgt_free_rx(sc); fail3: upgt_free_tx(sc); fail2: usbd_transfer_unsetup(sc->sc_xfer, UPGT_N_XFERS); fail1: mtx_destroy(&sc->sc_mtx); return (error); } static void upgt_txeof(struct usb_xfer *xfer, struct upgt_data *data) { if (data->m) { /* XXX status? */ ieee80211_tx_complete(data->ni, data->m, 0); data->m = NULL; data->ni = NULL; } } static void upgt_get_stats(struct upgt_softc *sc) { struct upgt_data *data_cmd; struct upgt_lmac_mem *mem; struct upgt_lmac_stats *stats; data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__); return; } /* * Transmit the URB containing the CMD data. */ memset(data_cmd->buf, 0, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); stats = (struct upgt_lmac_stats *)(mem + 1); stats->header1.flags = 0; stats->header1.type = UPGT_H1_TYPE_CTRL; stats->header1.len = htole16( sizeof(struct upgt_lmac_stats) - sizeof(struct upgt_lmac_header)); stats->header2.reqid = htole32(sc->sc_memaddr_frame_start); stats->header2.type = htole16(UPGT_H2_TYPE_STATS); stats->header2.flags = 0; data_cmd->buflen = sizeof(*mem) + sizeof(*stats); mem->chksum = upgt_chksum_le((uint32_t *)stats, data_cmd->buflen - sizeof(*mem)); upgt_bulk_tx(sc, data_cmd); } static void upgt_parent(struct ieee80211com *ic) { struct upgt_softc *sc = ic->ic_softc; int startall = 0; UPGT_LOCK(sc); if (sc->sc_flags & UPGT_FLAG_DETACHED) { UPGT_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if (sc->sc_flags & UPGT_FLAG_INITDONE) { if (ic->ic_allmulti > 0 || ic->ic_promisc > 0) upgt_set_multi(sc); } else { upgt_init(sc); startall = 1; } } else if (sc->sc_flags & UPGT_FLAG_INITDONE) upgt_stop(sc); UPGT_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static void upgt_stop(struct upgt_softc *sc) { UPGT_ASSERT_LOCKED(sc); if (sc->sc_flags & UPGT_FLAG_INITDONE) upgt_set_macfilter(sc, IEEE80211_S_INIT); upgt_abort_xfers_locked(sc); /* device down */ sc->sc_tx_timer = 0; sc->sc_flags &= ~UPGT_FLAG_INITDONE; } static void upgt_set_led(struct upgt_softc *sc, int action) { struct upgt_data *data_cmd; struct upgt_lmac_mem *mem; struct upgt_lmac_led *led; data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__); return; } /* * Transmit the URB containing the CMD data. */ memset(data_cmd->buf, 0, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); led = (struct upgt_lmac_led *)(mem + 1); led->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; led->header1.type = UPGT_H1_TYPE_CTRL; led->header1.len = htole16( sizeof(struct upgt_lmac_led) - sizeof(struct upgt_lmac_header)); led->header2.reqid = htole32(sc->sc_memaddr_frame_start); led->header2.type = htole16(UPGT_H2_TYPE_LED); led->header2.flags = 0; switch (action) { case UPGT_LED_OFF: led->mode = htole16(UPGT_LED_MODE_SET); led->action_fix = 0; led->action_tmp = htole16(UPGT_LED_ACTION_OFF); led->action_tmp_dur = 0; break; case UPGT_LED_ON: led->mode = htole16(UPGT_LED_MODE_SET); led->action_fix = 0; led->action_tmp = htole16(UPGT_LED_ACTION_ON); led->action_tmp_dur = 0; break; case UPGT_LED_BLINK: if (sc->sc_state != IEEE80211_S_RUN) { STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next); return; } if (sc->sc_led_blink) { /* previous blink was not finished */ STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next); return; } led->mode = htole16(UPGT_LED_MODE_SET); led->action_fix = htole16(UPGT_LED_ACTION_OFF); led->action_tmp = htole16(UPGT_LED_ACTION_ON); led->action_tmp_dur = htole16(UPGT_LED_ACTION_TMP_DUR); /* lock blink */ sc->sc_led_blink = 1; callout_reset(&sc->sc_led_ch, hz, upgt_set_led_blink, sc); break; default: STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data_cmd, next); return; } data_cmd->buflen = sizeof(*mem) + sizeof(*led); mem->chksum = upgt_chksum_le((uint32_t *)led, data_cmd->buflen - sizeof(*mem)); upgt_bulk_tx(sc, data_cmd); } static void upgt_set_led_blink(void *arg) { struct upgt_softc *sc = arg; /* blink finished, we are ready for a next one */ sc->sc_led_blink = 0; } static void upgt_init(struct upgt_softc *sc) { UPGT_ASSERT_LOCKED(sc); if (sc->sc_flags & UPGT_FLAG_INITDONE) upgt_stop(sc); usbd_transfer_start(sc->sc_xfer[UPGT_BULK_RX]); (void)upgt_set_macfilter(sc, IEEE80211_S_SCAN); sc->sc_flags |= UPGT_FLAG_INITDONE; callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc); } static int upgt_set_macfilter(struct upgt_softc *sc, uint8_t state) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; struct upgt_data *data_cmd; struct upgt_lmac_mem *mem; struct upgt_lmac_filter *filter; UPGT_ASSERT_LOCKED(sc); data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { device_printf(sc->sc_dev, "out of TX buffers.\n"); return (ENOBUFS); } /* * Transmit the URB containing the CMD data. */ memset(data_cmd->buf, 0, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); filter = (struct upgt_lmac_filter *)(mem + 1); filter->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; filter->header1.type = UPGT_H1_TYPE_CTRL; filter->header1.len = htole16( sizeof(struct upgt_lmac_filter) - sizeof(struct upgt_lmac_header)); filter->header2.reqid = htole32(sc->sc_memaddr_frame_start); filter->header2.type = htole16(UPGT_H2_TYPE_MACFILTER); filter->header2.flags = 0; switch (state) { case IEEE80211_S_INIT: DPRINTF(sc, UPGT_DEBUG_STATE, "%s: set MAC filter to INIT\n", __func__); filter->type = htole16(UPGT_FILTER_TYPE_RESET); break; case IEEE80211_S_SCAN: DPRINTF(sc, UPGT_DEBUG_STATE, "set MAC filter to SCAN (bssid %s)\n", ether_sprintf(ieee80211broadcastaddr)); filter->type = htole16(UPGT_FILTER_TYPE_NONE); IEEE80211_ADDR_COPY(filter->dst, vap ? vap->iv_myaddr : ic->ic_macaddr); IEEE80211_ADDR_COPY(filter->src, ieee80211broadcastaddr); filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1); filter->rxaddr = htole32(sc->sc_memaddr_rx_start); filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2); filter->rxhw = htole32(sc->sc_eeprom_hwrx); filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3); break; case IEEE80211_S_RUN: ni = ieee80211_ref_node(vap->iv_bss); /* XXX monitor mode isn't tested yet. */ if (vap->iv_opmode == IEEE80211_M_MONITOR) { filter->type = htole16(UPGT_FILTER_TYPE_MONITOR); IEEE80211_ADDR_COPY(filter->dst, vap ? vap->iv_myaddr : ic->ic_macaddr); IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid); filter->unknown1 = htole16(UPGT_FILTER_MONITOR_UNKNOWN1); filter->rxaddr = htole32(sc->sc_memaddr_rx_start); filter->unknown2 = htole16(UPGT_FILTER_MONITOR_UNKNOWN2); filter->rxhw = htole32(sc->sc_eeprom_hwrx); filter->unknown3 = htole16(UPGT_FILTER_MONITOR_UNKNOWN3); } else { DPRINTF(sc, UPGT_DEBUG_STATE, "set MAC filter to RUN (bssid %s)\n", ether_sprintf(ni->ni_bssid)); filter->type = htole16(UPGT_FILTER_TYPE_STA); IEEE80211_ADDR_COPY(filter->dst, vap ? vap->iv_myaddr : ic->ic_macaddr); IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid); filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1); filter->rxaddr = htole32(sc->sc_memaddr_rx_start); filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2); filter->rxhw = htole32(sc->sc_eeprom_hwrx); filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3); } ieee80211_free_node(ni); break; default: device_printf(sc->sc_dev, "MAC filter does not know that state\n"); break; } data_cmd->buflen = sizeof(*mem) + sizeof(*filter); mem->chksum = upgt_chksum_le((uint32_t *)filter, data_cmd->buflen - sizeof(*mem)); upgt_bulk_tx(sc, data_cmd); return (0); } static void upgt_setup_rates(struct ieee80211vap *vap, struct ieee80211com *ic) { struct upgt_softc *sc = ic->ic_softc; const struct ieee80211_txparam *tp; /* * 0x01 = OFMD6 0x10 = DS1 * 0x04 = OFDM9 0x11 = DS2 * 0x06 = OFDM12 0x12 = DS5 * 0x07 = OFDM18 0x13 = DS11 * 0x08 = OFDM24 * 0x09 = OFDM36 * 0x0a = OFDM48 * 0x0b = OFDM54 */ const uint8_t rateset_auto_11b[] = { 0x13, 0x13, 0x12, 0x11, 0x11, 0x10, 0x10, 0x10 }; const uint8_t rateset_auto_11g[] = { 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x04, 0x01 }; const uint8_t rateset_fix_11bg[] = { 0x10, 0x11, 0x12, 0x13, 0x01, 0x04, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b }; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; /* XXX */ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { /* * Automatic rate control is done by the device. * We just pass the rateset from which the device * will pickup a rate. */ if (ic->ic_curmode == IEEE80211_MODE_11B) memcpy(sc->sc_cur_rateset, rateset_auto_11b, sizeof(sc->sc_cur_rateset)); if (ic->ic_curmode == IEEE80211_MODE_11G || ic->ic_curmode == IEEE80211_MODE_AUTO) memcpy(sc->sc_cur_rateset, rateset_auto_11g, sizeof(sc->sc_cur_rateset)); } else { /* set a fixed rate */ memset(sc->sc_cur_rateset, rateset_fix_11bg[tp->ucastrate], sizeof(sc->sc_cur_rateset)); } } static void upgt_set_multi(void *arg) { /* XXX don't know how to set a device. Lack of docs. */ } static int upgt_transmit(struct ieee80211com *ic, struct mbuf *m) { struct upgt_softc *sc = ic->ic_softc; int error; UPGT_LOCK(sc); if ((sc->sc_flags & UPGT_FLAG_INITDONE) == 0) { UPGT_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { UPGT_UNLOCK(sc); return (error); } upgt_start(sc); UPGT_UNLOCK(sc); return (0); } static void upgt_start(struct upgt_softc *sc) { struct upgt_data *data_tx; struct ieee80211_node *ni; struct mbuf *m; UPGT_ASSERT_LOCKED(sc); if ((sc->sc_flags & UPGT_FLAG_INITDONE) == 0) return; while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { data_tx = upgt_gettxbuf(sc); if (data_tx == NULL) { mbufq_prepend(&sc->sc_snd, m); break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; if (upgt_tx_start(sc, m, ni, data_tx) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, data_tx, next); UPGT_STAT_INC(sc, st_tx_inactive); ieee80211_free_node(ni); continue; } sc->sc_tx_timer = 5; } } static int upgt_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct upgt_softc *sc = ic->ic_softc; struct upgt_data *data_tx = NULL; UPGT_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!(sc->sc_flags & UPGT_FLAG_INITDONE)) { m_freem(m); UPGT_UNLOCK(sc); return ENETDOWN; } data_tx = upgt_gettxbuf(sc); if (data_tx == NULL) { m_freem(m); UPGT_UNLOCK(sc); return (ENOBUFS); } if (upgt_tx_start(sc, m, ni, data_tx) != 0) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, data_tx, next); UPGT_STAT_INC(sc, st_tx_inactive); UPGT_UNLOCK(sc); return (EIO); } UPGT_UNLOCK(sc); sc->sc_tx_timer = 5; return (0); } static void upgt_watchdog(void *arg) { struct upgt_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "watchdog timeout\n"); /* upgt_init(sc); XXX needs a process context ? */ counter_u64_add(ic->ic_oerrors, 1); return; } callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc); } } static uint32_t upgt_mem_alloc(struct upgt_softc *sc) { int i; for (i = 0; i < sc->sc_memory.pages; i++) { if (sc->sc_memory.page[i].used == 0) { sc->sc_memory.page[i].used = 1; return (sc->sc_memory.page[i].addr); } } return (0); } static void upgt_scan_start(struct ieee80211com *ic) { /* do nothing. */ } static void upgt_scan_end(struct ieee80211com *ic) { /* do nothing. */ } static void upgt_set_channel(struct ieee80211com *ic) { struct upgt_softc *sc = ic->ic_softc; UPGT_LOCK(sc); upgt_set_chan(sc, ic->ic_curchan); UPGT_UNLOCK(sc); } static void upgt_set_chan(struct upgt_softc *sc, struct ieee80211_channel *c) { struct ieee80211com *ic = &sc->sc_ic; struct upgt_data *data_cmd; struct upgt_lmac_mem *mem; struct upgt_lmac_channel *chan; int channel; UPGT_ASSERT_LOCKED(sc); channel = ieee80211_chan2ieee(ic, c); if (channel == 0 || channel == IEEE80211_CHAN_ANY) { /* XXX should NEVER happen */ device_printf(sc->sc_dev, "%s: invalid channel %x\n", __func__, channel); return; } DPRINTF(sc, UPGT_DEBUG_STATE, "%s: channel %d\n", __func__, channel); data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { device_printf(sc->sc_dev, "%s: out of buffers.\n", __func__); return; } /* * Transmit the URB containing the CMD data. */ memset(data_cmd->buf, 0, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); chan = (struct upgt_lmac_channel *)(mem + 1); chan->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK; chan->header1.type = UPGT_H1_TYPE_CTRL; chan->header1.len = htole16( sizeof(struct upgt_lmac_channel) - sizeof(struct upgt_lmac_header)); chan->header2.reqid = htole32(sc->sc_memaddr_frame_start); chan->header2.type = htole16(UPGT_H2_TYPE_CHANNEL); chan->header2.flags = 0; chan->unknown1 = htole16(UPGT_CHANNEL_UNKNOWN1); chan->unknown2 = htole16(UPGT_CHANNEL_UNKNOWN2); chan->freq6 = sc->sc_eeprom_freq6[channel]; chan->settings = sc->sc_eeprom_freq6_settings; chan->unknown3 = UPGT_CHANNEL_UNKNOWN3; memcpy(chan->freq3_1, &sc->sc_eeprom_freq3[channel].data, sizeof(chan->freq3_1)); memcpy(chan->freq4, &sc->sc_eeprom_freq4[channel], sizeof(sc->sc_eeprom_freq4[channel])); memcpy(chan->freq3_2, &sc->sc_eeprom_freq3[channel].data, sizeof(chan->freq3_2)); data_cmd->buflen = sizeof(*mem) + sizeof(*chan); mem->chksum = upgt_chksum_le((uint32_t *)chan, data_cmd->buflen - sizeof(*mem)); upgt_bulk_tx(sc, data_cmd); } static struct ieee80211vap * upgt_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct upgt_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; uvp = malloc(sizeof(struct upgt_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = upgt_newstate; /* setup device rates */ upgt_setup_rates(vap, ic); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return vap; } static int upgt_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct upgt_vap *uvp = UPGT_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct upgt_softc *sc = ic->ic_softc; /* do it in a process context */ sc->sc_state = nstate; IEEE80211_UNLOCK(ic); UPGT_LOCK(sc); callout_stop(&sc->sc_led_ch); callout_stop(&sc->sc_watchdog_ch); switch (nstate) { case IEEE80211_S_INIT: /* do not accept any frames if the device is down */ (void)upgt_set_macfilter(sc, sc->sc_state); upgt_set_led(sc, UPGT_LED_OFF); break; case IEEE80211_S_SCAN: upgt_set_chan(sc, ic->ic_curchan); break; case IEEE80211_S_AUTH: upgt_set_chan(sc, ic->ic_curchan); break; case IEEE80211_S_ASSOC: break; case IEEE80211_S_RUN: upgt_set_macfilter(sc, sc->sc_state); upgt_set_led(sc, UPGT_LED_ON); break; default: break; } UPGT_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } static void upgt_vap_delete(struct ieee80211vap *vap) { struct upgt_vap *uvp = UPGT_VAP(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void upgt_update_mcast(struct ieee80211com *ic) { struct upgt_softc *sc = ic->ic_softc; upgt_set_multi(sc); } static int upgt_eeprom_parse(struct upgt_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct upgt_eeprom_header *eeprom_header; struct upgt_eeprom_option *eeprom_option; uint16_t option_len; uint16_t option_type; uint16_t preamble_len; int option_end = 0; /* calculate eeprom options start offset */ eeprom_header = (struct upgt_eeprom_header *)sc->sc_eeprom; preamble_len = le16toh(eeprom_header->preamble_len); eeprom_option = (struct upgt_eeprom_option *)(sc->sc_eeprom + (sizeof(struct upgt_eeprom_header) + preamble_len)); while (!option_end) { /* sanity check */ if (eeprom_option >= (struct upgt_eeprom_option *) (sc->sc_eeprom + UPGT_EEPROM_SIZE)) { return (EINVAL); } /* the eeprom option length is stored in words */ option_len = (le16toh(eeprom_option->len) - 1) * sizeof(uint16_t); option_type = le16toh(eeprom_option->type); /* sanity check */ if (option_len == 0 || option_len >= UPGT_EEPROM_SIZE) return (EINVAL); switch (option_type) { case UPGT_EEPROM_TYPE_NAME: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM name len=%d\n", option_len); break; case UPGT_EEPROM_TYPE_SERIAL: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM serial len=%d\n", option_len); break; case UPGT_EEPROM_TYPE_MAC: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM mac len=%d\n", option_len); IEEE80211_ADDR_COPY(ic->ic_macaddr, eeprom_option->data); break; case UPGT_EEPROM_TYPE_HWRX: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM hwrx len=%d\n", option_len); upgt_eeprom_parse_hwrx(sc, eeprom_option->data); break; case UPGT_EEPROM_TYPE_CHIP: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM chip len=%d\n", option_len); break; case UPGT_EEPROM_TYPE_FREQ3: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM freq3 len=%d\n", option_len); upgt_eeprom_parse_freq3(sc, eeprom_option->data, option_len); break; case UPGT_EEPROM_TYPE_FREQ4: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM freq4 len=%d\n", option_len); upgt_eeprom_parse_freq4(sc, eeprom_option->data, option_len); break; case UPGT_EEPROM_TYPE_FREQ5: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM freq5 len=%d\n", option_len); break; case UPGT_EEPROM_TYPE_FREQ6: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM freq6 len=%d\n", option_len); upgt_eeprom_parse_freq6(sc, eeprom_option->data, option_len); break; case UPGT_EEPROM_TYPE_END: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM end len=%d\n", option_len); option_end = 1; break; case UPGT_EEPROM_TYPE_OFF: DPRINTF(sc, UPGT_DEBUG_FW, "%s: EEPROM off without end option\n", __func__); return (EIO); default: DPRINTF(sc, UPGT_DEBUG_FW, "EEPROM unknown type 0x%04x len=%d\n", option_type, option_len); break; } /* jump to next EEPROM option */ eeprom_option = (struct upgt_eeprom_option *) (eeprom_option->data + option_len); } return (0); } static void upgt_eeprom_parse_freq3(struct upgt_softc *sc, uint8_t *data, int len) { struct upgt_eeprom_freq3_header *freq3_header; struct upgt_lmac_freq3 *freq3; int i; int elements; unsigned channel; freq3_header = (struct upgt_eeprom_freq3_header *)data; freq3 = (struct upgt_lmac_freq3 *)(freq3_header + 1); elements = freq3_header->elements; DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d\n", freq3_header->flags, elements); if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq3[0]))) return; for (i = 0; i < elements; i++) { channel = ieee80211_mhz2ieee(le16toh(freq3[i].freq), 0); if (channel >= IEEE80211_CHAN_MAX) continue; sc->sc_eeprom_freq3[channel] = freq3[i]; DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", le16toh(sc->sc_eeprom_freq3[channel].freq), channel); } } void upgt_eeprom_parse_freq4(struct upgt_softc *sc, uint8_t *data, int len) { struct upgt_eeprom_freq4_header *freq4_header; struct upgt_eeprom_freq4_1 *freq4_1; struct upgt_eeprom_freq4_2 *freq4_2; int i; int j; int elements; int settings; unsigned channel; freq4_header = (struct upgt_eeprom_freq4_header *)data; freq4_1 = (struct upgt_eeprom_freq4_1 *)(freq4_header + 1); elements = freq4_header->elements; settings = freq4_header->settings; /* we need this value later */ sc->sc_eeprom_freq6_settings = freq4_header->settings; DPRINTF(sc, UPGT_DEBUG_FW, "flags=0x%02x elements=%d settings=%d\n", freq4_header->flags, elements, settings); if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq4_1[0]))) return; for (i = 0; i < elements; i++) { channel = ieee80211_mhz2ieee(le16toh(freq4_1[i].freq), 0); if (channel >= IEEE80211_CHAN_MAX) continue; freq4_2 = (struct upgt_eeprom_freq4_2 *)freq4_1[i].data; for (j = 0; j < settings; j++) { sc->sc_eeprom_freq4[channel][j].cmd = freq4_2[j]; sc->sc_eeprom_freq4[channel][j].pad = 0; } DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", le16toh(freq4_1[i].freq), channel); } } void upgt_eeprom_parse_freq6(struct upgt_softc *sc, uint8_t *data, int len) { struct upgt_lmac_freq6 *freq6; int i; int elements; unsigned channel; freq6 = (struct upgt_lmac_freq6 *)data; elements = len / sizeof(struct upgt_lmac_freq6); DPRINTF(sc, UPGT_DEBUG_FW, "elements=%d\n", elements); if (elements >= (int)(UPGT_EEPROM_SIZE / sizeof(freq6[0]))) return; for (i = 0; i < elements; i++) { channel = ieee80211_mhz2ieee(le16toh(freq6[i].freq), 0); if (channel >= IEEE80211_CHAN_MAX) continue; sc->sc_eeprom_freq6[channel] = freq6[i]; DPRINTF(sc, UPGT_DEBUG_FW, "frequence=%d, channel=%d\n", le16toh(sc->sc_eeprom_freq6[channel].freq), channel); } } static void upgt_eeprom_parse_hwrx(struct upgt_softc *sc, uint8_t *data) { struct upgt_eeprom_option_hwrx *option_hwrx; option_hwrx = (struct upgt_eeprom_option_hwrx *)data; sc->sc_eeprom_hwrx = option_hwrx->rxfilter - UPGT_EEPROM_RX_CONST; DPRINTF(sc, UPGT_DEBUG_FW, "hwrx option value=0x%04x\n", sc->sc_eeprom_hwrx); } static int upgt_eeprom_read(struct upgt_softc *sc) { struct upgt_data *data_cmd; struct upgt_lmac_mem *mem; struct upgt_lmac_eeprom *eeprom; int block, error, offset; UPGT_LOCK(sc); usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(100)); offset = 0; block = UPGT_EEPROM_BLOCK_SIZE; while (offset < UPGT_EEPROM_SIZE) { DPRINTF(sc, UPGT_DEBUG_FW, "request EEPROM block (offset=%d, len=%d)\n", offset, block); data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { UPGT_UNLOCK(sc); return (ENOBUFS); } /* * Transmit the URB containing the CMD data. */ memset(data_cmd->buf, 0, MCLBYTES); mem = (struct upgt_lmac_mem *)data_cmd->buf; mem->addr = htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD); eeprom = (struct upgt_lmac_eeprom *)(mem + 1); eeprom->header1.flags = 0; eeprom->header1.type = UPGT_H1_TYPE_CTRL; eeprom->header1.len = htole16(( sizeof(struct upgt_lmac_eeprom) - sizeof(struct upgt_lmac_header)) + block); eeprom->header2.reqid = htole32(sc->sc_memaddr_frame_start); eeprom->header2.type = htole16(UPGT_H2_TYPE_EEPROM); eeprom->header2.flags = 0; eeprom->offset = htole16(offset); eeprom->len = htole16(block); data_cmd->buflen = sizeof(*mem) + sizeof(*eeprom) + block; mem->chksum = upgt_chksum_le((uint32_t *)eeprom, data_cmd->buflen - sizeof(*mem)); upgt_bulk_tx(sc, data_cmd); error = mtx_sleep(sc, &sc->sc_mtx, 0, "eeprom_request", hz); if (error != 0) { device_printf(sc->sc_dev, "timeout while waiting for EEPROM data\n"); UPGT_UNLOCK(sc); return (EIO); } offset += block; if (UPGT_EEPROM_SIZE - offset < block) block = UPGT_EEPROM_SIZE - offset; } UPGT_UNLOCK(sc); return (0); } /* * When a rx data came in the function returns a mbuf and a rssi values. */ static struct mbuf * upgt_rxeof(struct usb_xfer *xfer, struct upgt_data *data, int *rssi) { struct mbuf *m = NULL; struct upgt_softc *sc = usbd_xfer_softc(xfer); struct upgt_lmac_header *header; struct upgt_lmac_eeprom *eeprom; uint8_t h1_type; uint16_t h2_type; int actlen, sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); UPGT_ASSERT_LOCKED(sc); if (actlen < 1) return (NULL); /* Check only at the very beginning. */ if (!(sc->sc_flags & UPGT_FLAG_FWLOADED) && (memcmp(data->buf, "OK", 2) == 0)) { sc->sc_flags |= UPGT_FLAG_FWLOADED; wakeup_one(sc); return (NULL); } if (actlen < (int)UPGT_RX_MINSZ) return (NULL); /* * Check what type of frame came in. */ header = (struct upgt_lmac_header *)(data->buf + 4); h1_type = header->header1.type; h2_type = le16toh(header->header2.type); if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_EEPROM) { eeprom = (struct upgt_lmac_eeprom *)(data->buf + 4); uint16_t eeprom_offset = le16toh(eeprom->offset); uint16_t eeprom_len = le16toh(eeprom->len); DPRINTF(sc, UPGT_DEBUG_FW, "received EEPROM block (offset=%d, len=%d)\n", eeprom_offset, eeprom_len); memcpy(sc->sc_eeprom + eeprom_offset, data->buf + sizeof(struct upgt_lmac_eeprom) + 4, eeprom_len); /* EEPROM data has arrived in time, wakeup. */ wakeup(sc); } else if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_TX_DONE) { DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: received 802.11 TX done\n", __func__); upgt_tx_done(sc, data->buf + 4); } else if (h1_type == UPGT_H1_TYPE_RX_DATA || h1_type == UPGT_H1_TYPE_RX_DATA_MGMT) { DPRINTF(sc, UPGT_DEBUG_RECV, "%s: received 802.11 RX data\n", __func__); m = upgt_rx(sc, data->buf + 4, le16toh(header->header1.len), rssi); } else if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_STATS) { DPRINTF(sc, UPGT_DEBUG_STAT, "%s: received statistic data\n", __func__); /* TODO: what could we do with the statistic data? */ } else { /* ignore unknown frame types */ DPRINTF(sc, UPGT_DEBUG_INTR, "received unknown frame type 0x%02x\n", header->header1.type); } return (m); } /* * The firmware awaits a checksum for each frame we send to it. * The algorithm used therefor is uncommon but somehow similar to CRC32. */ static uint32_t upgt_chksum_le(const uint32_t *buf, size_t size) { size_t i; uint32_t crc = 0; for (i = 0; i < size; i += sizeof(uint32_t)) { crc = htole32(crc ^ *buf++); crc = htole32((crc >> 5) ^ (crc << 3)); } return (crc); } static struct mbuf * upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen, int *rssi) { struct ieee80211com *ic = &sc->sc_ic; struct upgt_lmac_rx_desc *rxdesc; struct mbuf *m; /* * don't pass packets to the ieee80211 framework if the driver isn't * RUNNING. */ if (!(sc->sc_flags & UPGT_FLAG_INITDONE)) return (NULL); /* access RX packet descriptor */ rxdesc = (struct upgt_lmac_rx_desc *)data; /* create mbuf which is suitable for strict alignment archs */ KASSERT((pkglen + ETHER_ALIGN) < MCLBYTES, ("A current mbuf storage is small (%d)", pkglen + ETHER_ALIGN)); m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { device_printf(sc->sc_dev, "could not create RX mbuf\n"); return (NULL); } m_adj(m, ETHER_ALIGN); memcpy(mtod(m, char *), rxdesc->data, pkglen); /* trim FCS */ m->m_len = m->m_pkthdr.len = pkglen - IEEE80211_CRC_LEN; if (ieee80211_radiotap_active(ic)) { struct upgt_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; tap->wr_rate = upgt_rx_rate(sc, rxdesc->rate); tap->wr_antsignal = rxdesc->rssi; } DPRINTF(sc, UPGT_DEBUG_RX_PROC, "%s: RX done\n", __func__); *rssi = rxdesc->rssi; return (m); } static uint8_t upgt_rx_rate(struct upgt_softc *sc, const int rate) { struct ieee80211com *ic = &sc->sc_ic; static const uint8_t cck_upgt2rate[4] = { 2, 4, 11, 22 }; static const uint8_t ofdm_upgt2rate[12] = { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; if (ic->ic_curmode == IEEE80211_MODE_11B && !(rate < 0 || rate > 3)) return cck_upgt2rate[rate & 0xf]; if (ic->ic_curmode == IEEE80211_MODE_11G && !(rate < 0 || rate > 11)) return ofdm_upgt2rate[rate & 0xf]; return (0); } static void upgt_tx_done(struct upgt_softc *sc, uint8_t *data) { struct upgt_lmac_tx_done_desc *desc; int i, freed = 0; UPGT_ASSERT_LOCKED(sc); desc = (struct upgt_lmac_tx_done_desc *)data; for (i = 0; i < UPGT_TX_MAXCOUNT; i++) { struct upgt_data *data_tx = &sc->sc_tx_data[i]; if (data_tx->addr == le32toh(desc->header2.reqid)) { upgt_mem_free(sc, data_tx->addr); data_tx->ni = NULL; data_tx->addr = 0; data_tx->m = NULL; DPRINTF(sc, UPGT_DEBUG_TX_PROC, "TX done: memaddr=0x%08x, status=0x%04x, rssi=%d, ", le32toh(desc->header2.reqid), le16toh(desc->status), le16toh(desc->rssi)); DPRINTF(sc, UPGT_DEBUG_TX_PROC, "seq=%d\n", le16toh(desc->seq)); freed++; } } if (freed != 0) { UPGT_UNLOCK(sc); sc->sc_tx_timer = 0; upgt_start(sc); UPGT_LOCK(sc); } } static void upgt_mem_free(struct upgt_softc *sc, uint32_t addr) { int i; for (i = 0; i < sc->sc_memory.pages; i++) { if (sc->sc_memory.page[i].addr == addr) { sc->sc_memory.page[i].used = 0; return; } } device_printf(sc->sc_dev, "could not free memory address 0x%08x\n", addr); } static int upgt_fw_load(struct upgt_softc *sc) { const struct firmware *fw; struct upgt_data *data_cmd; struct upgt_fw_x2_header *x2; char start_fwload_cmd[] = { 0x3c, 0x0d }; int error = 0; size_t offset; int bsize; int n; uint32_t crc32; fw = firmware_get(upgt_fwname); if (fw == NULL) { device_printf(sc->sc_dev, "could not read microcode %s\n", upgt_fwname); return (EIO); } UPGT_LOCK(sc); /* send firmware start load command */ data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { error = ENOBUFS; goto fail; } data_cmd->buflen = sizeof(start_fwload_cmd); memcpy(data_cmd->buf, start_fwload_cmd, data_cmd->buflen); upgt_bulk_tx(sc, data_cmd); /* send X2 header */ data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { error = ENOBUFS; goto fail; } data_cmd->buflen = sizeof(struct upgt_fw_x2_header); x2 = (struct upgt_fw_x2_header *)data_cmd->buf; memcpy(x2->signature, UPGT_X2_SIGNATURE, UPGT_X2_SIGNATURE_SIZE); x2->startaddr = htole32(UPGT_MEMADDR_FIRMWARE_START); x2->len = htole32(fw->datasize); x2->crc = upgt_crc32_le((uint8_t *)data_cmd->buf + UPGT_X2_SIGNATURE_SIZE, sizeof(struct upgt_fw_x2_header) - UPGT_X2_SIGNATURE_SIZE - sizeof(uint32_t)); upgt_bulk_tx(sc, data_cmd); /* download firmware */ for (offset = 0; offset < fw->datasize; offset += bsize) { if (fw->datasize - offset > UPGT_FW_BLOCK_SIZE) bsize = UPGT_FW_BLOCK_SIZE; else bsize = fw->datasize - offset; data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { error = ENOBUFS; goto fail; } n = upgt_fw_copy((const uint8_t *)fw->data + offset, data_cmd->buf, bsize); data_cmd->buflen = bsize; upgt_bulk_tx(sc, data_cmd); DPRINTF(sc, UPGT_DEBUG_FW, "FW offset=%zu, read=%d, sent=%d\n", offset, n, bsize); bsize = n; } DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware downloaded\n", __func__); /* load firmware */ data_cmd = upgt_getbuf(sc); if (data_cmd == NULL) { error = ENOBUFS; goto fail; } crc32 = upgt_crc32_le(fw->data, fw->datasize); *((uint32_t *)(data_cmd->buf) ) = crc32; *((uint8_t *)(data_cmd->buf) + 4) = 'g'; *((uint8_t *)(data_cmd->buf) + 5) = '\r'; data_cmd->buflen = 6; upgt_bulk_tx(sc, data_cmd); /* waiting 'OK' response. */ usbd_transfer_start(sc->sc_xfer[UPGT_BULK_RX]); error = mtx_sleep(sc, &sc->sc_mtx, 0, "upgtfw", 2 * hz); if (error != 0) { device_printf(sc->sc_dev, "firmware load failed\n"); error = EIO; } DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware loaded\n", __func__); fail: UPGT_UNLOCK(sc); firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static uint32_t upgt_crc32_le(const void *buf, size_t size) { uint32_t crc; crc = ether_crc32_le(buf, size); /* apply final XOR value as common for CRC-32 */ crc = htole32(crc ^ 0xffffffffU); return (crc); } /* * While copying the version 2 firmware, we need to replace two characters: * * 0x7e -> 0x7d 0x5e * 0x7d -> 0x7d 0x5d */ static int upgt_fw_copy(const uint8_t *src, char *dst, int size) { int i, j; for (i = 0, j = 0; i < size && j < size; i++) { switch (src[i]) { case 0x7e: dst[j] = 0x7d; j++; dst[j] = 0x5e; j++; break; case 0x7d: dst[j] = 0x7d; j++; dst[j] = 0x5d; j++; break; default: dst[j] = src[i]; j++; break; } } return (i); } static int upgt_mem_init(struct upgt_softc *sc) { int i; for (i = 0; i < UPGT_MEMORY_MAX_PAGES; i++) { sc->sc_memory.page[i].used = 0; if (i == 0) { /* * The first memory page is always reserved for * command data. */ sc->sc_memory.page[i].addr = sc->sc_memaddr_frame_start + MCLBYTES; } else { sc->sc_memory.page[i].addr = sc->sc_memory.page[i - 1].addr + MCLBYTES; } if (sc->sc_memory.page[i].addr + MCLBYTES >= sc->sc_memaddr_frame_end) break; DPRINTF(sc, UPGT_DEBUG_FW, "memory address page %d=0x%08x\n", i, sc->sc_memory.page[i].addr); } sc->sc_memory.pages = i; DPRINTF(sc, UPGT_DEBUG_FW, "memory pages=%d\n", sc->sc_memory.pages); return (0); } static int upgt_fw_verify(struct upgt_softc *sc) { const struct firmware *fw; const struct upgt_fw_bra_option *bra_opt; const struct upgt_fw_bra_descr *descr; const uint8_t *p; const uint32_t *uc; uint32_t bra_option_type, bra_option_len; size_t offset; int bra_end = 0; int error = 0; fw = firmware_get(upgt_fwname); if (fw == NULL) { device_printf(sc->sc_dev, "could not read microcode %s\n", upgt_fwname); return EIO; } /* * Seek to beginning of Boot Record Area (BRA). */ for (offset = 0; offset < fw->datasize; offset += sizeof(*uc)) { uc = (const uint32_t *)((const uint8_t *)fw->data + offset); if (*uc == 0) break; } for (; offset < fw->datasize; offset += sizeof(*uc)) { uc = (const uint32_t *)((const uint8_t *)fw->data + offset); if (*uc != 0) break; } if (offset == fw->datasize) { device_printf(sc->sc_dev, "firmware Boot Record Area not found\n"); error = EIO; goto fail; } DPRINTF(sc, UPGT_DEBUG_FW, "firmware Boot Record Area found at offset %zu\n", offset); /* * Parse Boot Record Area (BRA) options. */ while (offset < fw->datasize && bra_end == 0) { /* get current BRA option */ p = (const uint8_t *)fw->data + offset; bra_opt = (const struct upgt_fw_bra_option *)p; bra_option_type = le32toh(bra_opt->type); bra_option_len = le32toh(bra_opt->len) * sizeof(*uc); switch (bra_option_type) { case UPGT_BRA_TYPE_FW: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_FW len=%d\n", bra_option_len); if (bra_option_len != UPGT_BRA_FWTYPE_SIZE) { device_printf(sc->sc_dev, "wrong UPGT_BRA_TYPE_FW len\n"); error = EIO; goto fail; } if (memcmp(UPGT_BRA_FWTYPE_LM86, bra_opt->data, bra_option_len) == 0) { sc->sc_fw_type = UPGT_FWTYPE_LM86; break; } if (memcmp(UPGT_BRA_FWTYPE_LM87, bra_opt->data, bra_option_len) == 0) { sc->sc_fw_type = UPGT_FWTYPE_LM87; break; } device_printf(sc->sc_dev, "unsupported firmware type\n"); error = EIO; goto fail; case UPGT_BRA_TYPE_VERSION: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_VERSION len=%d\n", bra_option_len); break; case UPGT_BRA_TYPE_DEPIF: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_DEPIF len=%d\n", bra_option_len); break; case UPGT_BRA_TYPE_EXPIF: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_EXPIF len=%d\n", bra_option_len); break; case UPGT_BRA_TYPE_DESCR: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_DESCR len=%d\n", bra_option_len); descr = (const struct upgt_fw_bra_descr *)bra_opt->data; sc->sc_memaddr_frame_start = le32toh(descr->memaddr_space_start); sc->sc_memaddr_frame_end = le32toh(descr->memaddr_space_end); DPRINTF(sc, UPGT_DEBUG_FW, "memory address space start=0x%08x\n", sc->sc_memaddr_frame_start); DPRINTF(sc, UPGT_DEBUG_FW, "memory address space end=0x%08x\n", sc->sc_memaddr_frame_end); break; case UPGT_BRA_TYPE_END: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_END len=%d\n", bra_option_len); bra_end = 1; break; default: DPRINTF(sc, UPGT_DEBUG_FW, "unknown BRA option len=%d\n", bra_option_len); error = EIO; goto fail; } /* jump to next BRA option */ offset += sizeof(struct upgt_fw_bra_option) + bra_option_len; } DPRINTF(sc, UPGT_DEBUG_FW, "%s: firmware verified", __func__); fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static void upgt_bulk_tx(struct upgt_softc *sc, struct upgt_data *data) { UPGT_ASSERT_LOCKED(sc); STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); UPGT_STAT_INC(sc, st_tx_pending); usbd_transfer_start(sc->sc_xfer[UPGT_BULK_TX]); } static int upgt_device_reset(struct upgt_softc *sc) { struct upgt_data *data; char init_cmd[] = { 0x7e, 0x7e, 0x7e, 0x7e }; UPGT_LOCK(sc); data = upgt_getbuf(sc); if (data == NULL) { UPGT_UNLOCK(sc); return (ENOBUFS); } memcpy(data->buf, init_cmd, sizeof(init_cmd)); data->buflen = sizeof(init_cmd); upgt_bulk_tx(sc, data); usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(100)); UPGT_UNLOCK(sc); DPRINTF(sc, UPGT_DEBUG_FW, "%s: device initialized\n", __func__); return (0); } static int upgt_alloc_tx(struct upgt_softc *sc) { int i; STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); for (i = 0; i < UPGT_TX_MAXCOUNT; i++) { struct upgt_data *data = &sc->sc_tx_data[i]; data->buf = ((uint8_t *)sc->sc_tx_dma_buf) + (i * MCLBYTES); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); UPGT_STAT_INC(sc, st_tx_inactive); } return (0); } static int upgt_alloc_rx(struct upgt_softc *sc) { int i; STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < UPGT_RX_MAXCOUNT; i++) { struct upgt_data *data = &sc->sc_rx_data[i]; data->buf = ((uint8_t *)sc->sc_rx_dma_buf) + (i * MCLBYTES); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); } return (0); } static int upgt_detach(device_t dev) { struct upgt_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; unsigned x; /* * Prevent further allocations from RX/TX/CMD * data lists and ioctls */ UPGT_LOCK(sc); sc->sc_flags |= UPGT_FLAG_DETACHED; STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); upgt_stop(sc); UPGT_UNLOCK(sc); callout_drain(&sc->sc_led_ch); callout_drain(&sc->sc_watchdog_ch); /* drain USB transfers */ for (x = 0; x != UPGT_N_XFERS; x++) usbd_transfer_drain(sc->sc_xfer[x]); /* free data buffers */ UPGT_LOCK(sc); upgt_free_rx(sc); upgt_free_tx(sc); UPGT_UNLOCK(sc); /* free USB transfers and some data buffers */ usbd_transfer_unsetup(sc->sc_xfer, UPGT_N_XFERS); ieee80211_ifdetach(ic); mbufq_drain(&sc->sc_snd); mtx_destroy(&sc->sc_mtx); return (0); } static void upgt_free_rx(struct upgt_softc *sc) { int i; for (i = 0; i < UPGT_RX_MAXCOUNT; i++) { struct upgt_data *data = &sc->sc_rx_data[i]; data->buf = NULL; data->ni = NULL; } } static void upgt_free_tx(struct upgt_softc *sc) { int i; for (i = 0; i < UPGT_TX_MAXCOUNT; i++) { struct upgt_data *data = &sc->sc_tx_data[i]; if (data->ni != NULL) ieee80211_free_node(data->ni); data->buf = NULL; data->ni = NULL; } } static void upgt_abort_xfers_locked(struct upgt_softc *sc) { int i; UPGT_ASSERT_LOCKED(sc); /* abort any pending transfers */ for (i = 0; i < UPGT_N_XFERS; i++) usbd_transfer_stop(sc->sc_xfer[i]); } static void upgt_abort_xfers(struct upgt_softc *sc) { UPGT_LOCK(sc); upgt_abort_xfers_locked(sc); UPGT_UNLOCK(sc); } #define UPGT_SYSCTL_STAT_ADD32(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) static void upgt_sysctl_node(struct upgt_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child; struct sysctl_oid *tree; struct upgt_stat *stats; stats = &sc->sc_stat; ctx = device_get_sysctl_ctx(sc->sc_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "UPGT statistics"); child = SYSCTL_CHILDREN(tree); UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_active", &stats->st_tx_active, "Active numbers in TX queue"); UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_inactive", &stats->st_tx_inactive, "Inactive numbers in TX queue"); UPGT_SYSCTL_STAT_ADD32(ctx, child, "tx_pending", &stats->st_tx_pending, "Pending numbers in TX queue"); } #undef UPGT_SYSCTL_STAT_ADD32 static struct upgt_data * _upgt_getbuf(struct upgt_softc *sc) { struct upgt_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); UPGT_STAT_DEC(sc, st_tx_inactive); } else bf = NULL; if (bf == NULL) DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: %s\n", __func__, "out of xmit buffers"); return (bf); } static struct upgt_data * upgt_getbuf(struct upgt_softc *sc) { struct upgt_data *bf; UPGT_ASSERT_LOCKED(sc); bf = _upgt_getbuf(sc); if (bf == NULL) DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: stop queue\n", __func__); return (bf); } static struct upgt_data * upgt_gettxbuf(struct upgt_softc *sc) { struct upgt_data *bf; UPGT_ASSERT_LOCKED(sc); bf = upgt_getbuf(sc); if (bf == NULL) return (NULL); bf->addr = upgt_mem_alloc(sc); if (bf->addr == 0) { DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: no free prism memory!\n", __func__); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UPGT_STAT_INC(sc, st_tx_inactive); return (NULL); } return (bf); } static int upgt_tx_start(struct upgt_softc *sc, struct mbuf *m, struct ieee80211_node *ni, struct upgt_data *data) { struct ieee80211vap *vap = ni->ni_vap; int error = 0, len; struct ieee80211_frame *wh; struct ieee80211_key *k; struct upgt_lmac_mem *mem; struct upgt_lmac_tx_desc *txdesc; UPGT_ASSERT_LOCKED(sc); upgt_set_led(sc, UPGT_LED_BLINK); + /* Assign sequence number */ + ieee80211_output_seqno_assign(ni, -1, m); + /* * Software crypto. */ wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { device_printf(sc->sc_dev, "ieee80211_crypto_encap returns NULL.\n"); error = EIO; goto done; } /* in case packet header moved, reset pointer */ wh = mtod(m, struct ieee80211_frame *); } /* Transmit the URB containing the TX data. */ memset(data->buf, 0, MCLBYTES); mem = (struct upgt_lmac_mem *)data->buf; mem->addr = htole32(data->addr); txdesc = (struct upgt_lmac_tx_desc *)(mem + 1); if (IEEE80211_IS_MGMT(wh)) { /* mgmt frames */ txdesc->header1.flags = UPGT_H1_FLAGS_TX_MGMT; /* always send mgmt frames at lowest rate (DS1) */ memset(txdesc->rates, 0x10, sizeof(txdesc->rates)); } else { /* data frames */ txdesc->header1.flags = UPGT_H1_FLAGS_TX_DATA; memcpy(txdesc->rates, sc->sc_cur_rateset, sizeof(txdesc->rates)); } txdesc->header1.type = UPGT_H1_TYPE_TX_DATA; txdesc->header1.len = htole16(m->m_pkthdr.len); txdesc->header2.reqid = htole32(data->addr); txdesc->header2.type = htole16(UPGT_H2_TYPE_TX_ACK_YES); txdesc->header2.flags = htole16(UPGT_H2_FLAGS_TX_ACK_YES); txdesc->type = htole32(UPGT_TX_DESC_TYPE_DATA); txdesc->pad3[0] = UPGT_TX_DESC_PAD3_SIZE; if (ieee80211_radiotap_active_vap(vap)) { struct upgt_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = 0; /* XXX where to get from? */ ieee80211_radiotap_tx(vap, m); } /* copy frame below our TX descriptor header */ m_copydata(m, 0, m->m_pkthdr.len, data->buf + (sizeof(*mem) + sizeof(*txdesc))); /* calculate frame size */ len = sizeof(*mem) + sizeof(*txdesc) + m->m_pkthdr.len; /* we need to align the frame to a 4 byte boundary */ len = (len + 3) & ~3; /* calculate frame checksum */ mem->chksum = upgt_chksum_le((uint32_t *)txdesc, len - sizeof(*mem)); data->ni = ni; data->m = m; data->buflen = len; DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: TX start data sending (%d bytes)\n", __func__, len); KASSERT(len <= MCLBYTES, ("mbuf is small for saving data")); upgt_bulk_tx(sc, data); done: /* * If we don't regulary read the device statistics, the RX queue * will stall. It's strange, but it works, so we keep reading * the statistics here. *shrug* */ if (!(if_getcounter(vap->iv_ifp, IFCOUNTER_OPACKETS) % UPGT_TX_STAT_INTERVAL)) upgt_get_stats(sc); return (error); } static void upgt_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct upgt_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m = NULL; struct upgt_data *data; int8_t nf; int rssi = -1; UPGT_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_rx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); m = upgt_rxeof(xfer, data, &rssi); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) return; STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, MCLBYTES); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ UPGT_UNLOCK(sc); if (m != NULL) { wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); nf = -95; /* XXX */ if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, nf); /* node is no longer needed */ ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, nf); m = NULL; } UPGT_LOCK(sc); upgt_start(sc); break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&sc->sc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto setup; } break; } } static void upgt_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct upgt_softc *sc = usbd_xfer_softc(xfer); struct upgt_data *data; UPGT_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); UPGT_STAT_DEC(sc, st_tx_active); upgt_txeof(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); UPGT_STAT_INC(sc, st_tx_inactive); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_tx_pending); if (data == NULL) { DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: empty pending queue\n", __func__); return; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); UPGT_STAT_DEC(sc, st_tx_pending); STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); UPGT_STAT_INC(sc, st_tx_active); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(xfer); upgt_start(sc); break; default: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; if (data->ni != NULL) { if_inc_counter(data->ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(data->ni); data->ni = NULL; } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static device_method_t upgt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, upgt_match), DEVMETHOD(device_attach, upgt_attach), DEVMETHOD(device_detach, upgt_detach), DEVMETHOD_END }; static driver_t upgt_driver = { .name = "upgt", .methods = upgt_methods, .size = sizeof(struct upgt_softc) }; DRIVER_MODULE(if_upgt, uhub, upgt_driver, NULL, NULL); MODULE_VERSION(if_upgt, 1); MODULE_DEPEND(if_upgt, usb, 1, 1, 1); MODULE_DEPEND(if_upgt, wlan, 1, 1, 1); MODULE_DEPEND(if_upgt, upgtfw_fw, 1, 1, 1); USB_PNP_HOST_INFO(upgt_devs); diff --git a/sys/dev/usb/wlan/if_ural.c b/sys/dev/usb/wlan/if_ural.c index 260d75a9821d..adef924a085c 100644 --- a/sys/dev/usb/wlan/if_ural.c +++ b/sys/dev/usb/wlan/if_ural.c @@ -1,2213 +1,2219 @@ /*- * Copyright (c) 2005, 2006 * Damien Bergamini * * Copyright (c) 2006, 2008 * Hans Petter Selasky * * 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. */ /*- * Ralink Technology RT2500USB chipset driver * http://www.ralinktech.com/ */ #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 #ifdef INET #include #include #include #include #include #endif #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR ural_debug #include #include #include #ifdef USB_DEBUG static int ural_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB ural"); SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RWTUN, &ural_debug, 0, "Debug level"); #endif #define URAL_RSSI(rssi) \ ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) /* various supported device vendors/products */ static const STRUCT_USB_HOST_ID ural_devs[] = { #define URAL_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } URAL_DEV(ASUS, WL167G), URAL_DEV(ASUS, RT2570), URAL_DEV(BELKIN, F5D7050), URAL_DEV(BELKIN, F5D7051), URAL_DEV(CISCOLINKSYS, HU200TS), URAL_DEV(CISCOLINKSYS, WUSB54G), URAL_DEV(CISCOLINKSYS, WUSB54GP), URAL_DEV(CONCEPTRONIC2, C54RU), URAL_DEV(DLINK, DWLG122), URAL_DEV(GIGABYTE, GN54G), URAL_DEV(GIGABYTE, GNWBKG), URAL_DEV(GUILLEMOT, HWGUSB254), URAL_DEV(MELCO, KG54), URAL_DEV(MELCO, KG54AI), URAL_DEV(MELCO, KG54YB), URAL_DEV(MELCO, NINWIFI), URAL_DEV(MSI, RT2570), URAL_DEV(MSI, RT2570_2), URAL_DEV(MSI, RT2570_3), URAL_DEV(NOVATECH, NV902), URAL_DEV(RALINK, RT2570), URAL_DEV(RALINK, RT2570_2), URAL_DEV(RALINK, RT2570_3), URAL_DEV(SIEMENS2, WL54G), URAL_DEV(SMC, 2862WG), URAL_DEV(SPHAIRON, UB801R), URAL_DEV(SURECOM, RT2570), URAL_DEV(VTECH, RT2570), URAL_DEV(ZINWELL, RT2570), #undef URAL_DEV }; static usb_callback_t ural_bulk_read_callback; static usb_callback_t ural_bulk_write_callback; static usb_error_t ural_do_request(struct ural_softc *sc, struct usb_device_request *req, void *data); static struct ieee80211vap *ural_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void ural_vap_delete(struct ieee80211vap *); static void ural_tx_free(struct ural_tx_data *, int); static void ural_setup_tx_list(struct ural_softc *); static void ural_unsetup_tx_list(struct ural_softc *); static int ural_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void ural_setup_tx_desc(struct ural_softc *, struct ural_tx_desc *, uint32_t, int, int); static int ural_tx_bcn(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static int ural_tx_mgt(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static int ural_tx_data(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static int ural_transmit(struct ieee80211com *, struct mbuf *); static void ural_start(struct ural_softc *); static void ural_parent(struct ieee80211com *); static void ural_set_testmode(struct ural_softc *); static void ural_eeprom_read(struct ural_softc *, uint16_t, void *, int); static uint16_t ural_read(struct ural_softc *, uint16_t); static void ural_read_multi(struct ural_softc *, uint16_t, void *, int); static void ural_write(struct ural_softc *, uint16_t, uint16_t); static void ural_write_multi(struct ural_softc *, uint16_t, void *, int) __unused; static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t); static uint8_t ural_bbp_read(struct ural_softc *, uint8_t); static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t); static void ural_scan_start(struct ieee80211com *); static void ural_scan_end(struct ieee80211com *); static void ural_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static void ural_set_channel(struct ieee80211com *); static void ural_set_chan(struct ural_softc *, struct ieee80211_channel *); static void ural_disable_rf_tune(struct ural_softc *); static void ural_enable_tsf_sync(struct ural_softc *); static void ural_enable_tsf(struct ural_softc *); static void ural_update_slot(struct ural_softc *); static void ural_set_txpreamble(struct ural_softc *); static void ural_set_basicrates(struct ural_softc *, const struct ieee80211_channel *); static void ural_set_bssid(struct ural_softc *, const uint8_t *); static void ural_set_macaddr(struct ural_softc *, const uint8_t *); static void ural_update_promisc(struct ieee80211com *); static void ural_setpromisc(struct ural_softc *); static const char *ural_get_rf(int); static void ural_read_eeprom(struct ural_softc *); static int ural_bbp_init(struct ural_softc *); static void ural_set_txantenna(struct ural_softc *, int); static void ural_set_rxantenna(struct ural_softc *, int); static void ural_init(struct ural_softc *); static void ural_stop(struct ural_softc *); static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void ural_ratectl_start(struct ural_softc *, struct ieee80211_node *); static void ural_ratectl_timeout(void *); static void ural_ratectl_task(void *, int); static int ural_pause(struct ural_softc *sc, int timeout); /* * Default values for MAC registers; values taken from the reference driver. */ static const struct { uint16_t reg; uint16_t val; } ural_def_mac[] = { { RAL_TXRX_CSR5, 0x8c8d }, { RAL_TXRX_CSR6, 0x8b8a }, { RAL_TXRX_CSR7, 0x8687 }, { RAL_TXRX_CSR8, 0x0085 }, { RAL_MAC_CSR13, 0x1111 }, { RAL_MAC_CSR14, 0x1e11 }, { RAL_TXRX_CSR21, 0xe78f }, { RAL_MAC_CSR9, 0xff1d }, { RAL_MAC_CSR11, 0x0002 }, { RAL_MAC_CSR22, 0x0053 }, { RAL_MAC_CSR15, 0x0000 }, { RAL_MAC_CSR8, RAL_FRAME_SIZE }, { RAL_TXRX_CSR19, 0x0000 }, { RAL_TXRX_CSR18, 0x005a }, { RAL_PHY_CSR2, 0x0000 }, { RAL_TXRX_CSR0, 0x1ec0 }, { RAL_PHY_CSR4, 0x000f } }; /* * Default values for BBP registers; values taken from the reference driver. */ static const struct { uint8_t reg; uint8_t val; } ural_def_bbp[] = { { 3, 0x02 }, { 4, 0x19 }, { 14, 0x1c }, { 15, 0x30 }, { 16, 0xac }, { 17, 0x48 }, { 18, 0x18 }, { 19, 0xff }, { 20, 0x1e }, { 21, 0x08 }, { 22, 0x08 }, { 23, 0x08 }, { 24, 0x80 }, { 25, 0x50 }, { 26, 0x08 }, { 27, 0x23 }, { 30, 0x10 }, { 31, 0x2b }, { 32, 0xb9 }, { 34, 0x12 }, { 35, 0x50 }, { 39, 0xc4 }, { 40, 0x02 }, { 41, 0x60 }, { 53, 0x10 }, { 54, 0x18 }, { 56, 0x08 }, { 57, 0x10 }, { 58, 0x08 }, { 61, 0x60 }, { 62, 0x10 }, { 75, 0xff } }; /* * Default values for RF register R2 indexed by channel numbers. */ static const uint32_t ural_rf2522_r2[] = { 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e }; static const uint32_t ural_rf2523_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2524_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2525_r2[] = { 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 }; static const uint32_t ural_rf2525_hi_r2[] = { 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e }; static const uint32_t ural_rf2525e_r2[] = { 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b }; static const uint32_t ural_rf2526_hi_r2[] = { 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 }; static const uint32_t ural_rf2526_r2[] = { 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d }; /* * For dual-band RF, RF registers R1 and R4 also depend on channel number; * values taken from the reference driver. */ static const struct { uint8_t chan; uint32_t r1; uint32_t r2; uint32_t r4; } ural_rf5222[] = { { 1, 0x08808, 0x0044d, 0x00282 }, { 2, 0x08808, 0x0044e, 0x00282 }, { 3, 0x08808, 0x0044f, 0x00282 }, { 4, 0x08808, 0x00460, 0x00282 }, { 5, 0x08808, 0x00461, 0x00282 }, { 6, 0x08808, 0x00462, 0x00282 }, { 7, 0x08808, 0x00463, 0x00282 }, { 8, 0x08808, 0x00464, 0x00282 }, { 9, 0x08808, 0x00465, 0x00282 }, { 10, 0x08808, 0x00466, 0x00282 }, { 11, 0x08808, 0x00467, 0x00282 }, { 12, 0x08808, 0x00468, 0x00282 }, { 13, 0x08808, 0x00469, 0x00282 }, { 14, 0x08808, 0x0046b, 0x00286 }, { 36, 0x08804, 0x06225, 0x00287 }, { 40, 0x08804, 0x06226, 0x00287 }, { 44, 0x08804, 0x06227, 0x00287 }, { 48, 0x08804, 0x06228, 0x00287 }, { 52, 0x08804, 0x06229, 0x00287 }, { 56, 0x08804, 0x0622a, 0x00287 }, { 60, 0x08804, 0x0622b, 0x00287 }, { 64, 0x08804, 0x0622c, 0x00287 }, { 100, 0x08804, 0x02200, 0x00283 }, { 104, 0x08804, 0x02201, 0x00283 }, { 108, 0x08804, 0x02202, 0x00283 }, { 112, 0x08804, 0x02203, 0x00283 }, { 116, 0x08804, 0x02204, 0x00283 }, { 120, 0x08804, 0x02205, 0x00283 }, { 124, 0x08804, 0x02206, 0x00283 }, { 128, 0x08804, 0x02207, 0x00283 }, { 132, 0x08804, 0x02208, 0x00283 }, { 136, 0x08804, 0x02209, 0x00283 }, { 140, 0x08804, 0x0220a, 0x00283 }, { 149, 0x08808, 0x02429, 0x00281 }, { 153, 0x08808, 0x0242b, 0x00281 }, { 157, 0x08808, 0x0242d, 0x00281 }, { 161, 0x08808, 0x0242f, 0x00281 } }; static const uint8_t ural_chan_5ghz[] = { 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 149, 153, 157, 161 }; static const struct usb_config ural_config[URAL_N_TRANSFER] = { [URAL_BULK_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4), .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = ural_bulk_write_callback, .timeout = 5000, /* ms */ }, [URAL_BULK_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE), .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = ural_bulk_read_callback, }, }; static device_probe_t ural_match; static device_attach_t ural_attach; static device_detach_t ural_detach; static device_method_t ural_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ural_match), DEVMETHOD(device_attach, ural_attach), DEVMETHOD(device_detach, ural_detach), DEVMETHOD_END }; static driver_t ural_driver = { .name = "ural", .methods = ural_methods, .size = sizeof(struct ural_softc), }; DRIVER_MODULE(ural, uhub, ural_driver, NULL, NULL); MODULE_DEPEND(ural, usb, 1, 1, 1); MODULE_DEPEND(ural, wlan, 1, 1, 1); MODULE_VERSION(ural, 1); USB_PNP_HOST_INFO(ural_devs); static int ural_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != 0) return (ENXIO); if (uaa->info.bIfaceIndex != RAL_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa)); } static int ural_attach(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); struct ural_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; uint8_t iface_index; int error; device_set_usb_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF); mbufq_init(&sc->sc_snd, ifqmaxlen); iface_index = RAL_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, ural_config, URAL_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(self, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); goto detach; } RAL_LOCK(sc); /* retrieve RT2570 rev. no */ sc->asic_rev = ural_read(sc, RAL_MAC_CSR0); /* retrieve MAC address and various other things from EEPROM */ ural_read_eeprom(sc); RAL_UNLOCK(sc); device_printf(self, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n", sc->asic_rev, ural_get_rf(sc->rf_rev)); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(self); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; + ic->ic_flags_ext |= IEEE80211_FEXT_SEQNO_OFFLOAD; + ural_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); ieee80211_ifattach(ic); ic->ic_update_promisc = ural_update_promisc; ic->ic_raw_xmit = ural_raw_xmit; ic->ic_scan_start = ural_scan_start; ic->ic_scan_end = ural_scan_end; ic->ic_getradiocaps = ural_getradiocaps; ic->ic_set_channel = ural_set_channel; ic->ic_parent = ural_parent; ic->ic_transmit = ural_transmit; ic->ic_vap_create = ural_vap_create; ic->ic_vap_delete = ural_vap_delete; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RAL_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RAL_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return (0); detach: ural_detach(self); return (ENXIO); /* failure */ } static int ural_detach(device_t self) { struct ural_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; /* prevent further ioctls */ RAL_LOCK(sc); sc->sc_detached = 1; RAL_UNLOCK(sc); /* stop all USB transfers */ usbd_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); /* free TX list, if any */ RAL_LOCK(sc); ural_unsetup_tx_list(sc); RAL_UNLOCK(sc); if (ic->ic_softc == sc) ieee80211_ifdetach(ic); mbufq_drain(&sc->sc_snd); mtx_destroy(&sc->sc_mtx); return (0); } static usb_error_t ural_do_request(struct ural_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0) break; DPRINTFN(1, "Control request failed, %s (retrying)\n", usbd_errstr(err)); if (ural_pause(sc, hz / 100)) break; } return (err); } static struct ieee80211vap * ural_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ural_softc *sc = ic->ic_softc; struct ural_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; uvp = malloc(sizeof(struct ural_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = ural_newstate; usb_callout_init_mtx(&uvp->ratectl_ch, &sc->sc_mtx, 0); TASK_INIT(&uvp->ratectl_task, 0, ural_ratectl_task, uvp); ieee80211_ratectl_init(vap); ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return vap; } static void ural_vap_delete(struct ieee80211vap *vap) { struct ural_vap *uvp = URAL_VAP(vap); struct ieee80211com *ic = vap->iv_ic; usb_callout_drain(&uvp->ratectl_ch); ieee80211_draintask(ic, &uvp->ratectl_task); ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void ural_tx_free(struct ural_tx_data *data, int txerr) { struct ural_softc *sc = data->sc; if (data->m != NULL) { ieee80211_tx_complete(data->ni, data->m, txerr); data->m = NULL; data->ni = NULL; } STAILQ_INSERT_TAIL(&sc->tx_free, data, next); sc->tx_nfree++; } static void ural_setup_tx_list(struct ural_softc *sc) { struct ural_tx_data *data; int i; sc->tx_nfree = 0; STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); for (i = 0; i < RAL_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; data->sc = sc; STAILQ_INSERT_TAIL(&sc->tx_free, data, next); sc->tx_nfree++; } } static void ural_unsetup_tx_list(struct ural_softc *sc) { struct ural_tx_data *data; int i; /* make sure any subsequent use of the queues will fail */ sc->tx_nfree = 0; STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); /* free up all node references and mbufs */ for (i = 0; i < RAL_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; if (data->m != NULL) { m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ural_vap *uvp = URAL_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ural_softc *sc = ic->ic_softc; const struct ieee80211_txparam *tp; struct ieee80211_node *ni; struct mbuf *m; DPRINTF("%s -> %s\n", ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); RAL_LOCK(sc); usb_callout_stop(&uvp->ratectl_ch); switch (nstate) { case IEEE80211_S_INIT: if (vap->iv_state == IEEE80211_S_RUN) { /* abort TSF synchronization */ ural_write(sc, RAL_TXRX_CSR19, 0); /* force tx led to stop blinking */ ural_write(sc, RAL_MAC_CSR20, 0); } break; case IEEE80211_S_RUN: ni = ieee80211_ref_node(vap->iv_bss); if (vap->iv_opmode != IEEE80211_M_MONITOR) { if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) goto fail; ural_update_slot(sc); ural_set_txpreamble(sc); ural_set_basicrates(sc, ic->ic_bsschan); IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); ural_set_bssid(sc, sc->sc_bssid); } if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) { m = ieee80211_beacon_alloc(ni); if (m == NULL) { device_printf(sc->sc_dev, "could not allocate beacon\n"); goto fail; } ieee80211_ref_node(ni); if (ural_tx_bcn(sc, m, ni) != 0) { device_printf(sc->sc_dev, "could not send beacon\n"); goto fail; } } /* make tx led blink on tx (controlled by ASIC) */ ural_write(sc, RAL_MAC_CSR20, 1); if (vap->iv_opmode != IEEE80211_M_MONITOR) ural_enable_tsf_sync(sc); else ural_enable_tsf(sc); /* enable automatic rate adaptation */ /* XXX should use ic_bsschan but not valid until after newstate call below */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) ural_ratectl_start(sc, ni); ieee80211_free_node(ni); break; default: break; } RAL_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); fail: RAL_UNLOCK(sc); IEEE80211_LOCK(ic); ieee80211_free_node(ni); return (-1); } static void ural_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ural_softc *sc = usbd_xfer_softc(xfer); struct ieee80211vap *vap; struct ural_tx_data *data; struct mbuf *m; struct usb_page_cache *pc; int len; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete, %d bytes\n", len); /* free resources */ data = usbd_xfer_get_priv(xfer); ural_tx_free(data, 0); usbd_xfer_set_priv(xfer, NULL); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->tx_q); if (data) { STAILQ_REMOVE_HEAD(&sc->tx_q, next); m = data->m; if (m->m_pkthdr.len > (int)(RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE); } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &data->desc, RAL_TX_DESC_SIZE); usbd_m_copy_in(pc, RAL_TX_DESC_SIZE, m, 0, m->m_pkthdr.len); vap = data->ni->ni_vap; if (ieee80211_radiotap_active_vap(vap)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = data->rate; tap->wt_antenna = sc->tx_ant; ieee80211_radiotap_tx(vap, m); } /* xfer length needs to be a multiple of two! */ len = (RAL_TX_DESC_SIZE + m->m_pkthdr.len + 1) & ~1; if ((len % 64) == 0) len += 2; DPRINTFN(11, "sending frame len=%u xferlen=%u\n", m->m_pkthdr.len, len); usbd_xfer_set_frame_len(xfer, 0, len); usbd_xfer_set_priv(xfer, data); usbd_transfer_submit(xfer); } ural_start(sc); break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usbd_errstr(error)); data = usbd_xfer_get_priv(xfer); if (data != NULL) { ural_tx_free(data, error); usbd_xfer_set_priv(xfer, NULL); } if (error == USB_ERR_STALLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } if (error == USB_ERR_TIMEOUT) device_printf(sc->sc_dev, "device timeout\n"); break; } } static void ural_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ural_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct mbuf *m = NULL; struct usb_page_cache *pc; uint32_t flags; int8_t rssi = 0, nf = 0; int len; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(15, "rx done, actlen=%d\n", len); if (len < (int)(RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { DPRINTF("%s: xfer too short %d\n", device_get_nameunit(sc->sc_dev), len); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } len -= RAL_RX_DESC_SIZE; /* rx descriptor is located at the end */ pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, len, &sc->sc_rx_desc, RAL_RX_DESC_SIZE); rssi = URAL_RSSI(sc->sc_rx_desc.rssi); nf = RAL_NOISE_FLOOR; flags = le32toh(sc->sc_rx_desc.flags); if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) { /* * This should not happen since we did not * request to receive those frames when we * filled RAL_TXRX_CSR2: */ DPRINTFN(5, "PHY or CRC error\n"); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } usbd_copy_out(pc, 0, mtod(m, uint8_t *), len); /* finalize mbuf */ m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; if (ieee80211_radiotap_active(ic)) { struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; /* XXX set once */ tap->wr_flags = 0; tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, (flags & RAL_RX_OFDM) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_antenna = sc->rx_ant; tap->wr_antsignal = nf + rssi; tap->wr_antnoise = nf; } /* Strip trailing 802.11 MAC FCS. */ m_adj(m, -IEEE80211_CRC_LEN); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ RAL_UNLOCK(sc); if (m) { ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, nf); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, nf); } RAL_LOCK(sc); ural_start(sc); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static uint8_t ural_plcp_signal(int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; } return 0xff; /* XXX unsupported/unknown rate */ } static void ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc, uint32_t flags, int len, int rate) { struct ieee80211com *ic = &sc->sc_ic; uint16_t plcp_length; int remainder; desc->flags = htole32(flags); desc->flags |= htole32(RAL_TX_NEWSEQ); desc->flags |= htole32(len << 16); desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5)); desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame))); /* setup PLCP fields */ desc->plcp_signal = ural_plcp_signal(rate); desc->plcp_service = 4; len += IEEE80211_CRC_LEN; if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) { desc->flags |= htole32(RAL_TX_OFDM); plcp_length = len & 0xfff; desc->plcp_length_hi = plcp_length >> 6; desc->plcp_length_lo = plcp_length & 0x3f; } else { if (rate == 0) rate = 2; /* avoid division by zero */ plcp_length = howmany(16 * len, rate); if (rate == 22) { remainder = (16 * len) % 22; if (remainder != 0 && remainder < 7) desc->plcp_service |= RAL_PLCP_LENGEXT; } desc->plcp_length_hi = plcp_length >> 8; desc->plcp_length_lo = plcp_length & 0xff; if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->plcp_signal |= 0x08; } desc->iv = 0; desc->eiv = 0; } #define RAL_TX_TIMEOUT 5000 static int ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ural_tx_data *data; if (sc->tx_nfree == 0) { m_freem(m0); ieee80211_free_node(ni); return (EIO); } if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { m_freem(m0); ieee80211_free_node(ni); return (ENXIO); } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; data->m = m0; data->ni = ni; data->rate = tp->mgmtrate; ural_setup_tx_desc(sc, &data->desc, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len, tp->mgmtrate); DPRINTFN(10, "sending beacon frame len=%u rate=%u\n", m0->m_pkthdr.len, tp->mgmtrate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return (0); } static int ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { const struct ieee80211_txparam *tp = ni->ni_txparms; struct ieee80211com *ic = ni->ni_ic; struct ural_tx_data *data; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags; uint16_t dur; RAL_LOCK_ASSERT(sc, MA_OWNED); data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; + ieee80211_output_seqno_assign(ni, -1, m0); + wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } wh = mtod(m0, struct ieee80211_frame *); } data->m = m0; data->ni = ni; data->rate = tp->mgmtrate; flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); /* tell hardware to add timestamp for probe responses */ if (IEEE80211_IS_MGMT_PROBE_RESP(wh)) flags |= RAL_TX_TIMESTAMP; } ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, tp->mgmtrate); DPRINTFN(10, "sending mgt frame len=%u rate=%u\n", m0->m_pkthdr.len, tp->mgmtrate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return 0; } static int ural_sendprot(struct ural_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; struct ural_tx_data *data; struct mbuf *mprot; int protrate, flags; mprot = ieee80211_alloc_prot(ni, m, rate, prot); if (mprot == NULL) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); device_printf(sc->sc_dev, "could not allocate mbuf for protection mode %d\n", prot); return ENOBUFS; } protrate = ieee80211_ctl_rate(ic->ic_rt, rate); flags = RAL_TX_RETRY(7); if (prot == IEEE80211_PROT_RTSCTS) flags |= RAL_TX_ACK; data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = mprot; data->ni = ieee80211_ref_node(ni); data->rate = protrate; ural_setup_tx_desc(sc, &data->desc, flags, mprot->m_pkthdr.len, protrate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return 0; } static int ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ural_tx_data *data; uint32_t flags; int error; int rate; RAL_LOCK_ASSERT(sc, MA_OWNED); KASSERT(params != NULL, ("no raw xmit params")); rate = params->ibp_rate0; if (!ieee80211_isratevalid(ic->ic_rt, rate)) { m_freem(m0); return EINVAL; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RAL_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { error = ural_sendprot(sc, m0, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error || sc->tx_nfree == 0) { m_freem(m0); return ENOBUFS; } flags |= RAL_TX_IFS_SIFS; } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = m0; data->ni = ni; data->rate = rate; /* XXX need to setup descriptor ourself */ ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); DPRINTFN(10, "sending raw frame len=%u rate=%u\n", m0->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return 0; } static int ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ural_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp = ni->ni_txparms; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; int error, rate; RAL_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m0, struct ieee80211_frame *); if (m0->m_flags & M_EAPOL) rate = tp->mgmtrate; else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else { (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = ieee80211_node_get_txrate_dot11rate(ni); } + ieee80211_output_seqno_assign(ni, -1, m0); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { int prot = IEEE80211_PROT_NONE; if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { error = ural_sendprot(sc, m0, ni, prot, rate); if (error || sc->tx_nfree == 0) { m_freem(m0); return ENOBUFS; } flags |= RAL_TX_IFS_SIFS; } } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = m0; data->ni = ni; data->rate = rate; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; flags |= RAL_TX_RETRY(7); dur = ieee80211_ack_duration(ic->ic_rt, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); } ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); DPRINTFN(10, "sending data frame len=%u rate=%u\n", m0->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return 0; } static int ural_transmit(struct ieee80211com *ic, struct mbuf *m) { struct ural_softc *sc = ic->ic_softc; int error; RAL_LOCK(sc); if (!sc->sc_running) { RAL_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { RAL_UNLOCK(sc); return (error); } ural_start(sc); RAL_UNLOCK(sc); return (0); } static void ural_start(struct ural_softc *sc) { struct ieee80211_node *ni; struct mbuf *m; RAL_LOCK_ASSERT(sc, MA_OWNED); if (sc->sc_running == 0) return; while (sc->tx_nfree >= RAL_TX_MINFREE && (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (ural_tx_data(sc, m, ni) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); break; } } } static void ural_parent(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_softc; int startall = 0; RAL_LOCK(sc); if (sc->sc_detached) { RAL_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if (sc->sc_running == 0) { ural_init(sc); startall = 1; } else ural_setpromisc(sc); } else if (sc->sc_running) ural_stop(sc); RAL_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static void ural_set_testmode(struct ural_softc *sc) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_VENDOR_REQUEST; USETW(req.wValue, 4); USETW(req.wIndex, 1); USETW(req.wLength, 0); error = ural_do_request(sc, &req, NULL); if (error != 0) { device_printf(sc->sc_dev, "could not set test mode: %s\n", usbd_errstr(error)); } } static void ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); error = ural_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not read EEPROM: %s\n", usbd_errstr(error)); } } static uint16_t ural_read(struct ural_softc *sc, uint16_t reg) { struct usb_device_request req; usb_error_t error; uint16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, sizeof (uint16_t)); error = ural_do_request(sc, &req, &val); if (error != 0) { device_printf(sc->sc_dev, "could not read MAC register: %s\n", usbd_errstr(error)); return 0; } return le16toh(val); } static void ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = ural_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not read MAC register: %s\n", usbd_errstr(error)); } } static void ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MAC; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); error = ural_do_request(sc, &req, NULL); if (error != 0) { device_printf(sc->sc_dev, "could not write MAC register: %s\n", usbd_errstr(error)); } } static void ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = ural_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not write MAC register: %s\n", usbd_errstr(error)); } } static void ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) { uint16_t tmp; int ntries; for (ntries = 0; ntries < 100; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not write to BBP\n"); return; } tmp = reg << 8 | val; ural_write(sc, RAL_PHY_CSR7, tmp); } static uint8_t ural_bbp_read(struct ural_softc *sc, uint8_t reg) { uint16_t val; int ntries; val = RAL_BBP_WRITE | reg << 8; ural_write(sc, RAL_PHY_CSR7, val); for (ntries = 0; ntries < 100; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not read BBP\n"); return 0; } return ural_read(sc, RAL_PHY_CSR7) & 0xff; } static void ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; int ntries; for (ntries = 0; ntries < 100; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY)) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not write to RF\n"); return; } tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3); ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff); ural_write(sc, RAL_PHY_CSR10, tmp >> 16); /* remember last written value in sc */ sc->rf_regs[reg] = val; DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff); } static void ural_scan_start(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_softc; RAL_LOCK(sc); ural_write(sc, RAL_TXRX_CSR19, 0); ural_set_bssid(sc, ieee80211broadcastaddr); RAL_UNLOCK(sc); } static void ural_scan_end(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_softc; RAL_LOCK(sc); ural_enable_tsf_sync(sc); ural_set_bssid(sc, sc->sc_bssid); RAL_UNLOCK(sc); } static void ural_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { struct ural_softc *sc = ic->ic_softc; uint8_t bands[IEEE80211_MODE_BYTES]; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); if (sc->rf_rev == RAL_RF_5222) { setbit(bands, IEEE80211_MODE_11A); ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, ural_chan_5ghz, nitems(ural_chan_5ghz), bands, 0); } } static void ural_set_channel(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_softc; RAL_LOCK(sc); ural_set_chan(sc, ic->ic_curchan); RAL_UNLOCK(sc); } static void ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) { struct ieee80211com *ic = &sc->sc_ic; uint8_t power, tmp; int i, chan; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return; if (IEEE80211_IS_CHAN_2GHZ(c)) power = min(sc->txpow[chan - 1], 31); else power = 31; /* adjust txpower using ifconfig settings */ power -= (100 - ic->ic_txpowlimit) / 8; DPRINTFN(2, "setting channel to %u, txpower to %u\n", chan, power); switch (sc->rf_rev) { case RAL_RF_2522: ural_rf_write(sc, RAL_RF1, 0x00814); ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); break; case RAL_RF_2523: ural_rf_write(sc, RAL_RF1, 0x08804); ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2524: ural_rf_write(sc, RAL_RF1, 0x0c808); ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525: ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525E: ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); break; case RAL_RF_2526: ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); ural_rf_write(sc, RAL_RF1, 0x08804); ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); break; /* dual-band RF */ case RAL_RF_5222: for (i = 0; ural_rf5222[i].chan != chan; i++); ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); break; } if (ic->ic_opmode != IEEE80211_M_MONITOR && (ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* set Japan filter bit for channel 14 */ tmp = ural_bbp_read(sc, 70); tmp &= ~RAL_JAPAN_FILTER; if (chan == 14) tmp |= RAL_JAPAN_FILTER; ural_bbp_write(sc, 70, tmp); /* clear CRC errors */ ural_read(sc, RAL_STA_CSR0); ural_pause(sc, hz / 100); ural_disable_rf_tune(sc); } /* XXX doesn't belong here */ /* update basic rate set */ ural_set_basicrates(sc, c); /* give the hardware some time to do the switchover */ ural_pause(sc, hz / 100); } /* * Disable RF auto-tuning. */ static void ural_disable_rf_tune(struct ural_softc *sc) { uint32_t tmp; if (sc->rf_rev != RAL_RF_2523) { tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; ural_rf_write(sc, RAL_RF1, tmp); } tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; ural_rf_write(sc, RAL_RF3, tmp); DPRINTFN(2, "disabling RF autotune\n"); } /* * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF * synchronization. */ static void ural_enable_tsf_sync(struct ural_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint16_t logcwmin, preload, tmp; /* first, disable TSF synchronization */ ural_write(sc, RAL_TXRX_CSR19, 0); tmp = (16 * vap->iv_bss->ni_intval) << 4; ural_write(sc, RAL_TXRX_CSR18, tmp); logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; tmp = logcwmin << 12 | preload; ural_write(sc, RAL_TXRX_CSR20, tmp); /* finally, enable TSF synchronization */ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; if (ic->ic_opmode == IEEE80211_M_STA) tmp |= RAL_ENABLE_TSF_SYNC(1); else tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; ural_write(sc, RAL_TXRX_CSR19, tmp); DPRINTF("enabling TSF synchronization\n"); } static void ural_enable_tsf(struct ural_softc *sc) { /* first, disable TSF synchronization */ ural_write(sc, RAL_TXRX_CSR19, 0); ural_write(sc, RAL_TXRX_CSR19, RAL_ENABLE_TSF | RAL_ENABLE_TSF_SYNC(2)); } #define RAL_RXTX_TURNAROUND 5 /* us */ static void ural_update_slot(struct ural_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint16_t slottime, sifs, eifs; slottime = IEEE80211_GET_SLOTTIME(ic); /* * These settings may sound a bit inconsistent but this is what the * reference driver does. */ if (ic->ic_curmode == IEEE80211_MODE_11B) { sifs = 16 - RAL_RXTX_TURNAROUND; eifs = 364; } else { sifs = 10 - RAL_RXTX_TURNAROUND; eifs = 64; } ural_write(sc, RAL_MAC_CSR10, slottime); ural_write(sc, RAL_MAC_CSR11, sifs); ural_write(sc, RAL_MAC_CSR12, eifs); } static void ural_set_txpreamble(struct ural_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint16_t tmp; tmp = ural_read(sc, RAL_TXRX_CSR10); tmp &= ~RAL_SHORT_PREAMBLE; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RAL_SHORT_PREAMBLE; ural_write(sc, RAL_TXRX_CSR10, tmp); } static void ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c) { /* XXX wrong, take from rate set */ /* update basic rate set */ if (IEEE80211_IS_CHAN_5GHZ(c)) { /* 11a basic rates: 6, 12, 24Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x150); } else if (IEEE80211_IS_CHAN_ANYG(c)) { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x15f); } else { /* 11b basic rates: 1, 2Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x3); } } static void ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid) { uint16_t tmp; tmp = bssid[0] | bssid[1] << 8; ural_write(sc, RAL_MAC_CSR5, tmp); tmp = bssid[2] | bssid[3] << 8; ural_write(sc, RAL_MAC_CSR6, tmp); tmp = bssid[4] | bssid[5] << 8; ural_write(sc, RAL_MAC_CSR7, tmp); DPRINTF("setting BSSID to %6D\n", bssid, ":"); } static void ural_set_macaddr(struct ural_softc *sc, const uint8_t *addr) { uint16_t tmp; tmp = addr[0] | addr[1] << 8; ural_write(sc, RAL_MAC_CSR2, tmp); tmp = addr[2] | addr[3] << 8; ural_write(sc, RAL_MAC_CSR3, tmp); tmp = addr[4] | addr[5] << 8; ural_write(sc, RAL_MAC_CSR4, tmp); DPRINTF("setting MAC address to %6D\n", addr, ":"); } static void ural_setpromisc(struct ural_softc *sc) { uint32_t tmp; tmp = ural_read(sc, RAL_TXRX_CSR2); tmp &= ~RAL_DROP_NOT_TO_ME; if (sc->sc_ic.ic_promisc == 0) tmp |= RAL_DROP_NOT_TO_ME; ural_write(sc, RAL_TXRX_CSR2, tmp); DPRINTF("%s promiscuous mode\n", sc->sc_ic.ic_promisc ? "entering" : "leaving"); } static void ural_update_promisc(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_softc; RAL_LOCK(sc); if (sc->sc_running) ural_setpromisc(sc); RAL_UNLOCK(sc); } static const char * ural_get_rf(int rev) { switch (rev) { case RAL_RF_2522: return "RT2522"; case RAL_RF_2523: return "RT2523"; case RAL_RF_2524: return "RT2524"; case RAL_RF_2525: return "RT2525"; case RAL_RF_2525E: return "RT2525e"; case RAL_RF_2526: return "RT2526"; case RAL_RF_5222: return "RT5222"; default: return "unknown"; } } static void ural_read_eeprom(struct ural_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint16_t val; ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); val = le16toh(val); sc->rf_rev = (val >> 11) & 0x7; sc->hw_radio = (val >> 10) & 0x1; sc->led_mode = (val >> 6) & 0x7; sc->rx_ant = (val >> 4) & 0x3; sc->tx_ant = (val >> 2) & 0x3; sc->nb_ant = val & 0x3; /* read MAC address */ ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, ic->ic_macaddr, 6); /* read default values for BBP registers */ ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); /* read Tx power for all b/g channels */ ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14); } static int ural_bbp_init(struct ural_softc *sc) { int i, ntries; /* wait for BBP to be ready */ for (ntries = 0; ntries < 100; ntries++) { if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP\n"); return EIO; } /* initialize BBP registers to default values */ for (i = 0; i < nitems(ural_def_bbp); i++) ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); #if 0 /* initialize BBP registers to values stored in EEPROM */ for (i = 0; i < 16; i++) { if (sc->bbp_prom[i].reg == 0xff) continue; ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); } #endif return 0; } static void ural_set_txantenna(struct ural_softc *sc, int antenna) { uint16_t tmp; uint8_t tx; tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; if (antenna == 1) tx |= RAL_BBP_ANTA; else if (antenna == 2) tx |= RAL_BBP_ANTB; else tx |= RAL_BBP_DIVERSITY; /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 || sc->rf_rev == RAL_RF_5222) tx |= RAL_BBP_FLIPIQ; ural_bbp_write(sc, RAL_BBP_TX, tx); /* update values in PHY_CSR5 and PHY_CSR6 */ tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7; ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7; ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); } static void ural_set_rxantenna(struct ural_softc *sc, int antenna) { uint8_t rx; rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; if (antenna == 1) rx |= RAL_BBP_ANTA; else if (antenna == 2) rx |= RAL_BBP_ANTB; else rx |= RAL_BBP_DIVERSITY; /* need to force no I/Q flip for RF 2525e and 2526 */ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526) rx &= ~RAL_BBP_FLIPIQ; ural_bbp_write(sc, RAL_BBP_RX, rx); } static void ural_init(struct ural_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint16_t tmp; int i, ntries; RAL_LOCK_ASSERT(sc, MA_OWNED); ural_set_testmode(sc); ural_write(sc, 0x308, 0x00f0); /* XXX magic */ ural_stop(sc); /* initialize MAC registers to default values */ for (i = 0; i < nitems(ural_def_mac); i++) ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); /* wait for BBP and RF to wake up (this can take a long time!) */ for (ntries = 0; ntries < 100; ntries++) { tmp = ural_read(sc, RAL_MAC_CSR17); if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == (RAL_BBP_AWAKE | RAL_RF_AWAKE)) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP/RF to wakeup\n"); goto fail; } /* we're ready! */ ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); /* set basic rate set (will be updated later) */ ural_write(sc, RAL_TXRX_CSR11, 0x15f); if (ural_bbp_init(sc) != 0) goto fail; ural_set_chan(sc, ic->ic_curchan); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); ural_set_txantenna(sc, sc->tx_ant); ural_set_rxantenna(sc, sc->rx_ant); ural_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); /* * Allocate Tx and Rx xfer queues. */ ural_setup_tx_list(sc); /* kick Rx */ tmp = RAL_DROP_PHY | RAL_DROP_CRC; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION; if (ic->ic_opmode != IEEE80211_M_HOSTAP) tmp |= RAL_DROP_TODS; if (ic->ic_promisc == 0) tmp |= RAL_DROP_NOT_TO_ME; } ural_write(sc, RAL_TXRX_CSR2, tmp); sc->sc_running = 1; usbd_xfer_set_stall(sc->sc_xfer[URAL_BULK_WR]); usbd_transfer_start(sc->sc_xfer[URAL_BULK_RD]); return; fail: ural_stop(sc); } static void ural_stop(struct ural_softc *sc) { RAL_LOCK_ASSERT(sc, MA_OWNED); sc->sc_running = 0; /* * Drain all the transfers, if not already drained: */ RAL_UNLOCK(sc); usbd_transfer_drain(sc->sc_xfer[URAL_BULK_WR]); usbd_transfer_drain(sc->sc_xfer[URAL_BULK_RD]); RAL_LOCK(sc); ural_unsetup_tx_list(sc); /* disable Rx */ ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); /* reset ASIC and BBP (but won't reset MAC registers!) */ ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); /* wait a little */ ural_pause(sc, hz / 10); ural_write(sc, RAL_MAC_CSR1, 0); /* wait a little */ ural_pause(sc, hz / 10); } static int ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ural_softc *sc = ic->ic_softc; RAL_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!sc->sc_running) { RAL_UNLOCK(sc); m_freem(m); return ENETDOWN; } if (sc->tx_nfree < RAL_TX_MINFREE) { RAL_UNLOCK(sc); m_freem(m); return EIO; } if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ if (ural_tx_mgt(sc, m, ni) != 0) goto bad; } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ if (ural_tx_raw(sc, m, ni, params) != 0) goto bad; } RAL_UNLOCK(sc); return 0; bad: RAL_UNLOCK(sc); return EIO; /* XXX */ } static void ural_ratectl_start(struct ural_softc *sc, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ural_vap *uvp = URAL_VAP(vap); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp); } static void ural_ratectl_timeout(void *arg) { struct ural_vap *uvp = arg; struct ieee80211vap *vap = &uvp->vap; struct ieee80211com *ic = vap->iv_ic; ieee80211_runtask(ic, &uvp->ratectl_task); } static void ural_ratectl_task(void *arg, int pending) { struct ural_vap *uvp = arg; struct ieee80211vap *vap = &uvp->vap; struct ural_softc *sc = vap->iv_ic->ic_softc; struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; int fail; RAL_LOCK(sc); /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta)); txs->flags = IEEE80211_RATECTL_TX_STATS_RETRIES; txs->nsuccess = sc->sta[7] + /* TX ok w/o retry */ sc->sta[8]; /* TX ok w/ retry */ fail = sc->sta[9]; /* TX retry-fail count */ txs->nframes = txs->nsuccess + fail; /* XXX fail * maxretry */ txs->nretries = sc->sta[8] + fail; ieee80211_ratectl_tx_update(vap, txs); /* count TX retry-fail as Tx errors */ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, fail); usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp); RAL_UNLOCK(sc); } static int ural_pause(struct ural_softc *sc, int timeout) { usb_pause_mtx(&sc->sc_mtx, timeout); return (0); } diff --git a/sys/dev/usb/wlan/if_urtw.c b/sys/dev/usb/wlan/if_urtw.c index 439faeefc408..86cf4c653ae7 100644 --- a/sys/dev/usb/wlan/if_urtw.c +++ b/sys/dev/usb/wlan/if_urtw.c @@ -1,4430 +1,4436 @@ /*- * Copyright (c) 2008 Weongyo Jeong * * 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 "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #include #include #include #include #include #include "usbdevs.h" #include #include /* copy some rate indices from if_rtwn_ridx.h */ #define URTW_RIDX_CCK5 2 #define URTW_RIDX_CCK11 3 #define URTW_RIDX_OFDM6 4 #define URTW_RIDX_OFDM24 8 static SYSCTL_NODE(_hw_usb, OID_AUTO, urtw, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB Realtek 8187L"); #ifdef URTW_DEBUG int urtw_debug = 0; SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RWTUN, &urtw_debug, 0, "control debugging printfs"); enum { URTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ URTW_DEBUG_RECV = 0x00000002, /* basic recv operation */ URTW_DEBUG_RESET = 0x00000004, /* reset processing */ URTW_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */ URTW_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */ URTW_DEBUG_STATE = 0x00000020, /* 802.11 state transitions */ URTW_DEBUG_STAT = 0x00000040, /* statistic */ URTW_DEBUG_INIT = 0x00000080, /* initialization of dev */ URTW_DEBUG_TXSTATUS = 0x00000100, /* tx status */ URTW_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) #endif static int urtw_preamble_mode = URTW_PREAMBLE_MODE_LONG; SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RWTUN, &urtw_preamble_mode, 0, "set the preable mode (long or short)"); /* recognized device vendors/products */ #define urtw_lookup(v, p) \ ((const struct urtw_type *)usb_lookup(urtw_devs, v, p)) #define URTW_DEV_B(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187B) } #define URTW_DEV_L(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187L) } #define URTW_REV_RTL8187B 0 #define URTW_REV_RTL8187L 1 static const STRUCT_USB_HOST_ID urtw_devs[] = { URTW_DEV_B(NETGEAR, WG111V3), URTW_DEV_B(REALTEK, RTL8187B_0), URTW_DEV_B(REALTEK, RTL8187B_1), URTW_DEV_B(REALTEK, RTL8187B_2), URTW_DEV_B(SITECOMEU, WL168V4), URTW_DEV_L(ASUS, P5B_WIFI), URTW_DEV_L(BELKIN, F5D7050E), URTW_DEV_L(LINKSYS4, WUSB54GCV2), URTW_DEV_L(NETGEAR, WG111V2), URTW_DEV_L(REALTEK, RTL8187), URTW_DEV_L(SITECOMEU, WL168V1), URTW_DEV_L(SURECOM, EP9001G2A), { USB_VPI(USB_VENDOR_OVISLINK, 0x8187, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_DICKSMITH, 0x9401, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_HP, 0xca02, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_LOGITEC, 0x010c, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_NETGEAR, 0x6100, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_SPHAIRON, 0x0150, URTW_REV_RTL8187L) }, { USB_VPI(USB_VENDOR_QCOM, 0x6232, URTW_REV_RTL8187L) }, #undef URTW_DEV_L #undef URTW_DEV_B }; #define urtw_read8_m(sc, val, data) do { \ error = urtw_read8_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write8_m(sc, val, data) do { \ error = urtw_write8_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read16_m(sc, val, data) do { \ error = urtw_read16_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write16_m(sc, val, data) do { \ error = urtw_write16_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read32_m(sc, val, data) do { \ error = urtw_read32_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write32_m(sc, val, data) do { \ error = urtw_write32_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_8187_write_phy_ofdm(sc, val, data) do { \ error = urtw_8187_write_phy_ofdm_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_8187_write_phy_cck(sc, val, data) do { \ error = urtw_8187_write_phy_cck_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_8225_write(sc, val, data) do { \ error = urtw_8225_write_c(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) struct urtw_pair { uint32_t reg; uint32_t val; }; static uint8_t urtw_8225_agc[] = { 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static uint8_t urtw_8225z2_agc[] = { 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 }; static uint32_t urtw_8225_channel[] = { 0x0000, /* dummy channel 0 */ 0x085c, /* 1 */ 0x08dc, /* 2 */ 0x095c, /* 3 */ 0x09dc, /* 4 */ 0x0a5c, /* 5 */ 0x0adc, /* 6 */ 0x0b5c, /* 7 */ 0x0bdc, /* 8 */ 0x0c5c, /* 9 */ 0x0cdc, /* 10 */ 0x0d5c, /* 11 */ 0x0ddc, /* 12 */ 0x0e5c, /* 13 */ 0x0f72, /* 14 */ }; static uint8_t urtw_8225_gain[] = { 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ }; static struct urtw_pair urtw_8225_rf_part1[] = { { 0x00, 0x0067 }, { 0x01, 0x0fe0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, { 0x04, 0x0486 }, { 0x05, 0x0bc0 }, { 0x06, 0x0ae6 }, { 0x07, 0x082a }, { 0x08, 0x001f }, { 0x09, 0x0334 }, { 0x0a, 0x0fd4 }, { 0x0b, 0x0391 }, { 0x0c, 0x0050 }, { 0x0d, 0x06db }, { 0x0e, 0x0029 }, { 0x0f, 0x0914 }, }; static struct urtw_pair urtw_8225_rf_part2[] = { { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x09 }, { 0x0b, 0x80 }, { 0x0c, 0x01 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 }, { 0x11, 0x06 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x76 }, { 0x1c, 0x04 }, { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x27 }, { 0x22, 0x16 }, { 0x24, 0x46 }, { 0x25, 0x20 }, { 0x26, 0x90 }, { 0x27, 0x88 } }; static struct urtw_pair urtw_8225_rf_part3[] = { { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x10, 0x9b }, { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x40, 0x86 }, { 0x41, 0x8d }, { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x1f }, { 0x45, 0x1e }, { 0x46, 0x1a }, { 0x47, 0x15 }, { 0x48, 0x10 }, { 0x49, 0x0a }, { 0x4a, 0x05 }, { 0x4b, 0x02 }, { 0x4c, 0x05 } }; static uint16_t urtw_8225_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb }; static uint8_t urtw_8225_threshold[] = { 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd, }; static uint8_t urtw_8225_tx_gain_cck_ofdm[] = { 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e }; static uint8_t urtw_8225_txpwr_cck[] = { 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 }; static uint8_t urtw_8225_txpwr_cck_ch14[] = { 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 }; static uint8_t urtw_8225_txpwr_ofdm[]={ 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 }; static uint8_t urtw_8225v2_gain_bg[]={ 0x23, 0x15, 0xa5, /* -82-1dbm */ 0x23, 0x15, 0xb5, /* -82-2dbm */ 0x23, 0x15, 0xc5, /* -82-3dbm */ 0x33, 0x15, 0xc5, /* -78dbm */ 0x43, 0x15, 0xc5, /* -74dbm */ 0x53, 0x15, 0xc5, /* -70dbm */ 0x63, 0x15, 0xc5, /* -66dbm */ }; static struct urtw_pair urtw_8225v2_rf_part1[] = { { 0x00, 0x02bf }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } }; static struct urtw_pair urtw_8225v2b_rf_part0[] = { { 0x00, 0x00b7 }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } }; static struct urtw_pair urtw_8225v2b_rf_part1[] = { {0x0f0, 0x32}, {0x0f1, 0x32}, {0x0f2, 0x00}, {0x0f3, 0x00}, {0x0f4, 0x32}, {0x0f5, 0x43}, {0x0f6, 0x00}, {0x0f7, 0x00}, {0x0f8, 0x46}, {0x0f9, 0xa4}, {0x0fa, 0x00}, {0x0fb, 0x00}, {0x0fc, 0x96}, {0x0fd, 0xa4}, {0x0fe, 0x00}, {0x0ff, 0x00}, {0x158, 0x4b}, {0x159, 0x00}, {0x15a, 0x4b}, {0x15b, 0x00}, {0x160, 0x4b}, {0x161, 0x09}, {0x162, 0x4b}, {0x163, 0x09}, {0x1ce, 0x0f}, {0x1cf, 0x00}, {0x1e0, 0xff}, {0x1e1, 0x0f}, {0x1e2, 0x00}, {0x1f0, 0x4e}, {0x1f1, 0x01}, {0x1f2, 0x02}, {0x1f3, 0x03}, {0x1f4, 0x04}, {0x1f5, 0x05}, {0x1f6, 0x06}, {0x1f7, 0x07}, {0x1f8, 0x08}, {0x24e, 0x00}, {0x20c, 0x04}, {0x221, 0x61}, {0x222, 0x68}, {0x223, 0x6f}, {0x224, 0x76}, {0x225, 0x7d}, {0x226, 0x84}, {0x227, 0x8d}, {0x24d, 0x08}, {0x250, 0x05}, {0x251, 0xf5}, {0x252, 0x04}, {0x253, 0xa0}, {0x254, 0x1f}, {0x255, 0x23}, {0x256, 0x45}, {0x257, 0x67}, {0x258, 0x08}, {0x259, 0x08}, {0x25a, 0x08}, {0x25b, 0x08}, {0x260, 0x08}, {0x261, 0x08}, {0x262, 0x08}, {0x263, 0x08}, {0x264, 0xcf}, {0x272, 0x56}, {0x273, 0x9a}, {0x034, 0xf0}, {0x035, 0x0f}, {0x05b, 0x40}, {0x084, 0x88}, {0x085, 0x24}, {0x088, 0x54}, {0x08b, 0xb8}, {0x08c, 0x07}, {0x08d, 0x00}, {0x094, 0x1b}, {0x095, 0x12}, {0x096, 0x00}, {0x097, 0x06}, {0x09d, 0x1a}, {0x09f, 0x10}, {0x0b4, 0x22}, {0x0be, 0x80}, {0x0db, 0x00}, {0x0ee, 0x00}, {0x091, 0x03}, {0x24c, 0x00}, {0x39f, 0x00}, {0x08c, 0x01}, {0x08d, 0x10}, {0x08e, 0x08}, {0x08f, 0x00} }; static struct urtw_pair urtw_8225v2_rf_part2[] = { { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x08 }, { 0x0b, 0x80 }, { 0x0c, 0x01 }, { 0x0d, 0x43 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 }, { 0x11, 0x07 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x15 }, { 0x1c, 0x04 }, { 0x1d, 0xc5 }, { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x17 }, { 0x22, 0x16 }, { 0x23, 0x80 }, { 0x24, 0x46 }, { 0x25, 0x00 }, { 0x26, 0x90 }, { 0x27, 0x88 } }; static struct urtw_pair urtw_8225v2b_rf_part2[] = { { 0x00, 0x10 }, { 0x01, 0x0d }, { 0x02, 0x01 }, { 0x03, 0x00 }, { 0x04, 0x14 }, { 0x05, 0xfb }, { 0x06, 0xfb }, { 0x07, 0x60 }, { 0x08, 0x00 }, { 0x09, 0x60 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x0c, 0x00 }, { 0x0d, 0x5c }, { 0x0e, 0x00 }, { 0x0f, 0x00 }, { 0x10, 0x40 }, { 0x11, 0x00 }, { 0x12, 0x40 }, { 0x13, 0x00 }, { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0xa8 }, { 0x17, 0x26 }, { 0x18, 0x32 }, { 0x19, 0x33 }, { 0x1a, 0x07 }, { 0x1b, 0xa5 }, { 0x1c, 0x6f }, { 0x1d, 0x55 }, { 0x1e, 0xc8 }, { 0x1f, 0xb3 }, { 0x20, 0x0a }, { 0x21, 0xe1 }, { 0x22, 0x2C }, { 0x23, 0x8a }, { 0x24, 0x86 }, { 0x25, 0x83 }, { 0x26, 0x34 }, { 0x27, 0x0f }, { 0x28, 0x4f }, { 0x29, 0x24 }, { 0x2a, 0x6f }, { 0x2b, 0xc2 }, { 0x2c, 0x6b }, { 0x2d, 0x40 }, { 0x2e, 0x80 }, { 0x2f, 0x00 }, { 0x30, 0xc0 }, { 0x31, 0xc1 }, { 0x32, 0x58 }, { 0x33, 0xf1 }, { 0x34, 0x00 }, { 0x35, 0xe4 }, { 0x36, 0x90 }, { 0x37, 0x3e }, { 0x38, 0x6d }, { 0x39, 0x3c }, { 0x3a, 0xfb }, { 0x3b, 0x07 } }; static struct urtw_pair urtw_8225v2_rf_part3[] = { { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x09, 0x11 }, { 0x0a, 0x17 }, { 0x0b, 0x11 }, { 0x10, 0x9b }, { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x1d, 0x00 }, { 0x40, 0x86 }, { 0x41, 0x9d }, { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x36 }, { 0x45, 0x35 }, { 0x46, 0x2e }, { 0x47, 0x25 }, { 0x48, 0x1c }, { 0x49, 0x12 }, { 0x4a, 0x09 }, { 0x4b, 0x04 }, { 0x4c, 0x05 } }; static uint16_t urtw_8225v2_rxgain[] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb }; static uint16_t urtw_8225v2b_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb }; static uint8_t urtw_8225v2_tx_gain_cck_ofdm[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, }; static uint8_t urtw_8225v2_txpwr_cck[] = { 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 }; static uint8_t urtw_8225v2_txpwr_cck_ch14[] = { 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 }; static uint8_t urtw_8225v2b_txpwr_cck[] = { 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 }; static uint8_t urtw_8225v2b_txpwr_cck_ch14[] = { 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 }; static struct urtw_pair urtw_ratetable[] = { { 2, 0 }, { 4, 1 }, { 11, 2 }, { 12, 4 }, { 18, 5 }, { 22, 3 }, { 24, 6 }, { 36, 7 }, { 48, 8 }, { 72, 9 }, { 96, 10 }, { 108, 11 } }; #if 0 static const uint8_t urtw_8187b_reg_table[][3] = { { 0xf0, 0x32, 0 }, { 0xf1, 0x32, 0 }, { 0xf2, 0x00, 0 }, { 0xf3, 0x00, 0 }, { 0xf4, 0x32, 0 }, { 0xf5, 0x43, 0 }, { 0xf6, 0x00, 0 }, { 0xf7, 0x00, 0 }, { 0xf8, 0x46, 0 }, { 0xf9, 0xa4, 0 }, { 0xfa, 0x00, 0 }, { 0xfb, 0x00, 0 }, { 0xfc, 0x96, 0 }, { 0xfd, 0xa4, 0 }, { 0xfe, 0x00, 0 }, { 0xff, 0x00, 0 }, { 0x58, 0x4b, 1 }, { 0x59, 0x00, 1 }, { 0x5a, 0x4b, 1 }, { 0x5b, 0x00, 1 }, { 0x60, 0x4b, 1 }, { 0x61, 0x09, 1 }, { 0x62, 0x4b, 1 }, { 0x63, 0x09, 1 }, { 0xce, 0x0f, 1 }, { 0xcf, 0x00, 1 }, { 0xe0, 0xff, 1 }, { 0xe1, 0x0f, 1 }, { 0xe2, 0x00, 1 }, { 0xf0, 0x4e, 1 }, { 0xf1, 0x01, 1 }, { 0xf2, 0x02, 1 }, { 0xf3, 0x03, 1 }, { 0xf4, 0x04, 1 }, { 0xf5, 0x05, 1 }, { 0xf6, 0x06, 1 }, { 0xf7, 0x07, 1 }, { 0xf8, 0x08, 1 }, { 0x4e, 0x00, 2 }, { 0x0c, 0x04, 2 }, { 0x21, 0x61, 2 }, { 0x22, 0x68, 2 }, { 0x23, 0x6f, 2 }, { 0x24, 0x76, 2 }, { 0x25, 0x7d, 2 }, { 0x26, 0x84, 2 }, { 0x27, 0x8d, 2 }, { 0x4d, 0x08, 2 }, { 0x50, 0x05, 2 }, { 0x51, 0xf5, 2 }, { 0x52, 0x04, 2 }, { 0x53, 0xa0, 2 }, { 0x54, 0x1f, 2 }, { 0x55, 0x23, 2 }, { 0x56, 0x45, 2 }, { 0x57, 0x67, 2 }, { 0x58, 0x08, 2 }, { 0x59, 0x08, 2 }, { 0x5a, 0x08, 2 }, { 0x5b, 0x08, 2 }, { 0x60, 0x08, 2 }, { 0x61, 0x08, 2 }, { 0x62, 0x08, 2 }, { 0x63, 0x08, 2 }, { 0x64, 0xcf, 2 }, { 0x72, 0x56, 2 }, { 0x73, 0x9a, 2 }, { 0x34, 0xf0, 0 }, { 0x35, 0x0f, 0 }, { 0x5b, 0x40, 0 }, { 0x84, 0x88, 0 }, { 0x85, 0x24, 0 }, { 0x88, 0x54, 0 }, { 0x8b, 0xb8, 0 }, { 0x8c, 0x07, 0 }, { 0x8d, 0x00, 0 }, { 0x94, 0x1b, 0 }, { 0x95, 0x12, 0 }, { 0x96, 0x00, 0 }, { 0x97, 0x06, 0 }, { 0x9d, 0x1a, 0 }, { 0x9f, 0x10, 0 }, { 0xb4, 0x22, 0 }, { 0xbe, 0x80, 0 }, { 0xdb, 0x00, 0 }, { 0xee, 0x00, 0 }, { 0x91, 0x03, 0 }, { 0x4c, 0x00, 2 }, { 0x9f, 0x00, 3 }, { 0x8c, 0x01, 0 }, { 0x8d, 0x10, 0 }, { 0x8e, 0x08, 0 }, { 0x8f, 0x00, 0 } }; #endif static usb_callback_t urtw_bulk_rx_callback; static usb_callback_t urtw_bulk_tx_callback; static usb_callback_t urtw_bulk_tx_status_callback; static const struct usb_config urtw_8187b_usbconfig[URTW_8187B_N_XFERS] = { [URTW_8187B_BULK_RX] = { .type = UE_BULK, .endpoint = 0x83, .direction = UE_DIR_IN, .bufsize = MCLBYTES, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = urtw_bulk_rx_callback }, [URTW_8187B_BULK_TX_STATUS] = { .type = UE_BULK, .endpoint = 0x89, .direction = UE_DIR_IN, .bufsize = sizeof(uint64_t), .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = urtw_bulk_tx_status_callback }, [URTW_8187B_BULK_TX_BE] = { .type = UE_BULK, .endpoint = URTW_8187B_TXPIPE_BE, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE * URTW_TX_DATA_LIST_COUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187B_BULK_TX_BK] = { .type = UE_BULK, .endpoint = URTW_8187B_TXPIPE_BK, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187B_BULK_TX_VI] = { .type = UE_BULK, .endpoint = URTW_8187B_TXPIPE_VI, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187B_BULK_TX_VO] = { .type = UE_BULK, .endpoint = URTW_8187B_TXPIPE_VO, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187B_BULK_TX_EP12] = { .type = UE_BULK, .endpoint = 0xc, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT } }; static const struct usb_config urtw_8187l_usbconfig[URTW_8187L_N_XFERS] = { [URTW_8187L_BULK_RX] = { .type = UE_BULK, .endpoint = 0x81, .direction = UE_DIR_IN, .bufsize = MCLBYTES, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = urtw_bulk_rx_callback }, [URTW_8187L_BULK_TX_LOW] = { .type = UE_BULK, .endpoint = 0x2, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE * URTW_TX_DATA_LIST_COUNT, .flags = { .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, [URTW_8187L_BULK_TX_NORMAL] = { .type = UE_BULK, .endpoint = 0x3, .direction = UE_DIR_OUT, .bufsize = URTW_TX_MAXSIZE, .flags = { .ext_buffer = 1, .force_short_xfer = 1, .pipe_bof = 1, }, .callback = urtw_bulk_tx_callback, .timeout = URTW_DATA_TIMEOUT }, }; static struct ieee80211vap *urtw_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void urtw_vap_delete(struct ieee80211vap *); static void urtw_init(struct urtw_softc *); static void urtw_stop(struct urtw_softc *); static void urtw_parent(struct ieee80211com *); static int urtw_transmit(struct ieee80211com *, struct mbuf *); static void urtw_start(struct urtw_softc *); static int urtw_alloc_rx_data_list(struct urtw_softc *); static int urtw_alloc_tx_data_list(struct urtw_softc *); static int urtw_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void urtw_scan_start(struct ieee80211com *); static void urtw_scan_end(struct ieee80211com *); static void urtw_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static void urtw_set_channel(struct ieee80211com *); static void urtw_update_promisc(struct ieee80211com *); static void urtw_update_mcast(struct ieee80211com *); static int urtw_tx_start(struct urtw_softc *, struct ieee80211_node *, struct mbuf *, struct urtw_data *, int); static int urtw_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void urtw_led_ch(void *); static void urtw_ledtask(void *, int); static void urtw_watchdog(void *); static void urtw_set_multi(void *); static int urtw_isbmode(uint16_t); static uint16_t urtw_rtl2rate(uint32_t); static usb_error_t urtw_set_rate(struct urtw_softc *); static usb_error_t urtw_update_msr(struct urtw_softc *); static usb_error_t urtw_read8_c(struct urtw_softc *, int, uint8_t *); static usb_error_t urtw_read16_c(struct urtw_softc *, int, uint16_t *); static usb_error_t urtw_read32_c(struct urtw_softc *, int, uint32_t *); static usb_error_t urtw_write8_c(struct urtw_softc *, int, uint8_t); static usb_error_t urtw_write16_c(struct urtw_softc *, int, uint16_t); static usb_error_t urtw_write32_c(struct urtw_softc *, int, uint32_t); static usb_error_t urtw_eprom_cs(struct urtw_softc *, int); static usb_error_t urtw_eprom_ck(struct urtw_softc *); static usb_error_t urtw_eprom_sendbits(struct urtw_softc *, int16_t *, int); static usb_error_t urtw_eprom_read32(struct urtw_softc *, uint32_t, uint32_t *); static usb_error_t urtw_eprom_readbit(struct urtw_softc *, int16_t *); static usb_error_t urtw_eprom_writebit(struct urtw_softc *, int16_t); static usb_error_t urtw_get_macaddr(struct urtw_softc *); static usb_error_t urtw_get_txpwr(struct urtw_softc *); static usb_error_t urtw_get_rfchip(struct urtw_softc *); static usb_error_t urtw_led_init(struct urtw_softc *); static usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *); static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *, uint8_t); static usb_error_t urtw_8187_write_phy(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8225_setgain(struct urtw_softc *, int16_t); static usb_error_t urtw_8225_usb_init(struct urtw_softc *); static usb_error_t urtw_8225_write_c(struct urtw_softc *, uint8_t, uint16_t); static usb_error_t urtw_8225_write_s16(struct urtw_softc *, uint8_t, int, uint16_t *); static usb_error_t urtw_8225_read(struct urtw_softc *, uint8_t, uint32_t *); static usb_error_t urtw_8225_rf_init(struct urtw_softc *); static usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *, int); static usb_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *, int); static usb_error_t urtw_8225_rf_stop(struct urtw_softc *); static usb_error_t urtw_8225v2_rf_init(struct urtw_softc *); static usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int); static usb_error_t urtw_8225v2_setgain(struct urtw_softc *, int16_t); static usb_error_t urtw_8225_isv2(struct urtw_softc *, int *); static usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *); static usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_read8e(struct urtw_softc *, int, uint8_t *); static usb_error_t urtw_write8e(struct urtw_softc *, int, uint8_t); static usb_error_t urtw_8180_set_anaparam(struct urtw_softc *, uint32_t); static usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t); static usb_error_t urtw_intr_enable(struct urtw_softc *); static usb_error_t urtw_intr_disable(struct urtw_softc *); static usb_error_t urtw_reset(struct urtw_softc *); static usb_error_t urtw_led_on(struct urtw_softc *, int); static usb_error_t urtw_led_ctl(struct urtw_softc *, int); static usb_error_t urtw_led_blink(struct urtw_softc *); static usb_error_t urtw_led_mode0(struct urtw_softc *, int); static usb_error_t urtw_led_mode1(struct urtw_softc *, int); static usb_error_t urtw_led_mode2(struct urtw_softc *, int); static usb_error_t urtw_led_mode3(struct urtw_softc *, int); static usb_error_t urtw_rx_setconf(struct urtw_softc *); static usb_error_t urtw_rx_enable(struct urtw_softc *); static usb_error_t urtw_tx_enable(struct urtw_softc *sc); static void urtw_free_tx_data_list(struct urtw_softc *); static void urtw_free_rx_data_list(struct urtw_softc *); static void urtw_free_data_list(struct urtw_softc *, struct urtw_data data[], int, int); static usb_error_t urtw_set_macaddr(struct urtw_softc *, const uint8_t *); static usb_error_t urtw_adapter_start(struct urtw_softc *); static usb_error_t urtw_adapter_start_b(struct urtw_softc *); static usb_error_t urtw_set_mode(struct urtw_softc *, uint32_t); static usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *); static usb_error_t urtw_do_request(struct urtw_softc *, struct usb_device_request *, void *); static usb_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *, int); static usb_error_t urtw_led_off(struct urtw_softc *, int); static void urtw_abort_xfers(struct urtw_softc *); static struct urtw_data * urtw_getbuf(struct urtw_softc *sc); static int urtw_compute_txtime(uint16_t, uint16_t, uint8_t, uint8_t); static void urtw_updateslot(struct ieee80211com *); static void urtw_updateslottask(void *, int); static void urtw_sysctl_node(struct urtw_softc *); static int urtw_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != URTW_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != URTW_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(urtw_devs, sizeof(urtw_devs), uaa)); } static int urtw_attach(device_t dev) { const struct usb_config *setup_start; int ret = ENXIO; struct urtw_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct ieee80211com *ic = &sc->sc_ic; uint8_t iface_index = URTW_IFACE_INDEX; /* XXX */ uint16_t n_setup; uint32_t data; usb_error_t error; device_set_usb_desc(dev); sc->sc_dev = dev; sc->sc_udev = uaa->device; if (USB_GET_DRIVER_INFO(uaa) == URTW_REV_RTL8187B) sc->sc_flags |= URTW_RTL8187B; #ifdef URTW_DEBUG sc->sc_debug = urtw_debug; #endif mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); usb_callout_init_mtx(&sc->sc_led_ch, &sc->sc_mtx, 0); TASK_INIT(&sc->sc_led_task, 0, urtw_ledtask, sc); TASK_INIT(&sc->sc_updateslot_task, 0, urtw_updateslottask, sc); callout_init(&sc->sc_watchdog_ch, 0); mbufq_init(&sc->sc_snd, ifqmaxlen); if (sc->sc_flags & URTW_RTL8187B) { setup_start = urtw_8187b_usbconfig; n_setup = URTW_8187B_N_XFERS; } else { setup_start = urtw_8187l_usbconfig; n_setup = URTW_8187L_N_XFERS; } error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, setup_start, n_setup, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); ret = ENXIO; goto fail0; } if (sc->sc_flags & URTW_RTL8187B) { sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer(sc->sc_xfer[ URTW_8187B_BULK_TX_BE], 0); } else { sc->sc_tx_dma_buf = usbd_xfer_get_frame_buffer(sc->sc_xfer[ URTW_8187L_BULK_TX_LOW], 0); } URTW_LOCK(sc); urtw_read32_m(sc, URTW_RX, &data); sc->sc_epromtype = (data & URTW_RX_9356SEL) ? URTW_EEPROM_93C56 : URTW_EEPROM_93C46; error = urtw_get_rfchip(sc); if (error != 0) goto fail; error = urtw_get_macaddr(sc); if (error != 0) goto fail; error = urtw_get_txpwr(sc); if (error != 0) goto fail; error = urtw_led_init(sc); if (error != 0) goto fail; URTW_UNLOCK(sc); sc->sc_rts_retry = URTW_DEFAULT_RTS_RETRY; sc->sc_tx_retry = URTW_DEFAULT_TX_RETRY; sc->sc_currate = URTW_RIDX_CCK11; sc->sc_preamble_mode = urtw_preamble_mode; ic->ic_softc = sc; ic->ic_name = device_get_nameunit(dev); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA | /* station mode */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_BGSCAN | /* capable of bg scanning */ IEEE80211_C_WPA; /* 802.11i */ /* XXX TODO: setup regdomain if URTW_EPROM_CHANPLAN_BY_HW bit is set.*/ + ic->ic_flags_ext |= IEEE80211_FEXT_SEQNO_OFFLOAD; + urtw_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); ieee80211_ifattach(ic); ic->ic_raw_xmit = urtw_raw_xmit; ic->ic_scan_start = urtw_scan_start; ic->ic_scan_end = urtw_scan_end; ic->ic_getradiocaps = urtw_getradiocaps; ic->ic_set_channel = urtw_set_channel; ic->ic_updateslot = urtw_updateslot; ic->ic_vap_create = urtw_vap_create; ic->ic_vap_delete = urtw_vap_delete; ic->ic_update_promisc = urtw_update_promisc; ic->ic_update_mcast = urtw_update_mcast; ic->ic_parent = urtw_parent; ic->ic_transmit = urtw_transmit; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), URTW_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), URTW_RX_RADIOTAP_PRESENT); urtw_sysctl_node(sc); if (bootverbose) ieee80211_announce(ic); return (0); fail: URTW_UNLOCK(sc); usbd_transfer_unsetup(sc->sc_xfer, (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS : URTW_8187L_N_XFERS); fail0: return (ret); } static int urtw_detach(device_t dev) { struct urtw_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; unsigned x; unsigned n_xfers; /* Prevent further ioctls */ URTW_LOCK(sc); sc->sc_flags |= URTW_DETACHED; urtw_stop(sc); URTW_UNLOCK(sc); ieee80211_draintask(ic, &sc->sc_updateslot_task); ieee80211_draintask(ic, &sc->sc_led_task); usb_callout_drain(&sc->sc_led_ch); callout_drain(&sc->sc_watchdog_ch); n_xfers = (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS : URTW_8187L_N_XFERS; /* prevent further allocations from RX/TX data lists */ URTW_LOCK(sc); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); URTW_UNLOCK(sc); /* drain USB transfers */ for (x = 0; x != n_xfers; x++) usbd_transfer_drain(sc->sc_xfer[x]); /* free data buffers */ URTW_LOCK(sc); urtw_free_tx_data_list(sc); urtw_free_rx_data_list(sc); URTW_UNLOCK(sc); /* free USB transfers and some data buffers */ usbd_transfer_unsetup(sc->sc_xfer, n_xfers); ieee80211_ifdetach(ic); mbufq_drain(&sc->sc_snd); mtx_destroy(&sc->sc_mtx); return (0); } static void urtw_free_tx_data_list(struct urtw_softc *sc) { urtw_free_data_list(sc, sc->sc_tx, URTW_TX_DATA_LIST_COUNT, 0); } static void urtw_free_rx_data_list(struct urtw_softc *sc) { urtw_free_data_list(sc, sc->sc_rx, URTW_RX_DATA_LIST_COUNT, 1); } static void urtw_free_data_list(struct urtw_softc *sc, struct urtw_data data[], int ndata, int fillmbuf) { int i; for (i = 0; i < ndata; i++) { struct urtw_data *dp = &data[i]; if (fillmbuf == 1) { if (dp->m != NULL) { m_freem(dp->m); dp->m = NULL; dp->buf = NULL; } } else { dp->buf = NULL; } if (dp->ni != NULL) { ieee80211_free_node(dp->ni); dp->ni = NULL; } } } static struct ieee80211vap * urtw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct urtw_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); uvp = malloc(sizeof(struct urtw_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = urtw_newstate; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void urtw_vap_delete(struct ieee80211vap *vap) { struct urtw_vap *uvp = URTW_VAP(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void urtw_init(struct urtw_softc *sc) { usb_error_t error; int ret; URTW_ASSERT_LOCKED(sc); if (sc->sc_flags & URTW_RUNNING) urtw_stop(sc); error = (sc->sc_flags & URTW_RTL8187B) ? urtw_adapter_start_b(sc) : urtw_adapter_start(sc); if (error != 0) goto fail; /* reset softc variables */ sc->sc_txtimer = 0; if (!(sc->sc_flags & URTW_INIT_ONCE)) { ret = urtw_alloc_rx_data_list(sc); if (ret != 0) goto fail; ret = urtw_alloc_tx_data_list(sc); if (ret != 0) goto fail; sc->sc_flags |= URTW_INIT_ONCE; } error = urtw_rx_enable(sc); if (error != 0) goto fail; error = urtw_tx_enable(sc); if (error != 0) goto fail; if (sc->sc_flags & URTW_RTL8187B) usbd_transfer_start(sc->sc_xfer[URTW_8187B_BULK_TX_STATUS]); sc->sc_flags |= URTW_RUNNING; callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); fail: return; } static usb_error_t urtw_adapter_start_b(struct urtw_softc *sc) { uint8_t data8; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data8); urtw_write8_m(sc, URTW_CONFIG3, data8 | URTW_CONFIG3_ANAPARAM_WRITE | URTW_CONFIG3_GNT_SELECT); urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8187B_8225_ANAPARAM2_ON); urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_ON); urtw_write8_m(sc, URTW_ANAPARAM3, URTW_8187B_8225_ANAPARAM3_ON); urtw_write8_m(sc, 0x61, 0x10); urtw_read8_m(sc, 0x62, &data8); urtw_write8_m(sc, 0x62, data8 & ~(1 << 5)); urtw_write8_m(sc, 0x62, data8 | (1 << 5)); urtw_read8_m(sc, URTW_CONFIG3, &data8); data8 &= ~URTW_CONFIG3_ANAPARAM_WRITE; urtw_write8_m(sc, URTW_CONFIG3, data8); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_8187b_cmd_reset(sc); if (error) goto fail; error = sc->sc_rf_init(sc); if (error != 0) goto fail; urtw_write8_m(sc, URTW_CMD, URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE); /* fix RTL8187B RX stall */ error = urtw_intr_enable(sc); if (error) goto fail; error = urtw_write8e(sc, 0x41, 0xf4); if (error) goto fail; error = urtw_write8e(sc, 0x40, 0x00); if (error) goto fail; error = urtw_write8e(sc, 0x42, 0x00); if (error) goto fail; error = urtw_write8e(sc, 0x42, 0x01); if (error) goto fail; error = urtw_write8e(sc, 0x40, 0x0f); if (error) goto fail; error = urtw_write8e(sc, 0x42, 0x00); if (error) goto fail; error = urtw_write8e(sc, 0x42, 0x01); if (error) goto fail; urtw_read8_m(sc, 0xdb, &data8); urtw_write8_m(sc, 0xdb, data8 | (1 << 2)); urtw_write16_m(sc, 0x372, 0x59fa); urtw_write16_m(sc, 0x374, 0x59d2); urtw_write16_m(sc, 0x376, 0x59d2); urtw_write16_m(sc, 0x378, 0x19fa); urtw_write16_m(sc, 0x37a, 0x19fa); urtw_write16_m(sc, 0x37c, 0x00d0); urtw_write8_m(sc, 0x61, 0); urtw_write8_m(sc, 0x180, 0x0f); urtw_write8_m(sc, 0x183, 0x03); urtw_write8_m(sc, 0xda, 0x10); urtw_write8_m(sc, 0x24d, 0x08); urtw_write32_m(sc, URTW_HSSI_PARA, 0x0600321b); urtw_write16_m(sc, 0x1ec, 0x800); /* RX MAX SIZE */ fail: return (error); } static usb_error_t urtw_set_macaddr(struct urtw_softc *sc, const uint8_t *macaddr) { usb_error_t error; urtw_write32_m(sc, URTW_MAC0, ((const uint32_t *)macaddr)[0]); urtw_write16_m(sc, URTW_MAC4, ((const uint32_t *)macaddr)[1] & 0xffff); fail: return (error); } static usb_error_t urtw_adapter_start(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); const uint8_t *macaddr; usb_error_t error; error = urtw_reset(sc); if (error) goto fail; urtw_write8_m(sc, URTW_ADDR_MAGIC1, 0); urtw_write8_m(sc, URTW_GPIO, 0); /* for led */ urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4); error = urtw_led_ctl(sc, URTW_LED_CTL_POWER_ON); if (error != 0) goto fail; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; /* applying MAC address again. */ macaddr = vap ? vap->iv_myaddr : ic->ic_macaddr; urtw_set_macaddr(sc, macaddr); if (error) goto fail; error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_update_msr(sc); if (error) goto fail; urtw_write32_m(sc, URTW_INT_TIMEOUT, 0); urtw_write8_m(sc, URTW_WPA_CONFIG, 0); urtw_write8_m(sc, URTW_RATE_FALLBACK, URTW_RATE_FALLBACK_ENABLE | 0x1); error = urtw_set_rate(sc); if (error != 0) goto fail; error = sc->sc_rf_init(sc); if (error != 0) goto fail; if (sc->sc_rf_set_sens != NULL) sc->sc_rf_set_sens(sc, sc->sc_sens); /* XXX correct? to call write16 */ urtw_write16_m(sc, URTW_PSR, 1); urtw_write16_m(sc, URTW_ADDR_MAGIC2, 0x10); urtw_write8_m(sc, URTW_TALLY_SEL, 0x80); urtw_write8_m(sc, URTW_ADDR_MAGIC3, 0x60); /* XXX correct? to call write16 */ urtw_write16_m(sc, URTW_PSR, 0); urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4); error = urtw_intr_enable(sc); if (error != 0) goto fail; fail: return (error); } static usb_error_t urtw_set_mode(struct urtw_softc *sc, uint32_t mode) { uint8_t data; usb_error_t error; urtw_read8_m(sc, URTW_EPROM_CMD, &data); data = (data & ~URTW_EPROM_CMD_MASK) | (mode << URTW_EPROM_CMD_SHIFT); data = data & ~(URTW_EPROM_CS | URTW_EPROM_CK); urtw_write8_m(sc, URTW_EPROM_CMD, data); fail: return (error); } static void urtw_pause_ms(struct urtw_softc *sc, int delay) { usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(delay)); } static usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *sc) { int i; uint8_t data8; usb_error_t error; /* XXX the code can be duplicate with urtw_reset(). */ urtw_read8_m(sc, URTW_CMD, &data8); data8 = (data8 & 0x2) | URTW_CMD_RST; urtw_write8_m(sc, URTW_CMD, data8); for (i = 0; i < 20; i++) { urtw_pause_ms(sc, 2); urtw_read8_m(sc, URTW_CMD, &data8); if (!(data8 & URTW_CMD_RST)) break; } if (i >= 20) { device_printf(sc->sc_dev, "reset timeout\n"); goto fail; } fail: return (error); } static usb_error_t urtw_do_request(struct urtw_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; URTW_ASSERT_LOCKED(sc); while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0) break; DPRINTF(sc, URTW_DEBUG_INIT, "Control request failed, %s (retrying)\n", usbd_errstr(err)); urtw_pause_ms(sc, 10); } return (err); } static void urtw_stop(struct urtw_softc *sc) { uint8_t data8; usb_error_t error; URTW_ASSERT_LOCKED(sc); sc->sc_flags &= ~URTW_RUNNING; error = urtw_intr_disable(sc); if (error) goto fail; urtw_read8_m(sc, URTW_CMD, &data8); data8 &= ~(URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE); urtw_write8_m(sc, URTW_CMD, data8); error = sc->sc_rf_stop(sc); if (error != 0) goto fail; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG4, &data8); urtw_write8_m(sc, URTW_CONFIG4, data8 | URTW_CONFIG4_VCOOFF); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; fail: if (error) device_printf(sc->sc_dev, "failed to stop (%s)\n", usbd_errstr(error)); usb_callout_stop(&sc->sc_led_ch); callout_stop(&sc->sc_watchdog_ch); urtw_abort_xfers(sc); } static void urtw_abort_xfers(struct urtw_softc *sc) { int i, max; URTW_ASSERT_LOCKED(sc); max = (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS : URTW_8187L_N_XFERS; /* abort any pending transfers */ for (i = 0; i < max; i++) usbd_transfer_stop(sc->sc_xfer[i]); } static void urtw_parent(struct ieee80211com *ic) { struct urtw_softc *sc = ic->ic_softc; int startall = 0; URTW_LOCK(sc); if (sc->sc_flags & URTW_DETACHED) { URTW_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if (sc->sc_flags & URTW_RUNNING) { if (ic->ic_promisc > 0 || ic->ic_allmulti > 0) urtw_set_multi(sc); } else { urtw_init(sc); startall = 1; } } else if (sc->sc_flags & URTW_RUNNING) urtw_stop(sc); URTW_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static int urtw_transmit(struct ieee80211com *ic, struct mbuf *m) { struct urtw_softc *sc = ic->ic_softc; int error; URTW_LOCK(sc); if ((sc->sc_flags & URTW_RUNNING) == 0) { URTW_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { URTW_UNLOCK(sc); return (error); } urtw_start(sc); URTW_UNLOCK(sc); return (0); } static void urtw_start(struct urtw_softc *sc) { struct urtw_data *bf; struct ieee80211_node *ni; struct mbuf *m; URTW_ASSERT_LOCKED(sc); if ((sc->sc_flags & URTW_RUNNING) == 0) return; while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { bf = urtw_getbuf(sc); if (bf == NULL) { mbufq_prepend(&sc->sc_snd, m); break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_NORMAL) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); ieee80211_free_node(ni); break; } sc->sc_txtimer = 5; callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); } } static int urtw_alloc_data_list(struct urtw_softc *sc, struct urtw_data data[], int ndata, int maxsz, void *dma_buf) { int i, error; for (i = 0; i < ndata; i++) { struct urtw_data *dp = &data[i]; dp->sc = sc; if (dma_buf == NULL) { dp->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (dp->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } dp->buf = mtod(dp->m, uint8_t *); } else { dp->m = NULL; dp->buf = ((uint8_t *)dma_buf) + (i * maxsz); } dp->ni = NULL; } return (0); fail: urtw_free_data_list(sc, data, ndata, 1); return (error); } static int urtw_alloc_rx_data_list(struct urtw_softc *sc) { int error, i; error = urtw_alloc_data_list(sc, sc->sc_rx, URTW_RX_DATA_LIST_COUNT, MCLBYTES, NULL /* mbufs */); if (error != 0) return (error); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); return (0); } static int urtw_alloc_tx_data_list(struct urtw_softc *sc) { int error, i; error = urtw_alloc_data_list(sc, sc->sc_tx, URTW_TX_DATA_LIST_COUNT, URTW_TX_MAXSIZE, sc->sc_tx_dma_buf /* no mbufs */); if (error != 0) return (error); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); for (i = 0; i < URTW_TX_DATA_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); return (0); } static int urtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct urtw_softc *sc = ic->ic_softc; struct urtw_data *bf; /* prevent management frames from being sent if we're not ready */ if (!(sc->sc_flags & URTW_RUNNING)) { m_freem(m); return ENETDOWN; } URTW_LOCK(sc); bf = urtw_getbuf(sc); if (bf == NULL) { m_freem(m); URTW_UNLOCK(sc); return (ENOBUFS); /* XXX */ } if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_LOW) != 0) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); URTW_UNLOCK(sc); return (EIO); } URTW_UNLOCK(sc); sc->sc_txtimer = 5; return (0); } static void urtw_scan_start(struct ieee80211com *ic) { /* XXX do nothing? */ } static void urtw_scan_end(struct ieee80211com *ic) { /* XXX do nothing? */ } static void urtw_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { uint8_t bands[IEEE80211_MODE_BYTES]; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); } static void urtw_set_channel(struct ieee80211com *ic) { struct urtw_softc *sc = ic->ic_softc; uint32_t data, orig; usb_error_t error; /* * if the user set a channel explicitly using ifconfig(8) this function * can be called earlier than we're expected that in some cases the * initialization would be failed if setting a channel is called before * the init have done. */ if (!(sc->sc_flags & URTW_RUNNING)) return; if (sc->sc_curchan != NULL && sc->sc_curchan == ic->ic_curchan) return; URTW_LOCK(sc); /* * during changing th channel we need to temporarily be disable * TX. */ urtw_read32_m(sc, URTW_TX_CONF, &orig); data = orig & ~URTW_TX_LOOPBACK_MASK; urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_MAC); error = sc->sc_rf_set_chan(sc, ieee80211_chan2ieee(ic, ic->ic_curchan)); if (error != 0) goto fail; urtw_pause_ms(sc, 10); urtw_write32_m(sc, URTW_TX_CONF, orig); urtw_write16_m(sc, URTW_ATIM_WND, 2); urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); fail: URTW_UNLOCK(sc); sc->sc_curchan = ic->ic_curchan; if (error != 0) device_printf(sc->sc_dev, "could not change the channel\n"); } static void urtw_update_promisc(struct ieee80211com *ic) { struct urtw_softc *sc = ic->ic_softc; URTW_LOCK(sc); if (sc->sc_flags & URTW_RUNNING) urtw_rx_setconf(sc); URTW_UNLOCK(sc); } static void urtw_update_mcast(struct ieee80211com *ic) { /* XXX do nothing? */ } static int urtw_tx_start(struct urtw_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, struct urtw_data *data, int prior) { struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); struct ieee80211_key *k; const struct ieee80211_txparam *tp = ni->ni_txparms; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct usb_xfer *rtl8187b_pipes[URTW_8187B_TXPIPE_MAX] = { sc->sc_xfer[URTW_8187B_BULK_TX_BE], sc->sc_xfer[URTW_8187B_BULK_TX_BK], sc->sc_xfer[URTW_8187B_BULK_TX_VI], sc->sc_xfer[URTW_8187B_BULK_TX_VO] }; struct usb_xfer *xfer; int dur = 0, rtsdur = 0, rtsenable = 0, ctsenable = 0, rate, type, pkttime = 0, txdur = 0, isshort = 0, xferlen, ismcast; uint16_t acktime, rtstime, ctstime; uint32_t flags; usb_error_t error; URTW_ASSERT_LOCKED(sc); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* Assign sequence number */ + ieee80211_output_seqno_assign(ni, -1, m0); + /* * Software crypto. */ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { device_printf(sc->sc_dev, "ieee80211_crypto_encap returns NULL.\n"); /* XXX we don't expect the fragmented frames */ m_freem(m0); return (ENOBUFS); } /* in case packet header moved, reset pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (ieee80211_radiotap_active_vap(vap)) { struct urtw_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; ieee80211_radiotap_tx(vap, m0); } if (IEEE80211_IS_MGMT(wh) || IEEE80211_IS_CTL(wh) || (m0->m_flags & M_EAPOL) != 0) { rate = tp->mgmtrate; } else { /* for data frames */ if (ismcast) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = urtw_rtl2rate(sc->sc_currate); } sc->sc_stats.txrates[sc->sc_currate]++; if (ismcast) txdur = pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, 0); else { acktime = urtw_compute_txtime(14, 2,0, 0); if ((m0->m_pkthdr.len + 4) > vap->iv_rtsthreshold) { rtsenable = 1; ctsenable = 0; rtstime = urtw_compute_txtime(URTW_ACKCTS_LEN, 2, 0, 0); ctstime = urtw_compute_txtime(14, 2, 0, 0); pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort); rtsdur = ctstime + pkttime + acktime + 3 * URTW_ASIFS_TIME; txdur = rtstime + rtsdur; } else { rtsenable = ctsenable = rtsdur = 0; pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort); txdur = pkttime + URTW_ASIFS_TIME + acktime; } if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) dur = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort) + 3 * URTW_ASIFS_TIME + 2 * acktime; else dur = URTW_ASIFS_TIME + acktime; } USETW(wh->i_dur, dur); xferlen = m0->m_pkthdr.len; xferlen += (sc->sc_flags & URTW_RTL8187B) ? (4 * 8) : (4 * 3); if ((0 == xferlen % 64) || (0 == xferlen % 512)) xferlen += 1; memset(data->buf, 0, URTW_TX_MAXSIZE); flags = m0->m_pkthdr.len & 0xfff; flags |= URTW_TX_FLAG_NO_ENC; if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) && (sc->sc_preamble_mode == URTW_PREAMBLE_MODE_SHORT) && (sc->sc_currate != 0)) flags |= URTW_TX_FLAG_SPLCP; if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) flags |= URTW_TX_FLAG_MOREFRAG; flags |= (sc->sc_currate & 0xf) << URTW_TX_FLAG_TXRATE_SHIFT; if (sc->sc_flags & URTW_RTL8187B) { struct urtw_8187b_txhdr *tx; tx = (struct urtw_8187b_txhdr *)data->buf; if (ctsenable) flags |= URTW_TX_FLAG_CTS; if (rtsenable) { flags |= URTW_TX_FLAG_RTS; flags |= URTW_RIDX_CCK5 << URTW_TX_FLAG_RTSRATE_SHIFT; tx->rtsdur = rtsdur; } tx->flag = htole32(flags); tx->txdur = txdur; if (IEEE80211_IS_MGMT_PROBE_RESP(wh)) tx->retry = 1; else tx->retry = URTW_TX_MAXRETRY; m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1)); } else { struct urtw_8187l_txhdr *tx; tx = (struct urtw_8187l_txhdr *)data->buf; if (rtsenable) { flags |= URTW_TX_FLAG_RTS; tx->rtsdur = rtsdur; } flags |= URTW_RIDX_CCK5 << URTW_TX_FLAG_RTSRATE_SHIFT; tx->flag = htole32(flags); tx->retry = 3; /* CW minimum */ tx->retry |= 7 << 4; /* CW maximum */ tx->retry |= URTW_TX_MAXRETRY << 8; /* retry limitation */ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1)); } data->buflen = xferlen; data->ni = ni; data->m = m0; if (sc->sc_flags & URTW_RTL8187B) { switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: xfer = sc->sc_xfer[URTW_8187B_BULK_TX_EP12]; break; default: KASSERT(M_WME_GETAC(m0) < URTW_8187B_TXPIPE_MAX, ("unsupported WME pipe %d", M_WME_GETAC(m0))); xfer = rtl8187b_pipes[M_WME_GETAC(m0)]; break; } } else xfer = (prior == URTW_PRIORITY_LOW) ? sc->sc_xfer[URTW_8187L_BULK_TX_LOW] : sc->sc_xfer[URTW_8187L_BULK_TX_NORMAL]; STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); usbd_transfer_start(xfer); error = urtw_led_ctl(sc, URTW_LED_CTL_TX); if (error != 0) device_printf(sc->sc_dev, "could not control LED (%d)\n", error); return (0); } static int urtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ieee80211com *ic = vap->iv_ic; struct urtw_softc *sc = ic->ic_softc; struct urtw_vap *uvp = URTW_VAP(vap); struct ieee80211_node *ni; usb_error_t error = 0; DPRINTF(sc, URTW_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); sc->sc_state = nstate; IEEE80211_UNLOCK(ic); URTW_LOCK(sc); usb_callout_stop(&sc->sc_led_ch); callout_stop(&sc->sc_watchdog_ch); switch (nstate) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: break; case IEEE80211_S_RUN: ni = ieee80211_ref_node(vap->iv_bss); /* setting bssid. */ urtw_write32_m(sc, URTW_BSSID, ((uint32_t *)ni->ni_bssid)[0]); urtw_write16_m(sc, URTW_BSSID + 4, ((uint16_t *)ni->ni_bssid)[2]); urtw_update_msr(sc); /* XXX maybe the below would be incorrect. */ urtw_write16_m(sc, URTW_ATIM_WND, 2); urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); urtw_write16_m(sc, URTW_BEACON_INTERVAL, 0x64); urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); error = urtw_led_ctl(sc, URTW_LED_CTL_LINK); if (error != 0) device_printf(sc->sc_dev, "could not control LED (%d)\n", error); ieee80211_free_node(ni); break; default: break; } fail: URTW_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } static void urtw_watchdog(void *arg) { struct urtw_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; if (sc->sc_txtimer > 0) { if (--sc->sc_txtimer == 0) { device_printf(sc->sc_dev, "device timeout\n"); counter_u64_add(ic->ic_oerrors, 1); ieee80211_restart_all(ic); return; } callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); } } static void urtw_set_multi(void *arg) { /* XXX don't know how to set a device. Lack of docs. */ } static usb_error_t urtw_set_rate(struct urtw_softc *sc) { int i, basic_rate, min_rr_rate, max_rr_rate; uint16_t data; usb_error_t error; basic_rate = URTW_RIDX_OFDM24; min_rr_rate = URTW_RIDX_OFDM6; max_rr_rate = URTW_RIDX_OFDM24; urtw_write8_m(sc, URTW_RESP_RATE, max_rr_rate << URTW_RESP_MAX_RATE_SHIFT | min_rr_rate << URTW_RESP_MIN_RATE_SHIFT); urtw_read16_m(sc, URTW_BRSR, &data); data &= ~URTW_BRSR_MBR_8185; for (i = 0; i <= basic_rate; i++) data |= (1 << i); urtw_write16_m(sc, URTW_BRSR, data); fail: return (error); } static uint16_t urtw_rtl2rate(uint32_t rate) { unsigned i; for (i = 0; i < nitems(urtw_ratetable); i++) { if (rate == urtw_ratetable[i].val) return urtw_ratetable[i].reg; } return (0); } static usb_error_t urtw_update_msr(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint8_t data; usb_error_t error; urtw_read8_m(sc, URTW_MSR, &data); data &= ~URTW_MSR_LINK_MASK; if (sc->sc_state == IEEE80211_S_RUN) { switch (ic->ic_opmode) { case IEEE80211_M_STA: case IEEE80211_M_MONITOR: data |= URTW_MSR_LINK_STA; if (sc->sc_flags & URTW_RTL8187B) data |= URTW_MSR_LINK_ENEDCA; break; case IEEE80211_M_IBSS: data |= URTW_MSR_LINK_ADHOC; break; case IEEE80211_M_HOSTAP: data |= URTW_MSR_LINK_HOSTAP; break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported operation mode 0x%x\n", ic->ic_opmode); error = USB_ERR_INVAL; goto fail; } } else data |= URTW_MSR_LINK_NONE; urtw_write8_m(sc, URTW_MSR, data); fail: return (error); } static usb_error_t urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint8_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint16_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint32_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint8_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint16_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint32_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_get_macaddr(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t data; usb_error_t error; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR, &data); if (error != 0) goto fail; ic->ic_macaddr[0] = data & 0xff; ic->ic_macaddr[1] = (data & 0xff00) >> 8; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 1, &data); if (error != 0) goto fail; ic->ic_macaddr[2] = data & 0xff; ic->ic_macaddr[3] = (data & 0xff00) >> 8; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 2, &data); if (error != 0) goto fail; ic->ic_macaddr[4] = data & 0xff; ic->ic_macaddr[5] = (data & 0xff00) >> 8; fail: return (error); } static usb_error_t urtw_eprom_read32(struct urtw_softc *sc, uint32_t addr, uint32_t *data) { #define URTW_READCMD_LEN 3 int addrlen, i; int16_t addrstr[8], data16, readcmd[] = { 1, 1, 0 }; usb_error_t error; /* NB: make sure the buffer is initialized */ *data = 0; /* enable EPROM programming */ urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_PROGRAM_MODE); DELAY(URTW_EPROM_DELAY); error = urtw_eprom_cs(sc, URTW_EPROM_ENABLE); if (error != 0) goto fail; error = urtw_eprom_ck(sc); if (error != 0) goto fail; error = urtw_eprom_sendbits(sc, readcmd, URTW_READCMD_LEN); if (error != 0) goto fail; if (sc->sc_epromtype == URTW_EEPROM_93C56) { addrlen = 8; addrstr[0] = addr & (1 << 7); addrstr[1] = addr & (1 << 6); addrstr[2] = addr & (1 << 5); addrstr[3] = addr & (1 << 4); addrstr[4] = addr & (1 << 3); addrstr[5] = addr & (1 << 2); addrstr[6] = addr & (1 << 1); addrstr[7] = addr & (1 << 0); } else { addrlen=6; addrstr[0] = addr & (1 << 5); addrstr[1] = addr & (1 << 4); addrstr[2] = addr & (1 << 3); addrstr[3] = addr & (1 << 2); addrstr[4] = addr & (1 << 1); addrstr[5] = addr & (1 << 0); } error = urtw_eprom_sendbits(sc, addrstr, addrlen); if (error != 0) goto fail; error = urtw_eprom_writebit(sc, 0); if (error != 0) goto fail; for (i = 0; i < 16; i++) { error = urtw_eprom_ck(sc); if (error != 0) goto fail; error = urtw_eprom_readbit(sc, &data16); if (error != 0) goto fail; (*data) |= (data16 << (15 - i)); } error = urtw_eprom_cs(sc, URTW_EPROM_DISABLE); if (error != 0) goto fail; error = urtw_eprom_ck(sc); if (error != 0) goto fail; /* now disable EPROM programming */ urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_NORMAL_MODE); fail: return (error); #undef URTW_READCMD_LEN } static usb_error_t urtw_eprom_cs(struct urtw_softc *sc, int able) { uint8_t data; usb_error_t error; urtw_read8_m(sc, URTW_EPROM_CMD, &data); if (able == URTW_EPROM_ENABLE) urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CS); else urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CS); DELAY(URTW_EPROM_DELAY); fail: return (error); } static usb_error_t urtw_eprom_ck(struct urtw_softc *sc) { uint8_t data; usb_error_t error; /* masking */ urtw_read8_m(sc, URTW_EPROM_CMD, &data); urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CK); DELAY(URTW_EPROM_DELAY); /* unmasking */ urtw_read8_m(sc, URTW_EPROM_CMD, &data); urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CK); DELAY(URTW_EPROM_DELAY); fail: return (error); } static usb_error_t urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data) { uint8_t data8; usb_error_t error; urtw_read8_m(sc, URTW_EPROM_CMD, &data8); *data = (data8 & URTW_EPROM_READBIT) ? 1 : 0; DELAY(URTW_EPROM_DELAY); fail: return (error); } static usb_error_t urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit) { uint8_t data; usb_error_t error; urtw_read8_m(sc, URTW_EPROM_CMD, &data); if (bit != 0) urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_WRITEBIT); else urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_WRITEBIT); DELAY(URTW_EPROM_DELAY); fail: return (error); } static usb_error_t urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen) { int i = 0; usb_error_t error = 0; for (i = 0; i < buflen; i++) { error = urtw_eprom_writebit(sc, buf[i]); if (error != 0) goto fail; error = urtw_eprom_ck(sc); if (error != 0) goto fail; } fail: return (error); } static usb_error_t urtw_get_txpwr(struct urtw_softc *sc) { int i, j; uint32_t data; usb_error_t error; error = urtw_eprom_read32(sc, URTW_EPROM_TXPW_BASE, &data); if (error != 0) goto fail; sc->sc_txpwr_cck_base = data & 0xf; sc->sc_txpwr_ofdm_base = (data >> 4) & 0xf; for (i = 1, j = 0; i < 6; i += 2, j++) { error = urtw_eprom_read32(sc, URTW_EPROM_TXPW0 + j, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[i] = data & 0xf; sc->sc_txpwr_cck[i + 1] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[i] = (data & 0xf0) >> 4; sc->sc_txpwr_ofdm[i + 1] = (data & 0xf000) >> 12; } for (i = 1, j = 0; i < 4; i += 2, j++) { error = urtw_eprom_read32(sc, URTW_EPROM_TXPW1 + j, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[i + 6] = data & 0xf; sc->sc_txpwr_cck[i + 6 + 1] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[i + 6] = (data & 0xf0) >> 4; sc->sc_txpwr_ofdm[i + 6 + 1] = (data & 0xf000) >> 12; } if (sc->sc_flags & URTW_RTL8187B) { error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[1 + 6 + 4] = data & 0xf; sc->sc_txpwr_ofdm[1 + 6 + 4] = (data & 0xf0) >> 4; error = urtw_eprom_read32(sc, 0x0a, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[2 + 6 + 4] = data & 0xf; sc->sc_txpwr_ofdm[2 + 6 + 4] = (data & 0xf0) >> 4; error = urtw_eprom_read32(sc, 0x1c, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[3 + 6 + 4] = data & 0xf; sc->sc_txpwr_cck[3 + 6 + 4 + 1] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[3 + 6 + 4] = (data & 0xf0) >> 4; sc->sc_txpwr_ofdm[3 + 6 + 4 + 1] = (data & 0xf000) >> 12; } else { for (i = 1, j = 0; i < 4; i += 2, j++) { error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2 + j, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[i + 6 + 4] = data & 0xf; sc->sc_txpwr_cck[i + 6 + 4 + 1] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[i + 6 + 4] = (data & 0xf0) >> 4; sc->sc_txpwr_ofdm[i + 6 + 4 + 1] = (data & 0xf000) >> 12; } } fail: return (error); } static usb_error_t urtw_get_rfchip(struct urtw_softc *sc) { int ret; uint8_t data8; uint32_t data; usb_error_t error; if (sc->sc_flags & URTW_RTL8187B) { urtw_read8_m(sc, 0xe1, &data8); switch (data8) { case 0: sc->sc_flags |= URTW_RTL8187B_REV_B; break; case 1: sc->sc_flags |= URTW_RTL8187B_REV_D; break; case 2: sc->sc_flags |= URTW_RTL8187B_REV_E; break; default: device_printf(sc->sc_dev, "unknown type: %#x\n", data8); sc->sc_flags |= URTW_RTL8187B_REV_B; break; } } else { urtw_read32_m(sc, URTW_TX_CONF, &data); switch (data & URTW_TX_HWMASK) { case URTW_TX_R8187vD_B: sc->sc_flags |= URTW_RTL8187B; break; case URTW_TX_R8187vD: break; default: device_printf(sc->sc_dev, "unknown RTL8187L type: %#x\n", data & URTW_TX_HWMASK); break; } } error = urtw_eprom_read32(sc, URTW_EPROM_RFCHIPID, &data); if (error != 0) goto fail; switch (data & 0xff) { case URTW_EPROM_RFCHIPID_RTL8225U: error = urtw_8225_isv2(sc, &ret); if (error != 0) goto fail; if (ret == 0) { sc->sc_rf_init = urtw_8225_rf_init; sc->sc_rf_set_sens = urtw_8225_rf_set_sens; sc->sc_rf_set_chan = urtw_8225_rf_set_chan; sc->sc_rf_stop = urtw_8225_rf_stop; } else { sc->sc_rf_init = urtw_8225v2_rf_init; sc->sc_rf_set_chan = urtw_8225v2_rf_set_chan; sc->sc_rf_stop = urtw_8225_rf_stop; } sc->sc_max_sens = URTW_8225_RF_MAX_SENS; sc->sc_sens = URTW_8225_RF_DEF_SENS; break; case URTW_EPROM_RFCHIPID_RTL8225Z2: sc->sc_rf_init = urtw_8225v2b_rf_init; sc->sc_rf_set_chan = urtw_8225v2b_rf_set_chan; sc->sc_max_sens = URTW_8225_RF_MAX_SENS; sc->sc_sens = URTW_8225_RF_DEF_SENS; sc->sc_rf_stop = urtw_8225_rf_stop; break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported RF chip %d\n", data & 0xff); error = USB_ERR_INVAL; goto fail; } device_printf(sc->sc_dev, "%s rf %s hwrev %s\n", (sc->sc_flags & URTW_RTL8187B) ? "rtl8187b" : "rtl8187l", ((data & 0xff) == URTW_EPROM_RFCHIPID_RTL8225U) ? "rtl8225u" : "rtl8225z2", (sc->sc_flags & URTW_RTL8187B) ? ((data8 == 0) ? "b" : (data8 == 1) ? "d" : "e") : "none"); fail: return (error); } static usb_error_t urtw_led_init(struct urtw_softc *sc) { uint32_t rev; usb_error_t error; urtw_read8_m(sc, URTW_PSR, &sc->sc_psr); error = urtw_eprom_read32(sc, URTW_EPROM_SWREV, &rev); if (error != 0) goto fail; switch (rev & URTW_EPROM_CID_MASK) { case URTW_EPROM_CID_ALPHA0: sc->sc_strategy = URTW_SW_LED_MODE1; break; case URTW_EPROM_CID_SERCOMM_PS: sc->sc_strategy = URTW_SW_LED_MODE3; break; case URTW_EPROM_CID_HW_LED: sc->sc_strategy = URTW_HW_LED; break; case URTW_EPROM_CID_RSVD0: case URTW_EPROM_CID_RSVD1: default: sc->sc_strategy = URTW_SW_LED_MODE0; break; } sc->sc_gpio_ledpin = URTW_LED_PIN_GPIO0; fail: return (error); } static usb_error_t urtw_8225_rf_init(struct urtw_softc *sc) { unsigned i; uint16_t data; usb_error_t error; error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8225_usb_init(sc); if (error) goto fail; urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ urtw_write16_m(sc, URTW_BRSR, 0xffff); urtw_write32_m(sc, URTW_RF_PARA, 0x100044); error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_write8_m(sc, URTW_CONFIG3, 0x44); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_8185_rf_pins_enable(sc); if (error) goto fail; urtw_pause_ms(sc, 1000); for (i = 0; i < nitems(urtw_8225_rf_part1); i++) { urtw_8225_write(sc, urtw_8225_rf_part1[i].reg, urtw_8225_rf_part1[i].val); urtw_pause_ms(sc, 1); } urtw_pause_ms(sc, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); urtw_pause_ms(sc, 200); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); urtw_pause_ms(sc, 200); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC3); for (i = 0; i < 95; i++) { urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225_rxgain[i]); } urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC4); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC5); for (i = 0; i < 128; i++) { urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); urtw_pause_ms(sc, 1); urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); urtw_pause_ms(sc, 1); } for (i = 0; i < nitems(urtw_8225_rf_part2); i++) { urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg, urtw_8225_rf_part2[i].val); urtw_pause_ms(sc, 1); } error = urtw_8225_setgain(sc, 4); if (error) goto fail; for (i = 0; i < nitems(urtw_8225_rf_part3); i++) { urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg, urtw_8225_rf_part3[i].val); urtw_pause_ms(sc, 1); } urtw_write8_m(sc, URTW_TESTR, 0x0d); error = urtw_8225_set_txpwrlvl(sc, 1); if (error) goto fail; urtw_8187_write_phy_cck(sc, 0x10, 0x9b); urtw_pause_ms(sc, 1); urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); urtw_pause_ms(sc, 1); /* TX ant A, 0x0 for B */ error = urtw_8185_tx_antenna(sc, 0x3); if (error) goto fail; urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002); error = urtw_8225_rf_set_chan(sc, 1); fail: return (error); } static usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *sc) { usb_error_t error = 0; urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7); fail: return (error); } static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant) { usb_error_t error; urtw_write8_m(sc, URTW_TX_ANTENNA, ant); urtw_pause_ms(sc, 1); fail: return (error); } static usb_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *sc, uint8_t addr, uint32_t data) { data = data & 0xff; return urtw_8187_write_phy(sc, addr, data); } static usb_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *sc, uint8_t addr, uint32_t data) { data = data & 0xff; return urtw_8187_write_phy(sc, addr, data | 0x10000); } static usb_error_t urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data) { uint32_t phyw; usb_error_t error; phyw = ((data << 8) | (addr | 0x80)); urtw_write8_m(sc, URTW_PHY_MAGIC4, ((phyw & 0xff000000) >> 24)); urtw_write8_m(sc, URTW_PHY_MAGIC3, ((phyw & 0x00ff0000) >> 16)); urtw_write8_m(sc, URTW_PHY_MAGIC2, ((phyw & 0x0000ff00) >> 8)); urtw_write8_m(sc, URTW_PHY_MAGIC1, ((phyw & 0x000000ff))); urtw_pause_ms(sc, 1); fail: return (error); } static usb_error_t urtw_8225_setgain(struct urtw_softc *sc, int16_t gain) { usb_error_t error; urtw_8187_write_phy_ofdm(sc, 0x0d, urtw_8225_gain[gain * 4]); urtw_8187_write_phy_ofdm(sc, 0x1b, urtw_8225_gain[gain * 4 + 2]); urtw_8187_write_phy_ofdm(sc, 0x1d, urtw_8225_gain[gain * 4 + 3]); urtw_8187_write_phy_ofdm(sc, 0x23, urtw_8225_gain[gain * 4 + 1]); fail: return (error); } static usb_error_t urtw_8225_usb_init(struct urtw_softc *sc) { uint8_t data; usb_error_t error; urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 0); urtw_write8_m(sc, URTW_GPIO, 0); error = urtw_read8e(sc, 0x53, &data); if (error) goto fail; error = urtw_write8e(sc, 0x53, data | (1 << 7)); if (error) goto fail; urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 4); urtw_write8_m(sc, URTW_GPIO, 0x20); urtw_write8_m(sc, URTW_GP_ENABLE, 0); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x80); urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x80); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x80); urtw_pause_ms(sc, 500); fail: return (error); } static usb_error_t urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data) { uint16_t d80, d82, d84; usb_error_t error; urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &d80); d80 &= URTW_RF_PINS_MAGIC1; urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &d82); urtw_read16_m(sc, URTW_RF_PINS_SELECT, &d84); d84 &= URTW_RF_PINS_MAGIC2; urtw_write16_m(sc, URTW_RF_PINS_ENABLE, d82 | URTW_RF_PINS_MAGIC3); urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84 | URTW_RF_PINS_MAGIC3); DELAY(10); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80); DELAY(10); error = urtw_8225_write_s16(sc, addr, 0x8225, &data); if (error != 0) goto fail; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); DELAY(10); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84); urtw_pause_ms(sc, 2); fail: return (error); } static usb_error_t urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index, uint16_t *data) { uint8_t buf[2]; uint16_t data16; struct usb_device_request req; usb_error_t error = 0; data16 = *data; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, addr); USETW(req.wIndex, index); USETW(req.wLength, sizeof(uint16_t)); buf[0] = (data16 & 0x00ff); buf[1] = (data16 & 0xff00) >> 8; error = urtw_do_request(sc, &req, buf); return (error); } static usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan) { usb_error_t error; error = urtw_8225_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); urtw_pause_ms(sc, 10); fail: return (error); } static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens) { usb_error_t error; if (sens < 0 || sens > 6) return -1; if (sens > 4) urtw_8225_write(sc, URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC1); else urtw_8225_write(sc, URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC2); sens = 6 - sens; error = urtw_8225_setgain(sc, sens); if (error) goto fail; urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[sens]); fail: return (error); } static usb_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *sc, int chan) { int i, idx, set; uint8_t *cck_pwltable; uint8_t cck_pwrlvl_max, ofdm_pwrlvl_min, ofdm_pwrlvl_max; uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; usb_error_t error; cck_pwrlvl_max = 11; ofdm_pwrlvl_max = 25; /* 12 -> 25 */ ofdm_pwrlvl_min = 10; /* CCK power setting */ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl; idx = cck_pwrlvl % 6; set = cck_pwrlvl / 6; cck_pwltable = (chan == 14) ? urtw_8225_txpwr_cck_ch14 : urtw_8225_txpwr_cck; urtw_write8_m(sc, URTW_TX_GAIN_CCK, urtw_8225_tx_gain_cck_ofdm[set] >> 1); for (i = 0; i < 8; i++) { urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwltable[idx * 8 + i]); } urtw_pause_ms(sc, 1); /* OFDM power setting */ ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ? ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min; ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; idx = ofdm_pwrlvl % 6; set = ofdm_pwrlvl / 6; error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); if (error) goto fail; urtw_8187_write_phy_ofdm(sc, 2, 0x42); urtw_8187_write_phy_ofdm(sc, 6, 0); urtw_8187_write_phy_ofdm(sc, 8, 0); urtw_write8_m(sc, URTW_TX_GAIN_OFDM, urtw_8225_tx_gain_cck_ofdm[set] >> 1); urtw_8187_write_phy_ofdm(sc, 0x5, urtw_8225_txpwr_ofdm[idx]); urtw_8187_write_phy_ofdm(sc, 0x7, urtw_8225_txpwr_ofdm[idx]); urtw_pause_ms(sc, 1); fail: return (error); } static usb_error_t urtw_8225_rf_stop(struct urtw_softc *sc) { uint8_t data; usb_error_t error; urtw_8225_write(sc, 0x4, 0x1f); error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); if (sc->sc_flags & URTW_RTL8187B) { urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8187B_8225_ANAPARAM2_OFF); urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_OFF); urtw_write32_m(sc, URTW_ANAPARAM3, URTW_8187B_8225_ANAPARAM3_OFF); } else { urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8225_ANAPARAM2_OFF); urtw_write32_m(sc, URTW_ANAPARAM, URTW_8225_ANAPARAM_OFF); } urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; fail: return (error); } static usb_error_t urtw_8225v2_rf_init(struct urtw_softc *sc) { unsigned i; uint16_t data; uint32_t data32; usb_error_t error; error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8225_usb_init(sc); if (error) goto fail; urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ urtw_write16_m(sc, URTW_BRSR, 0xffff); urtw_write32_m(sc, URTW_RF_PARA, 0x100044); error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_write8_m(sc, URTW_CONFIG3, 0x44); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_8185_rf_pins_enable(sc); if (error) goto fail; urtw_pause_ms(sc, 500); for (i = 0; i < nitems(urtw_8225v2_rf_part1); i++) { urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg, urtw_8225v2_rf_part1[i].val); } urtw_pause_ms(sc, 50); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1); for (i = 0; i < 95; i++) { urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225v2_rxgain[i]); } urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, URTW_8225_ADDR_3_DATA_MAGIC1); urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, URTW_8225_ADDR_5_DATA_MAGIC1); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); urtw_pause_ms(sc, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); urtw_pause_ms(sc, 100); error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32); if (error != 0) goto fail; if (data32 != URTW_8225_ADDR_6_DATA_MAGIC1) device_printf(sc->sc_dev, "expect 0xe6!! (0x%x)\n", data32); if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) { urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); urtw_pause_ms(sc, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); urtw_pause_ms(sc, 50); error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32); if (error != 0) goto fail; if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) device_printf(sc->sc_dev, "RF calibration failed\n"); } urtw_pause_ms(sc, 100); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC6); for (i = 0; i < 128; i++) { urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); } for (i = 0; i < nitems(urtw_8225v2_rf_part2); i++) { urtw_8187_write_phy_ofdm(sc, urtw_8225v2_rf_part2[i].reg, urtw_8225v2_rf_part2[i].val); } error = urtw_8225v2_setgain(sc, 4); if (error) goto fail; for (i = 0; i < nitems(urtw_8225v2_rf_part3); i++) { urtw_8187_write_phy_cck(sc, urtw_8225v2_rf_part3[i].reg, urtw_8225v2_rf_part3[i].val); } urtw_write8_m(sc, URTW_TESTR, 0x0d); error = urtw_8225v2_set_txpwrlvl(sc, 1); if (error) goto fail; urtw_8187_write_phy_cck(sc, 0x10, 0x9b); urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); /* TX ant A, 0x0 for B */ error = urtw_8185_tx_antenna(sc, 0x3); if (error) goto fail; urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002); error = urtw_8225_rf_set_chan(sc, 1); fail: return (error); } static usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan) { usb_error_t error; error = urtw_8225v2_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); urtw_pause_ms(sc, 10); fail: return (error); } static usb_error_t urtw_8225_read(struct urtw_softc *sc, uint8_t addr, uint32_t *data) { int i; int16_t bit; uint8_t rlen = 12, wlen = 6; uint16_t o1, o2, o3, tmp; uint32_t d2w = ((uint32_t)(addr & 0x1f)) << 27; uint32_t mask = 0x80000000, value = 0; usb_error_t error; urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &o1); urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &o2); urtw_read16_m(sc, URTW_RF_PINS_SELECT, &o3); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2 | URTW_RF_PINS_MAGIC4); urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3 | URTW_RF_PINS_MAGIC4); o1 &= ~URTW_RF_PINS_MAGIC4; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN); DELAY(5); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1); DELAY(5); for (i = 0; i < (wlen / 2); i++, mask = mask >> 1) { bit = ((d2w & mask) != 0) ? 1 : 0; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_CLK); DELAY(2); mask = mask >> 1; if (i == 2) break; bit = ((d2w & mask) != 0) ? 1 : 0; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1); DELAY(1); } urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW); DELAY(2); mask = 0x800; for (i = 0; i < rlen; i++, mask = mask >> 1) { urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); DELAY(2); urtw_read16_m(sc, URTW_RF_PINS_INPUT, &tmp); value |= ((tmp & URTW_BB_HOST_BANG_CLK) ? mask : 0); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW); DELAY(2); } urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN | URTW_BB_HOST_BANG_RW); DELAY(2); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2); urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3); urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_OUTPUT_MAGIC1); if (data != NULL) *data = value; fail: return (error); } static usb_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *sc, int chan) { int i; uint8_t *cck_pwrtable; uint8_t cck_pwrlvl_max = 15, ofdm_pwrlvl_max = 25, ofdm_pwrlvl_min = 10; uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; usb_error_t error; /* CCK power setting */ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl; cck_pwrlvl += sc->sc_txpwr_cck_base; cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; cck_pwrtable = (chan == 14) ? urtw_8225v2_txpwr_cck_ch14 : urtw_8225v2_txpwr_cck; for (i = 0; i < 8; i++) urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]); urtw_write8_m(sc, URTW_TX_GAIN_CCK, urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl]); urtw_pause_ms(sc, 1); /* OFDM power setting */ ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ? ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min; ofdm_pwrlvl += sc->sc_txpwr_ofdm_base; ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); if (error) goto fail; urtw_8187_write_phy_ofdm(sc, 2, 0x42); urtw_8187_write_phy_ofdm(sc, 5, 0x0); urtw_8187_write_phy_ofdm(sc, 6, 0x40); urtw_8187_write_phy_ofdm(sc, 7, 0x0); urtw_8187_write_phy_ofdm(sc, 8, 0x40); urtw_write8_m(sc, URTW_TX_GAIN_OFDM, urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl]); urtw_pause_ms(sc, 1); fail: return (error); } static usb_error_t urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain) { uint8_t *gainp; usb_error_t error; /* XXX for A? */ gainp = urtw_8225v2_gain_bg; urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]); urtw_pause_ms(sc, 1); urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]); urtw_pause_ms(sc, 1); urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]); urtw_pause_ms(sc, 1); urtw_8187_write_phy_ofdm(sc, 0x21, 0x17); urtw_pause_ms(sc, 1); fail: return (error); } static usb_error_t urtw_8225_isv2(struct urtw_softc *sc, int *ret) { uint32_t data; usb_error_t error; *ret = 1; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_MAGIC5); urtw_write16_m(sc, URTW_RF_PINS_SELECT, URTW_RF_PINS_MAGIC5); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, URTW_RF_PINS_MAGIC5); urtw_pause_ms(sc, 500); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1); error = urtw_8225_read(sc, URTW_8225_ADDR_8_MAGIC, &data); if (error != 0) goto fail; if (data != URTW_8225_ADDR_8_DATA_MAGIC1) *ret = 0; else { error = urtw_8225_read(sc, URTW_8225_ADDR_9_MAGIC, &data); if (error != 0) goto fail; if (data != URTW_8225_ADDR_9_DATA_MAGIC1) *ret = 0; } urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2); fail: return (error); } static usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); const uint8_t *macaddr; unsigned i; uint8_t data8; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; /* * initialize extra registers on 8187 */ urtw_write16_m(sc, URTW_BRSR_8187B, 0xfff); /* retry limit */ urtw_read8_m(sc, URTW_CW_CONF, &data8); data8 |= URTW_CW_CONF_PERPACKET_RETRY; urtw_write8_m(sc, URTW_CW_CONF, data8); /* TX AGC */ urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); data8 |= URTW_TX_AGC_CTL_PERPACKET_GAIN; urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); /* Auto Rate Fallback Control */ #define URTW_ARFR 0x1e0 urtw_write16_m(sc, URTW_ARFR, 0xfff); urtw_read8_m(sc, URTW_RATE_FALLBACK, &data8); urtw_write8_m(sc, URTW_RATE_FALLBACK, data8 | URTW_RATE_FALLBACK_ENABLE); urtw_read8_m(sc, URTW_MSR, &data8); urtw_write8_m(sc, URTW_MSR, data8 & 0xf3); urtw_read8_m(sc, URTW_MSR, &data8); urtw_write8_m(sc, URTW_MSR, data8 | URTW_MSR_LINK_ENEDCA); urtw_write8_m(sc, URTW_ACM_CONTROL, sc->sc_acmctl); urtw_write16_m(sc, URTW_ATIM_WND, 2); urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); #define URTW_FEMR_FOR_8187B 0x1d4 urtw_write16_m(sc, URTW_FEMR_FOR_8187B, 0xffff); /* led type */ urtw_read8_m(sc, URTW_CONFIG1, &data8); data8 = (data8 & 0x3f) | 0x80; urtw_write8_m(sc, URTW_CONFIG1, data8); /* applying MAC address again. */ macaddr = vap ? vap->iv_myaddr : ic->ic_macaddr; error = urtw_set_macaddr(sc, macaddr); if (error) goto fail; error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; urtw_write8_m(sc, URTW_WPA_CONFIG, 0); /* * MAC configuration */ for (i = 0; i < nitems(urtw_8225v2b_rf_part1); i++) urtw_write8_m(sc, urtw_8225v2b_rf_part1[i].reg, urtw_8225v2b_rf_part1[i].val); urtw_write16_m(sc, URTW_TID_AC_MAP, 0xfa50); urtw_write16_m(sc, URTW_INT_MIG, 0x0000); urtw_write32_m(sc, 0x1f0, 0); urtw_write32_m(sc, 0x1f4, 0); urtw_write8_m(sc, 0x1f8, 0); urtw_write32_m(sc, URTW_RF_TIMING, 0x4001); #define URTW_RFSW_CTRL 0x272 urtw_write16_m(sc, URTW_RFSW_CTRL, 0x569a); /* * initialize PHY */ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data8); urtw_write8_m(sc, URTW_CONFIG3, data8 | URTW_CONFIG3_ANAPARAM_WRITE); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; /* setup RFE initial timing */ urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x0480); urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x2488); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1fff); urtw_pause_ms(sc, 1100); for (i = 0; i < nitems(urtw_8225v2b_rf_part0); i++) { urtw_8225_write(sc, urtw_8225v2b_rf_part0[i].reg, urtw_8225v2b_rf_part0[i].val); urtw_pause_ms(sc, 1); } urtw_8225_write(sc, 0x00, 0x01b7); for (i = 0; i < 95; i++) { urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); urtw_pause_ms(sc, 1); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225v2b_rxgain[i]); urtw_pause_ms(sc, 1); } urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, 0x080); urtw_pause_ms(sc, 1); urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, 0x004); urtw_pause_ms(sc, 1); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x0b7); urtw_pause_ms(sc, 1); urtw_pause_ms(sc, 3000); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0xc4d); urtw_pause_ms(sc, 2000); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0x44d); urtw_pause_ms(sc, 1); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x2bf); urtw_pause_ms(sc, 1); urtw_write8_m(sc, URTW_TX_GAIN_CCK, 0x03); urtw_write8_m(sc, URTW_TX_GAIN_OFDM, 0x07); urtw_write8_m(sc, URTW_TX_ANTENNA, 0x03); urtw_8187_write_phy_ofdm(sc, 0x80, 0x12); for (i = 0; i < 128; i++) { uint32_t addr, data; data = (urtw_8225z2_agc[i] << 8) | 0x0000008f; addr = ((i + 0x80) << 8) | 0x0000008e; urtw_8187_write_phy_ofdm(sc, data & 0x7f, (data >> 8) & 0xff); urtw_8187_write_phy_ofdm(sc, addr & 0x7f, (addr >> 8) & 0xff); urtw_8187_write_phy_ofdm(sc, 0x0e, 0x00); } urtw_8187_write_phy_ofdm(sc, 0x80, 0x10); for (i = 0; i < nitems(urtw_8225v2b_rf_part2); i++) urtw_8187_write_phy_ofdm(sc, i, urtw_8225v2b_rf_part2[i].val); urtw_write32_m(sc, URTW_8187B_AC_VO, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_VI, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_BE, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_BK, (7 << 12) | (3 << 8) | 0x1c); urtw_8187_write_phy_ofdm(sc, 0x97, 0x46); urtw_8187_write_phy_ofdm(sc, 0xa4, 0xb6); urtw_8187_write_phy_ofdm(sc, 0x85, 0xfc); urtw_8187_write_phy_cck(sc, 0xc1, 0x88); fail: return (error); } static usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *sc, int chan) { usb_error_t error; error = urtw_8225v2b_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); urtw_pause_ms(sc, 10); fail: return (error); } static usb_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *sc, int chan) { int i; uint8_t *cck_pwrtable; uint8_t cck_pwrlvl_max = 15; uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; usb_error_t error; /* CCK power setting */ cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? ((sc->sc_flags & URTW_RTL8187B_REV_B) ? cck_pwrlvl_max : 22) : (cck_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 0 : 7)); cck_pwrlvl += sc->sc_txpwr_cck_base; cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; cck_pwrtable = (chan == 14) ? urtw_8225v2b_txpwr_cck_ch14 : urtw_8225v2b_txpwr_cck; if (sc->sc_flags & URTW_RTL8187B_REV_B) cck_pwrtable += (cck_pwrlvl <= 6) ? 0 : ((cck_pwrlvl <= 11) ? 8 : 16); else cck_pwrtable += (cck_pwrlvl <= 5) ? 0 : ((cck_pwrlvl <= 11) ? 8 : ((cck_pwrlvl <= 17) ? 16 : 24)); for (i = 0; i < 8; i++) urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]); urtw_write8_m(sc, URTW_TX_GAIN_CCK, urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl] << 1); urtw_pause_ms(sc, 1); /* OFDM power setting */ ofdm_pwrlvl = (ofdm_pwrlvl > 15) ? ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 17 : 25) : (ofdm_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 2 : 10)); ofdm_pwrlvl += sc->sc_txpwr_ofdm_base; ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; urtw_write8_m(sc, URTW_TX_GAIN_OFDM, urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl] << 1); if (sc->sc_flags & URTW_RTL8187B_REV_B) { if (ofdm_pwrlvl <= 11) { urtw_8187_write_phy_ofdm(sc, 0x87, 0x60); urtw_8187_write_phy_ofdm(sc, 0x89, 0x60); } else { urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c); urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c); } } else { if (ofdm_pwrlvl <= 11) { urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c); urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c); } else if (ofdm_pwrlvl <= 17) { urtw_8187_write_phy_ofdm(sc, 0x87, 0x54); urtw_8187_write_phy_ofdm(sc, 0x89, 0x54); } else { urtw_8187_write_phy_ofdm(sc, 0x87, 0x50); urtw_8187_write_phy_ofdm(sc, 0x89, 0x50); } } urtw_pause_ms(sc, 1); fail: return (error); } static usb_error_t urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, val | 0xfe00); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(uint8_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_write8e(struct urtw_softc *sc, int val, uint8_t data) { struct usb_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, val | 0xfe00); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(uint8_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val) { uint8_t data; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); urtw_write32_m(sc, URTW_ANAPARAM, val); urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; fail: return (error); } static usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val) { uint8_t data; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); urtw_write32_m(sc, URTW_ANAPARAM2, val); urtw_read8_m(sc, URTW_CONFIG3, &data); urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; fail: return (error); } static usb_error_t urtw_intr_enable(struct urtw_softc *sc) { usb_error_t error; urtw_write16_m(sc, URTW_INTR_MASK, 0xffff); fail: return (error); } static usb_error_t urtw_intr_disable(struct urtw_softc *sc) { usb_error_t error; urtw_write16_m(sc, URTW_INTR_MASK, 0); fail: return (error); } static usb_error_t urtw_reset(struct urtw_softc *sc) { uint8_t data; usb_error_t error; error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); if (error) goto fail; error = urtw_intr_disable(sc); if (error) goto fail; urtw_pause_ms(sc, 100); error = urtw_write8e(sc, 0x18, 0x10); if (error != 0) goto fail; error = urtw_write8e(sc, 0x18, 0x11); if (error != 0) goto fail; error = urtw_write8e(sc, 0x18, 0x00); if (error != 0) goto fail; urtw_pause_ms(sc, 100); urtw_read8_m(sc, URTW_CMD, &data); data = (data & 0x2) | URTW_CMD_RST; urtw_write8_m(sc, URTW_CMD, data); urtw_pause_ms(sc, 100); urtw_read8_m(sc, URTW_CMD, &data); if (data & URTW_CMD_RST) { device_printf(sc->sc_dev, "reset timeout\n"); goto fail; } error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD); if (error) goto fail; urtw_pause_ms(sc, 100); error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); if (error) goto fail; fail: return (error); } static usb_error_t urtw_led_ctl(struct urtw_softc *sc, int mode) { usb_error_t error = 0; switch (sc->sc_strategy) { case URTW_SW_LED_MODE0: error = urtw_led_mode0(sc, mode); break; case URTW_SW_LED_MODE1: error = urtw_led_mode1(sc, mode); break; case URTW_SW_LED_MODE2: error = urtw_led_mode2(sc, mode); break; case URTW_SW_LED_MODE3: error = urtw_led_mode3(sc, mode); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED mode %d\n", sc->sc_strategy); error = USB_ERR_INVAL; break; } return (error); } static usb_error_t urtw_led_mode0(struct urtw_softc *sc, int mode) { switch (mode) { case URTW_LED_CTL_POWER_ON: sc->sc_gpio_ledstate = URTW_LED_POWER_ON_BLINK; break; case URTW_LED_CTL_TX: if (sc->sc_gpio_ledinprogress == 1) return (0); sc->sc_gpio_ledstate = URTW_LED_BLINK_NORMAL; sc->sc_gpio_blinktime = 2; break; case URTW_LED_CTL_LINK: sc->sc_gpio_ledstate = URTW_LED_ON; break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED mode 0x%x", mode); return (USB_ERR_INVAL); } switch (sc->sc_gpio_ledstate) { case URTW_LED_ON: if (sc->sc_gpio_ledinprogress != 0) break; urtw_led_on(sc, URTW_LED_GPIO); break; case URTW_LED_BLINK_NORMAL: if (sc->sc_gpio_ledinprogress != 0) break; sc->sc_gpio_ledinprogress = 1; sc->sc_gpio_blinkstate = (sc->sc_gpio_ledon != 0) ? URTW_LED_OFF : URTW_LED_ON; usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc); break; case URTW_LED_POWER_ON_BLINK: urtw_led_on(sc, URTW_LED_GPIO); urtw_pause_ms(sc, 100); urtw_led_off(sc, URTW_LED_GPIO); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unknown LED status 0x%x", sc->sc_gpio_ledstate); return (USB_ERR_INVAL); } return (0); } static usb_error_t urtw_led_mode1(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_mode2(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_mode3(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_on(struct urtw_softc *sc, int type) { usb_error_t error; if (type == URTW_LED_GPIO) { switch (sc->sc_gpio_ledpin) { case URTW_LED_PIN_GPIO0: urtw_write8_m(sc, URTW_GPIO, 0x01); urtw_write8_m(sc, URTW_GP_ENABLE, 0x00); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED PIN type 0x%x", sc->sc_gpio_ledpin); error = USB_ERR_INVAL; goto fail; } } else { DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED type 0x%x", type); error = USB_ERR_INVAL; goto fail; } sc->sc_gpio_ledon = 1; fail: return (error); } static usb_error_t urtw_led_off(struct urtw_softc *sc, int type) { usb_error_t error; if (type == URTW_LED_GPIO) { switch (sc->sc_gpio_ledpin) { case URTW_LED_PIN_GPIO0: urtw_write8_m(sc, URTW_GPIO, URTW_GPIO_DATA_MAGIC1); urtw_write8_m(sc, URTW_GP_ENABLE, URTW_GP_ENABLE_DATA_MAGIC1); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED PIN type 0x%x", sc->sc_gpio_ledpin); error = USB_ERR_INVAL; goto fail; } } else { DPRINTF(sc, URTW_DEBUG_STATE, "unsupported LED type 0x%x", type); error = USB_ERR_INVAL; goto fail; } sc->sc_gpio_ledon = 0; fail: return (error); } static void urtw_led_ch(void *arg) { struct urtw_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; ieee80211_runtask(ic, &sc->sc_led_task); } static void urtw_ledtask(void *arg, int pending) { struct urtw_softc *sc = arg; if (sc->sc_strategy != URTW_SW_LED_MODE0) { DPRINTF(sc, URTW_DEBUG_STATE, "could not process a LED strategy 0x%x", sc->sc_strategy); return; } URTW_LOCK(sc); urtw_led_blink(sc); URTW_UNLOCK(sc); } static usb_error_t urtw_led_blink(struct urtw_softc *sc) { uint8_t ing = 0; if (sc->sc_gpio_blinkstate == URTW_LED_ON) urtw_led_on(sc, URTW_LED_GPIO); else urtw_led_off(sc, URTW_LED_GPIO); sc->sc_gpio_blinktime--; if (sc->sc_gpio_blinktime == 0) ing = 1; else { if (sc->sc_gpio_ledstate != URTW_LED_BLINK_NORMAL && sc->sc_gpio_ledstate != URTW_LED_BLINK_SLOWLY && sc->sc_gpio_ledstate != URTW_LED_BLINK_CM3) ing = 1; } if (ing == 1) { if (sc->sc_gpio_ledstate == URTW_LED_ON && sc->sc_gpio_ledon == 0) urtw_led_on(sc, URTW_LED_GPIO); else if (sc->sc_gpio_ledstate == URTW_LED_OFF && sc->sc_gpio_ledon == 1) urtw_led_off(sc, URTW_LED_GPIO); sc->sc_gpio_blinktime = 0; sc->sc_gpio_ledinprogress = 0; return (0); } sc->sc_gpio_blinkstate = (sc->sc_gpio_blinkstate != URTW_LED_ON) ? URTW_LED_ON : URTW_LED_OFF; switch (sc->sc_gpio_ledstate) { case URTW_LED_BLINK_NORMAL: usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc); break; default: DPRINTF(sc, URTW_DEBUG_STATE, "unknown LED status 0x%x", sc->sc_gpio_ledstate); return (USB_ERR_INVAL); } return (0); } static usb_error_t urtw_rx_enable(struct urtw_softc *sc) { uint8_t data; usb_error_t error; usbd_transfer_start((sc->sc_flags & URTW_RTL8187B) ? sc->sc_xfer[URTW_8187B_BULK_RX] : sc->sc_xfer[URTW_8187L_BULK_RX]); error = urtw_rx_setconf(sc); if (error != 0) goto fail; if ((sc->sc_flags & URTW_RTL8187B) == 0) { urtw_read8_m(sc, URTW_CMD, &data); urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE); } fail: return (error); } static usb_error_t urtw_tx_enable(struct urtw_softc *sc) { uint8_t data8; uint32_t data; usb_error_t error; if (sc->sc_flags & URTW_RTL8187B) { urtw_read32_m(sc, URTW_TX_CONF, &data); data &= ~URTW_TX_LOOPBACK_MASK; data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); data &= ~URTW_TX_SWPLCPLEN; data |= URTW_TX_HW_SEQNUM | URTW_TX_DISREQQSIZE | (7 << 8) | /* short retry limit */ (7 << 0) | /* long retry limit */ (7 << 21); /* MAX TX DMA */ urtw_write32_m(sc, URTW_TX_CONF, data); urtw_read8_m(sc, URTW_MSR, &data8); data8 |= URTW_MSR_LINK_ENEDCA; urtw_write8_m(sc, URTW_MSR, data8); return (error); } urtw_read8_m(sc, URTW_CW_CONF, &data8); data8 &= ~(URTW_CW_CONF_PERPACKET_CW | URTW_CW_CONF_PERPACKET_RETRY); urtw_write8_m(sc, URTW_CW_CONF, data8); urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); data8 &= ~URTW_TX_AGC_CTL_PERPACKET_GAIN; data8 &= ~URTW_TX_AGC_CTL_PERPACKET_ANTSEL; data8 &= ~URTW_TX_AGC_CTL_FEEDBACK_ANT; urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); urtw_read32_m(sc, URTW_TX_CONF, &data); data &= ~URTW_TX_LOOPBACK_MASK; data |= URTW_TX_LOOPBACK_NONE; data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); data |= sc->sc_tx_retry << URTW_TX_DPRETRY_SHIFT; data |= sc->sc_rts_retry << URTW_TX_RTSRETRY_SHIFT; data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); data |= URTW_TX_MXDMA_2048 | URTW_TX_CWMIN | URTW_TX_DISCW; data &= ~URTW_TX_SWPLCPLEN; data |= URTW_TX_NOICV; urtw_write32_m(sc, URTW_TX_CONF, data); urtw_read8_m(sc, URTW_CMD, &data8); urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE); fail: return (error); } static usb_error_t urtw_rx_setconf(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t data; usb_error_t error; urtw_read32_m(sc, URTW_RX, &data); data = data &~ URTW_RX_FILTER_MASK; if (sc->sc_flags & URTW_RTL8187B) { data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA | URTW_RX_FILTER_MCAST | URTW_RX_FILTER_BCAST | URTW_RX_FIFO_THRESHOLD_NONE | URTW_MAX_RX_DMA_2048 | URTW_RX_AUTORESETPHY | URTW_RCR_ONLYERLPKT; } else { data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA; data = data | URTW_RX_FILTER_BCAST | URTW_RX_FILTER_MCAST; if (ic->ic_opmode == IEEE80211_M_MONITOR) { data = data | URTW_RX_FILTER_ICVERR; data = data | URTW_RX_FILTER_PWR; } if (sc->sc_crcmon == 1 && ic->ic_opmode == IEEE80211_M_MONITOR) data = data | URTW_RX_FILTER_CRCERR; data = data &~ URTW_RX_FIFO_THRESHOLD_MASK; data = data | URTW_RX_FIFO_THRESHOLD_NONE | URTW_RX_AUTORESETPHY; data = data &~ URTW_MAX_RX_DMA_MASK; data = data | URTW_MAX_RX_DMA_2048 | URTW_RCR_ONLYERLPKT; } /* XXX allmulti should not be checked here... */ if (ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_promisc > 0 || ic->ic_allmulti > 0) { data = data | URTW_RX_FILTER_CTL; data = data | URTW_RX_FILTER_ALLMAC; } else { data = data | URTW_RX_FILTER_NICMAC; data = data | URTW_RX_CHECK_BSSID; } urtw_write32_m(sc, URTW_RX, data); fail: return (error); } static struct mbuf * urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p, int8_t *nf_p) { int actlen, flen, rssi; struct ieee80211_frame *wh; struct mbuf *m, *mnew; struct urtw_softc *sc = data->sc; struct ieee80211com *ic = &sc->sc_ic; uint8_t noise = 0, rate; uint64_t mactime; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (sc->sc_flags & URTW_RTL8187B) { struct urtw_8187b_rxhdr *rx; if (actlen < sizeof(*rx) + IEEE80211_ACK_LEN) goto fail; rx = (struct urtw_8187b_rxhdr *)(data->buf + (actlen - (sizeof(struct urtw_8187b_rxhdr)))); flen = le32toh(rx->flag) & 0xfff; if (flen > actlen - sizeof(*rx)) goto fail; rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; /* XXX correct? */ rssi = rx->rssi & URTW_RX_RSSI_MASK; noise = rx->noise; if (ieee80211_radiotap_active(ic)) mactime = rx->mactime; } else { struct urtw_8187l_rxhdr *rx; if (actlen < sizeof(*rx) + IEEE80211_ACK_LEN) goto fail; rx = (struct urtw_8187l_rxhdr *)(data->buf + (actlen - (sizeof(struct urtw_8187l_rxhdr)))); flen = le32toh(rx->flag) & 0xfff; if (flen > actlen - sizeof(*rx)) goto fail; rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; /* XXX correct? */ rssi = rx->rssi & URTW_RX_8187L_RSSI_MASK; noise = rx->noise; if (ieee80211_radiotap_active(ic)) mactime = rx->mactime; } if (flen < IEEE80211_ACK_LEN) goto fail; mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) goto fail; m = data->m; data->m = mnew; data->buf = mtod(mnew, uint8_t *); /* finalize mbuf */ m->m_pkthdr.len = m->m_len = flen - IEEE80211_CRC_LEN; if (ieee80211_radiotap_active(ic)) { struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_tsf = mactime; tap->wr_flags = 0; tap->wr_dbm_antsignal = (int8_t)rssi; } wh = mtod(m, struct ieee80211_frame *); if (IEEE80211_IS_DATA(wh)) sc->sc_currate = (rate > 0) ? rate : sc->sc_currate; *rssi_p = rssi; *nf_p = noise; /* XXX correct? */ return (m); fail: counter_u64_add(ic->ic_ierrors, 1); return (NULL); } static void urtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct mbuf *m = NULL; struct urtw_data *data; int8_t nf = -95; int rssi = 1; URTW_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_rx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); m = urtw_rxeof(xfer, data, &rssi, &nf); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) { KASSERT(m == NULL, ("mbuf isn't NULL")); return; } STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ URTW_UNLOCK(sc); if (m != NULL) { if (m->m_pkthdr.len >= sizeof(struct ieee80211_frame_min)) { ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); } else ni = NULL; if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, nf); /* node is no longer needed */ ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, nf); m = NULL; } URTW_LOCK(sc); break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&sc->sc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto setup; } break; } } #define URTW_STATUS_TYPE_TXCLOSE 1 #define URTW_STATUS_TYPE_BEACON_INTR 0 static void urtw_txstatus_eof(struct usb_xfer *xfer) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; int actlen, type, pktretry; uint64_t val; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (actlen != sizeof(uint64_t)) return; val = le64toh(sc->sc_txstatus); type = (val >> 30) & 0x3; if (type == URTW_STATUS_TYPE_TXCLOSE) { pktretry = val & 0xff; if (pktretry == URTW_TX_MAXRETRY) counter_u64_add(ic->ic_oerrors, 1); DPRINTF(sc, URTW_DEBUG_TXSTATUS, "pktretry %d seq %#x\n", pktretry, (val >> 16) & 0xff); } } static void urtw_bulk_tx_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; void *dma_buf = usbd_xfer_get_frame_buffer(xfer, 0); URTW_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: urtw_txstatus_eof(xfer); /* FALLTHROUGH */ case USB_ST_SETUP: setup: memcpy(dma_buf, &sc->sc_txstatus, sizeof(uint64_t)); usbd_xfer_set_frame_len(xfer, 0, sizeof(uint64_t)); usbd_transfer_submit(xfer); break; default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto setup; } break; } } static void urtw_txeof(struct usb_xfer *xfer, struct urtw_data *data) { struct urtw_softc *sc = usbd_xfer_softc(xfer); URTW_ASSERT_LOCKED(sc); if (data->m) { /* XXX status? */ ieee80211_tx_complete(data->ni, data->m, 0); data->m = NULL; data->ni = NULL; } sc->sc_txtimer = 0; } static void urtw_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct urtw_data *data; URTW_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); urtw_txeof(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: setup: data = STAILQ_FIRST(&sc->sc_tx_pending); if (data == NULL) { DPRINTF(sc, URTW_DEBUG_XMIT, "%s: empty pending queue\n", __func__); return; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(xfer); urtw_start(sc); break; default: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto setup; if (data->ni != NULL) { if_inc_counter(data->ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(data->ni); data->ni = NULL; } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto setup; } break; } } static struct urtw_data * _urtw_getbuf(struct urtw_softc *sc) { struct urtw_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); else bf = NULL; if (bf == NULL) DPRINTF(sc, URTW_DEBUG_XMIT, "%s: %s\n", __func__, "out of xmit buffers"); return (bf); } static struct urtw_data * urtw_getbuf(struct urtw_softc *sc) { struct urtw_data *bf; URTW_ASSERT_LOCKED(sc); bf = _urtw_getbuf(sc); if (bf == NULL) DPRINTF(sc, URTW_DEBUG_XMIT, "%s: stop queue\n", __func__); return (bf); } static int urtw_isbmode(uint16_t rate) { return ((rate <= 22 && rate != 12 && rate != 18) || rate == 44) ? (1) : (0); } static uint16_t urtw_rate2dbps(uint16_t rate) { switch(rate) { case 12: case 18: case 24: case 36: case 48: case 72: case 96: case 108: return (rate * 2); default: break; } return (24); } static int urtw_compute_txtime(uint16_t framelen, uint16_t rate, uint8_t ismgt, uint8_t isshort) { uint16_t ceiling, frametime, n_dbps; if (urtw_isbmode(rate)) { if (ismgt || !isshort || rate == 2) frametime = (uint16_t)(144 + 48 + (framelen * 8 / (rate / 2))); else frametime = (uint16_t)(72 + 24 + (framelen * 8 / (rate / 2))); if ((framelen * 8 % (rate / 2)) != 0) frametime++; } else { n_dbps = urtw_rate2dbps(rate); ceiling = (16 + 8 * framelen + 6) / n_dbps + (((16 + 8 * framelen + 6) % n_dbps) ? 1 : 0); frametime = (uint16_t)(16 + 4 + 4 * ceiling + 6); } return (frametime); } /* * Callback from the 802.11 layer to update the * slot time based on the current setting. */ static void urtw_updateslot(struct ieee80211com *ic) { struct urtw_softc *sc = ic->ic_softc; ieee80211_runtask(ic, &sc->sc_updateslot_task); } static void urtw_updateslottask(void *arg, int pending) { struct urtw_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; int error; URTW_LOCK(sc); if ((sc->sc_flags & URTW_RUNNING) == 0) { URTW_UNLOCK(sc); return; } if (sc->sc_flags & URTW_RTL8187B) { urtw_write8_m(sc, URTW_SIFS, 0x22); if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SHSLOT); else urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SLOT); urtw_write8_m(sc, URTW_8187B_EIFS, 0x5b); urtw_write8_m(sc, URTW_CARRIER_SCOUNT, 0x5b); } else { urtw_write8_m(sc, URTW_SIFS, 0x22); if (sc->sc_state == IEEE80211_S_ASSOC && ic->ic_flags & IEEE80211_F_SHSLOT) urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SHSLOT); else urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_SLOT); if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) { urtw_write8_m(sc, URTW_DIFS, 0x14); urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14); urtw_write8_m(sc, URTW_CW_VAL, 0x73); } else { urtw_write8_m(sc, URTW_DIFS, 0x24); urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24); urtw_write8_m(sc, URTW_CW_VAL, 0xa5); } } fail: URTW_UNLOCK(sc); } static void urtw_sysctl_node(struct urtw_softc *sc) { #define URTW_SYSCTL_STAT_ADD32(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child, *parent; struct sysctl_oid *tree; struct urtw_stats *stats = &sc->sc_stats; ctx = device_get_sysctl_ctx(sc->sc_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "URTW statistics"); parent = SYSCTL_CHILDREN(tree); /* Tx statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Tx MAC statistics"); child = SYSCTL_CHILDREN(tree); URTW_SYSCTL_STAT_ADD32(ctx, child, "1m", &stats->txrates[0], "1 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "2m", &stats->txrates[1], "2 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "5.5m", &stats->txrates[2], "5.5 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "6m", &stats->txrates[4], "6 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "9m", &stats->txrates[5], "9 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "11m", &stats->txrates[3], "11 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "12m", &stats->txrates[6], "12 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "18m", &stats->txrates[7], "18 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "24m", &stats->txrates[8], "24 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "36m", &stats->txrates[9], "36 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "48m", &stats->txrates[10], "48 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "54m", &stats->txrates[11], "54 Mbit/s"); #undef URTW_SYSCTL_STAT_ADD32 } static device_method_t urtw_methods[] = { DEVMETHOD(device_probe, urtw_match), DEVMETHOD(device_attach, urtw_attach), DEVMETHOD(device_detach, urtw_detach), DEVMETHOD_END }; static driver_t urtw_driver = { .name = "urtw", .methods = urtw_methods, .size = sizeof(struct urtw_softc) }; DRIVER_MODULE(urtw, uhub, urtw_driver, NULL, NULL); MODULE_DEPEND(urtw, wlan, 1, 1, 1); MODULE_DEPEND(urtw, usb, 1, 1, 1); MODULE_VERSION(urtw, 1); USB_PNP_HOST_INFO(urtw_devs); diff --git a/sys/dev/usb/wlan/if_zyd.c b/sys/dev/usb/wlan/if_zyd.c index 1a698caef3c5..7affdcdce089 100644 --- a/sys/dev/usb/wlan/if_zyd.c +++ b/sys/dev/usb/wlan/if_zyd.c @@ -1,2911 +1,2915 @@ /* $OpenBSD: if_zyd.c,v 1.52 2007/02/11 00:08:04 jsg Exp $ */ /* $NetBSD: if_zyd.c,v 1.7 2007/06/21 04:04:29 kiyohara Exp $ */ /*- * Copyright (c) 2006 by Damien Bergamini * Copyright (c) 2006 by Florian Stoehr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * ZyDAS ZD1211/ZD1211B USB WLAN driver. */ #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 #ifdef INET #include #include #include #include #include #endif #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #ifdef USB_DEBUG static int zyd_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, zyd, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB zyd"); SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RWTUN, &zyd_debug, 0, "zyd debug level"); enum { ZYD_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ ZYD_DEBUG_RECV = 0x00000002, /* basic recv operation */ ZYD_DEBUG_RESET = 0x00000004, /* reset processing */ ZYD_DEBUG_INIT = 0x00000008, /* device init */ ZYD_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */ ZYD_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */ ZYD_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */ ZYD_DEBUG_STAT = 0x00000080, /* statistic */ ZYD_DEBUG_FW = 0x00000100, /* firmware */ ZYD_DEBUG_CMD = 0x00000200, /* fw commands */ ZYD_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (zyd_debug & (m)) \ printf("%s: " fmt, __func__, ## __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, m, fmt, ...) do { \ (void) sc; \ } while (0) #endif #define zyd_do_request(sc,req,data) \ usbd_do_request_flags((sc)->sc_udev, &(sc)->sc_mtx, req, data, 0, NULL, 5000) static device_probe_t zyd_match; static device_attach_t zyd_attach; static device_detach_t zyd_detach; static usb_callback_t zyd_intr_read_callback; static usb_callback_t zyd_intr_write_callback; static usb_callback_t zyd_bulk_read_callback; static usb_callback_t zyd_bulk_write_callback; static struct ieee80211vap *zyd_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void zyd_vap_delete(struct ieee80211vap *); static void zyd_tx_free(struct zyd_tx_data *, int); static void zyd_setup_tx_list(struct zyd_softc *); static void zyd_unsetup_tx_list(struct zyd_softc *); static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int, void *, int, int); static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *); static int zyd_read32(struct zyd_softc *, uint16_t, uint32_t *); static int zyd_write16(struct zyd_softc *, uint16_t, uint16_t); static int zyd_write32(struct zyd_softc *, uint16_t, uint32_t); static int zyd_rfwrite(struct zyd_softc *, uint32_t); static int zyd_lock_phy(struct zyd_softc *); static int zyd_unlock_phy(struct zyd_softc *); static int zyd_rf_attach(struct zyd_softc *, uint8_t); static const char *zyd_rf_name(uint8_t); static int zyd_hw_init(struct zyd_softc *); static int zyd_read_pod(struct zyd_softc *); static int zyd_read_eeprom(struct zyd_softc *); static int zyd_get_macaddr(struct zyd_softc *); static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *); static int zyd_set_bssid(struct zyd_softc *, const uint8_t *); static int zyd_switch_radio(struct zyd_softc *, int); static int zyd_set_led(struct zyd_softc *, int, int); static void zyd_set_multi(struct zyd_softc *); static void zyd_update_mcast(struct ieee80211com *); static int zyd_set_rxfilter(struct zyd_softc *); static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *); static int zyd_set_beacon_interval(struct zyd_softc *, int); static void zyd_rx_data(struct usb_xfer *, int, uint16_t); static int zyd_tx_start(struct zyd_softc *, struct mbuf *, struct ieee80211_node *); static int zyd_transmit(struct ieee80211com *, struct mbuf *); static void zyd_start(struct zyd_softc *); static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void zyd_parent(struct ieee80211com *); static void zyd_init_locked(struct zyd_softc *); static void zyd_stop(struct zyd_softc *); static int zyd_loadfirmware(struct zyd_softc *); static void zyd_scan_start(struct ieee80211com *); static void zyd_scan_end(struct ieee80211com *); static void zyd_getradiocaps(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static void zyd_set_channel(struct ieee80211com *); static int zyd_rfmd_init(struct zyd_rf *); static int zyd_rfmd_switch_radio(struct zyd_rf *, int); static int zyd_rfmd_set_channel(struct zyd_rf *, uint8_t); static int zyd_al2230_init(struct zyd_rf *); static int zyd_al2230_switch_radio(struct zyd_rf *, int); static int zyd_al2230_set_channel(struct zyd_rf *, uint8_t); static int zyd_al2230_set_channel_b(struct zyd_rf *, uint8_t); static int zyd_al2230_init_b(struct zyd_rf *); static int zyd_al7230B_init(struct zyd_rf *); static int zyd_al7230B_switch_radio(struct zyd_rf *, int); static int zyd_al7230B_set_channel(struct zyd_rf *, uint8_t); static int zyd_al2210_init(struct zyd_rf *); static int zyd_al2210_switch_radio(struct zyd_rf *, int); static int zyd_al2210_set_channel(struct zyd_rf *, uint8_t); static int zyd_gct_init(struct zyd_rf *); static int zyd_gct_switch_radio(struct zyd_rf *, int); static int zyd_gct_set_channel(struct zyd_rf *, uint8_t); static int zyd_gct_mode(struct zyd_rf *); static int zyd_gct_set_channel_synth(struct zyd_rf *, int, int); static int zyd_gct_write(struct zyd_rf *, uint16_t); static int zyd_gct_txgain(struct zyd_rf *, uint8_t); static int zyd_maxim2_init(struct zyd_rf *); static int zyd_maxim2_switch_radio(struct zyd_rf *, int); static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t); static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY; static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB; /* various supported device vendors/products */ #define ZYD_ZD1211 0 #define ZYD_ZD1211B 1 #define ZYD_ZD1211_DEV(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, ZYD_ZD1211) } #define ZYD_ZD1211B_DEV(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, ZYD_ZD1211B) } static const STRUCT_USB_HOST_ID zyd_devs[] = { /* ZYD_ZD1211 */ ZYD_ZD1211_DEV(3COM2, 3CRUSB10075), ZYD_ZD1211_DEV(ABOCOM, WL54), ZYD_ZD1211_DEV(ASUS, WL159G), ZYD_ZD1211_DEV(CYBERTAN, TG54USB), ZYD_ZD1211_DEV(DRAYTEK, VIGOR550), ZYD_ZD1211_DEV(PLANEX2, GWUS54GD), ZYD_ZD1211_DEV(PLANEX2, GWUS54GZL), ZYD_ZD1211_DEV(PLANEX3, GWUS54GZ), ZYD_ZD1211_DEV(PLANEX3, GWUS54MINI), ZYD_ZD1211_DEV(SAGEM, XG760A), ZYD_ZD1211_DEV(SENAO, NUB8301), ZYD_ZD1211_DEV(SITECOMEU, WL113), ZYD_ZD1211_DEV(SWEEX, ZD1211), ZYD_ZD1211_DEV(TEKRAM, QUICKWLAN), ZYD_ZD1211_DEV(TEKRAM, ZD1211_1), ZYD_ZD1211_DEV(TEKRAM, ZD1211_2), ZYD_ZD1211_DEV(TWINMOS, G240), ZYD_ZD1211_DEV(UMEDIA, ALL0298V2), ZYD_ZD1211_DEV(UMEDIA, TEW429UB_A), ZYD_ZD1211_DEV(UMEDIA, TEW429UB), ZYD_ZD1211_DEV(WISTRONNEWEB, UR055G), ZYD_ZD1211_DEV(ZCOM, ZD1211), ZYD_ZD1211_DEV(ZYDAS, ZD1211), ZYD_ZD1211_DEV(ZYXEL, AG225H), ZYD_ZD1211_DEV(ZYXEL, ZYAIRG220), ZYD_ZD1211_DEV(ZYXEL, G200V2), /* ZYD_ZD1211B */ ZYD_ZD1211B_DEV(ACCTON, SMCWUSBG_NF), ZYD_ZD1211B_DEV(ACCTON, SMCWUSBG), ZYD_ZD1211B_DEV(ACCTON, ZD1211B), ZYD_ZD1211B_DEV(ASUS, A9T_WIFI), ZYD_ZD1211B_DEV(BELKIN, F5D7050_V4000), ZYD_ZD1211B_DEV(BELKIN, ZD1211B), ZYD_ZD1211B_DEV(CISCOLINKSYS, WUSBF54G), ZYD_ZD1211B_DEV(FIBERLINE, WL430U), ZYD_ZD1211B_DEV(MELCO, KG54L), ZYD_ZD1211B_DEV(PHILIPS, SNU5600), ZYD_ZD1211B_DEV(PLANEX2, GW_US54GXS), ZYD_ZD1211B_DEV(SAGEM, XG76NA), ZYD_ZD1211B_DEV(SITECOMEU, ZD1211B), ZYD_ZD1211B_DEV(UMEDIA, TEW429UBC1), ZYD_ZD1211B_DEV(USR, USR5423), ZYD_ZD1211B_DEV(VTECH, ZD1211B), ZYD_ZD1211B_DEV(ZCOM, ZD1211B), ZYD_ZD1211B_DEV(ZYDAS, ZD1211B), ZYD_ZD1211B_DEV(ZYXEL, M202), ZYD_ZD1211B_DEV(ZYXEL, G202), ZYD_ZD1211B_DEV(ZYXEL, G220V2) }; static const struct usb_config zyd_config[ZYD_N_TRANSFER] = { [ZYD_BULK_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = ZYD_MAX_TXBUFSZ, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = zyd_bulk_write_callback, .ep_index = 0, .timeout = 10000, /* 10 seconds */ }, [ZYD_BULK_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = ZYX_MAX_RXBUFSZ, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = zyd_bulk_read_callback, .ep_index = 0, }, [ZYD_INTR_WR] = { .type = UE_BULK_INTR, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = sizeof(struct zyd_cmd), .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = zyd_intr_write_callback, .timeout = 1000, /* 1 second */ .ep_index = 1, }, [ZYD_INTR_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = sizeof(struct zyd_cmd), .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = zyd_intr_read_callback, }, }; #define zyd_read16_m(sc, val, data) do { \ error = zyd_read16(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define zyd_write16_m(sc, val, data) do { \ error = zyd_write16(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define zyd_read32_m(sc, val, data) do { \ error = zyd_read32(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) #define zyd_write32_m(sc, val, data) do { \ error = zyd_write32(sc, val, data); \ if (error != 0) \ goto fail; \ } while (0) static int zyd_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != ZYD_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa)); } static int zyd_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct zyd_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; uint8_t iface_index; int error; if (uaa->info.bcdDevice < 0x4330) { device_printf(dev, "device version mismatch: 0x%X " "(only >= 43.30 supported)\n", uaa->info.bcdDevice); return (EINVAL); } device_set_usb_desc(dev); sc->sc_dev = dev; sc->sc_udev = uaa->device; sc->sc_macrev = USB_GET_DRIVER_INFO(uaa); mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, MTX_DEF); STAILQ_INIT(&sc->sc_rqh); mbufq_init(&sc->sc_snd, ifqmaxlen); iface_index = ZYD_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, zyd_config, ZYD_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); goto detach; } ZYD_LOCK(sc); if ((error = zyd_get_macaddr(sc)) != 0) { device_printf(sc->sc_dev, "could not read EEPROM\n"); ZYD_UNLOCK(sc); goto detach; } ZYD_UNLOCK(sc); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(dev); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_WPA /* 802.11i */ ; + ic->ic_flags_ext |= IEEE80211_FEXT_SEQNO_OFFLOAD; + zyd_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); ieee80211_ifattach(ic); ic->ic_raw_xmit = zyd_raw_xmit; ic->ic_scan_start = zyd_scan_start; ic->ic_scan_end = zyd_scan_end; ic->ic_getradiocaps = zyd_getradiocaps; ic->ic_set_channel = zyd_set_channel; ic->ic_vap_create = zyd_vap_create; ic->ic_vap_delete = zyd_vap_delete; ic->ic_update_mcast = zyd_update_mcast; ic->ic_update_promisc = zyd_update_mcast; ic->ic_parent = zyd_parent; ic->ic_transmit = zyd_transmit; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), ZYD_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), ZYD_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return (0); detach: zyd_detach(dev); return (ENXIO); /* failure */ } static void zyd_drain_mbufq(struct zyd_softc *sc) { struct mbuf *m; struct ieee80211_node *ni; ZYD_LOCK_ASSERT(sc, MA_OWNED); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; ieee80211_free_node(ni); m_freem(m); } } static int zyd_detach(device_t dev) { struct zyd_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; unsigned x; /* * Prevent further allocations from RX/TX data * lists and ioctls: */ ZYD_LOCK(sc); sc->sc_flags |= ZYD_FLAG_DETACHED; zyd_drain_mbufq(sc); STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); ZYD_UNLOCK(sc); /* drain USB transfers */ for (x = 0; x != ZYD_N_TRANSFER; x++) usbd_transfer_drain(sc->sc_xfer[x]); /* free TX list, if any */ ZYD_LOCK(sc); zyd_unsetup_tx_list(sc); ZYD_UNLOCK(sc); /* free USB transfers and some data buffers */ usbd_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER); if (ic->ic_softc == sc) ieee80211_ifdetach(ic); mtx_destroy(&sc->sc_mtx); return (0); } static struct ieee80211vap * zyd_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct zyd_vap *zvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); zvp = malloc(sizeof(struct zyd_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &zvp->vap; /* enable s/w bmiss handling for sta mode */ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(zvp, M_80211_VAP); return (NULL); } /* override state transition machine */ zvp->newstate = vap->iv_newstate; vap->iv_newstate = zyd_newstate; ieee80211_ratectl_init(vap); ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void zyd_vap_delete(struct ieee80211vap *vap) { struct zyd_vap *zvp = ZYD_VAP(vap); ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); free(zvp, M_80211_VAP); } static void zyd_tx_free(struct zyd_tx_data *data, int txerr) { struct zyd_softc *sc = data->sc; if (data->m != NULL) { ieee80211_tx_complete(data->ni, data->m, txerr); data->m = NULL; data->ni = NULL; } STAILQ_INSERT_TAIL(&sc->tx_free, data, next); sc->tx_nfree++; } static void zyd_setup_tx_list(struct zyd_softc *sc) { struct zyd_tx_data *data; int i; sc->tx_nfree = 0; STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); for (i = 0; i < ZYD_TX_LIST_CNT; i++) { data = &sc->tx_data[i]; data->sc = sc; STAILQ_INSERT_TAIL(&sc->tx_free, data, next); sc->tx_nfree++; } } static void zyd_unsetup_tx_list(struct zyd_softc *sc) { struct zyd_tx_data *data; int i; /* make sure any subsequent use of the queues will fail */ sc->tx_nfree = 0; STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); /* free up all node references and mbufs */ for (i = 0; i < ZYD_TX_LIST_CNT; i++) { data = &sc->tx_data[i]; if (data->m != NULL) { m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct zyd_vap *zvp = ZYD_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct zyd_softc *sc = ic->ic_softc; int error; DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); ZYD_LOCK(sc); switch (nstate) { case IEEE80211_S_AUTH: zyd_set_chan(sc, ic->ic_curchan); break; case IEEE80211_S_RUN: if (vap->iv_opmode == IEEE80211_M_MONITOR) break; /* turn link LED on */ error = zyd_set_led(sc, ZYD_LED1, 1); if (error != 0) break; /* make data LED blink upon Tx */ zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1); IEEE80211_ADDR_COPY(sc->sc_bssid, vap->iv_bss->ni_bssid); zyd_set_bssid(sc, sc->sc_bssid); break; default: break; } fail: ZYD_UNLOCK(sc); IEEE80211_LOCK(ic); return (zvp->newstate(vap, nstate, arg)); } /* * Callback handler for interrupt transfer */ static void zyd_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct zyd_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; struct zyd_cmd *cmd = &sc->sc_ibuf; struct usb_page_cache *pc; int datalen; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, cmd, sizeof(*cmd)); switch (le16toh(cmd->code)) { case ZYD_NOTIF_RETRYSTATUS: { struct zyd_notif_retry *retry = (struct zyd_notif_retry *)cmd->data; uint16_t count = le16toh(retry->count); DPRINTF(sc, ZYD_DEBUG_TX_PROC, "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n", le16toh(retry->rate), ether_sprintf(retry->macaddr), count & 0xff, count); /* * Find the node to which the packet was sent and * update its retry statistics. In BSS mode, this node * is the AP we're associated to so no lookup is * actually needed. */ ni = ieee80211_find_txnode(vap, retry->macaddr); if (ni != NULL) { struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; int retrycnt = count & 0xff; txs->flags = IEEE80211_RATECTL_STATUS_LONG_RETRY; txs->long_retries = retrycnt; if (count & 0x100) { txs->status = IEEE80211_RATECTL_TX_FAIL_LONG; } else { txs->status = IEEE80211_RATECTL_TX_SUCCESS; } ieee80211_ratectl_tx_complete(ni, txs); ieee80211_free_node(ni); } if (count & 0x100) /* too many retries */ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1); break; } case ZYD_NOTIF_IORD: { struct zyd_rq *rqp; if (le16toh(*(uint16_t *)cmd->data) == ZYD_CR_INTERRUPT) break; /* HMAC interrupt */ datalen = actlen - sizeof(cmd->code); datalen -= 2; /* XXX: padding? */ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { int i; int count; if (rqp->olen != datalen) continue; count = rqp->olen / sizeof(struct zyd_pair); for (i = 0; i < count; i++) { if (*(((const uint16_t *)rqp->idata) + i) != (((struct zyd_pair *)cmd->data) + i)->reg) break; } if (i != count) continue; /* copy answer into caller-supplied buffer */ memcpy(rqp->odata, cmd->data, rqp->olen); DPRINTF(sc, ZYD_DEBUG_CMD, "command %p complete, data = %*D \n", rqp, rqp->olen, (char *)rqp->odata, ":"); wakeup(rqp); /* wakeup caller */ break; } if (rqp == NULL) { device_printf(sc->sc_dev, "unexpected IORD notification %*D\n", datalen, cmd->data, ":"); } break; } default: device_printf(sc->sc_dev, "unknown notification %x\n", le16toh(cmd->code)); } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: /* Error */ DPRINTF(sc, ZYD_DEBUG_CMD, "error = %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void zyd_intr_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct zyd_softc *sc = usbd_xfer_softc(xfer); struct zyd_rq *rqp, *cmd; struct usb_page_cache *pc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: cmd = usbd_xfer_get_priv(xfer); DPRINTF(sc, ZYD_DEBUG_CMD, "command %p transferred\n", cmd); STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { /* Ensure the cached rq pointer is still valid */ if (rqp == cmd && (rqp->flags & ZYD_CMD_FLAG_READ) == 0) wakeup(rqp); /* wakeup caller */ } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) { if (rqp->flags & ZYD_CMD_FLAG_SENT) continue; pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, rqp->cmd, rqp->ilen); usbd_xfer_set_frame_len(xfer, 0, rqp->ilen); usbd_xfer_set_priv(xfer, rqp); rqp->flags |= ZYD_CMD_FLAG_SENT; usbd_transfer_submit(xfer); break; } break; default: /* Error */ DPRINTF(sc, ZYD_DEBUG_ANY, "error = %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static int zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen, void *odata, int olen, int flags) { struct zyd_cmd cmd; struct zyd_rq rq; int error; if (ilen > (int)sizeof(cmd.data)) return (EINVAL); cmd.code = htole16(code); memcpy(cmd.data, idata, ilen); DPRINTF(sc, ZYD_DEBUG_CMD, "sending cmd %p = %*D\n", &rq, ilen, idata, ":"); rq.cmd = &cmd; rq.idata = idata; rq.odata = odata; rq.ilen = sizeof(uint16_t) + ilen; rq.olen = olen; rq.flags = flags; STAILQ_INSERT_TAIL(&sc->sc_rqh, &rq, rq); usbd_transfer_start(sc->sc_xfer[ZYD_INTR_RD]); usbd_transfer_start(sc->sc_xfer[ZYD_INTR_WR]); /* wait at most one second for command reply */ error = mtx_sleep(&rq, &sc->sc_mtx, 0 , "zydcmd", hz); if (error) device_printf(sc->sc_dev, "command timeout\n"); STAILQ_REMOVE(&sc->sc_rqh, &rq, zyd_rq, rq); DPRINTF(sc, ZYD_DEBUG_CMD, "finsihed cmd %p, error = %d \n", &rq, error); return (error); } static int zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val) { struct zyd_pair tmp; int error; reg = htole16(reg); error = zyd_cmd(sc, ZYD_CMD_IORD, ®, sizeof(reg), &tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); if (error == 0) *val = le16toh(tmp.val); return (error); } static int zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val) { struct zyd_pair tmp[2]; uint16_t regs[2]; int error; regs[0] = htole16(ZYD_REG32_HI(reg)); regs[1] = htole16(ZYD_REG32_LO(reg)); error = zyd_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); if (error == 0) *val = le16toh(tmp[0].val) << 16 | le16toh(tmp[1].val); return (error); } static int zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) { struct zyd_pair pair; pair.reg = htole16(reg); pair.val = htole16(val); return zyd_cmd(sc, ZYD_CMD_IOWR, &pair, sizeof(pair), NULL, 0, 0); } static int zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val) { struct zyd_pair pair[2]; pair[0].reg = htole16(ZYD_REG32_HI(reg)); pair[0].val = htole16(val >> 16); pair[1].reg = htole16(ZYD_REG32_LO(reg)); pair[1].val = htole16(val & 0xffff); return zyd_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); } static int zyd_rfwrite(struct zyd_softc *sc, uint32_t val) { struct zyd_rf *rf = &sc->sc_rf; struct zyd_rfwrite_cmd req; uint16_t cr203; int error, i; zyd_read16_m(sc, ZYD_CR203, &cr203); cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); req.code = htole16(2); req.width = htole16(rf->width); for (i = 0; i < rf->width; i++) { req.bit[i] = htole16(cr203); if (val & (1 << (rf->width - 1 - i))) req.bit[i] |= htole16(ZYD_RF_DATA); } error = zyd_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + 2 * rf->width, NULL, 0, 0); fail: return (error); } static int zyd_rfwrite_cr(struct zyd_softc *sc, uint32_t val) { int error; zyd_write16_m(sc, ZYD_CR244, (val >> 16) & 0xff); zyd_write16_m(sc, ZYD_CR243, (val >> 8) & 0xff); zyd_write16_m(sc, ZYD_CR242, (val >> 0) & 0xff); fail: return (error); } static int zyd_lock_phy(struct zyd_softc *sc) { int error; uint32_t tmp; zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); tmp &= ~ZYD_UNLOCK_PHY_REGS; zyd_write32_m(sc, ZYD_MAC_MISC, tmp); fail: return (error); } static int zyd_unlock_phy(struct zyd_softc *sc) { int error; uint32_t tmp; zyd_read32_m(sc, ZYD_MAC_MISC, &tmp); tmp |= ZYD_UNLOCK_PHY_REGS; zyd_write32_m(sc, ZYD_MAC_MISC, tmp); fail: return (error); } /* * RFMD RF methods. */ static int zyd_rfmd_init(struct zyd_rf *rf) { struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY; static const uint32_t rfini[] = ZYD_RFMD_RF; int i, error; /* init RF-dependent PHY registers */ for (i = 0; i < nitems(phyini); i++) { zyd_write16_m(sc, phyini[i].reg, phyini[i].val); } /* init RFMD radio */ for (i = 0; i < nitems(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } fail: return (error); } static int zyd_rfmd_switch_radio(struct zyd_rf *rf, int on) { int error; struct zyd_softc *sc = rf->rf_sc; zyd_write16_m(sc, ZYD_CR10, on ? 0x89 : 0x15); zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x81); fail: return (error); } static int zyd_rfmd_set_channel(struct zyd_rf *rf, uint8_t chan) { int error; struct zyd_softc *sc = rf->rf_sc; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_RFMD_CHANTABLE; error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; fail: return (error); } /* * AL2230 RF methods. */ static int zyd_al2230_init(struct zyd_rf *rf) { struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; static const struct zyd_phy_pair phypll[] = { { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } }; static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1; static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2; static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3; int i, error; /* init RF-dependent PHY registers */ for (i = 0; i < nitems(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { for (i = 0; i < nitems(phy2230s); i++) zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); } /* init AL2230 radio */ for (i = 0; i < nitems(rfini1); i++) { error = zyd_rfwrite(sc, rfini1[i]); if (error != 0) goto fail; } if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) error = zyd_rfwrite(sc, 0x000824); else error = zyd_rfwrite(sc, 0x0005a4); if (error != 0) goto fail; for (i = 0; i < nitems(rfini2); i++) { error = zyd_rfwrite(sc, rfini2[i]); if (error != 0) goto fail; } for (i = 0; i < nitems(phypll); i++) zyd_write16_m(sc, phypll[i].reg, phypll[i].val); for (i = 0; i < nitems(rfini3); i++) { error = zyd_rfwrite(sc, rfini3[i]); if (error != 0) goto fail; } fail: return (error); } static int zyd_al2230_fini(struct zyd_rf *rf) { int error, i; struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1; for (i = 0; i < nitems(phy); i++) zyd_write16_m(sc, phy[i].reg, phy[i].val); if (sc->sc_newphy != 0) zyd_write16_m(sc, ZYD_CR9, 0xe1); zyd_write16_m(sc, ZYD_CR203, 0x6); fail: return (error); } static int zyd_al2230_init_b(struct zyd_rf *rf) { struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2; static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3; static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1; static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2; static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3; static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE; int i, error; for (i = 0; i < nitems(phy1); i++) zyd_write16_m(sc, phy1[i].reg, phy1[i].val); /* init RF-dependent PHY registers */ for (i = 0; i < nitems(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) { for (i = 0; i < nitems(phy2230s); i++) zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val); } for (i = 0; i < 3; i++) { error = zyd_rfwrite_cr(sc, zyd_al2230_chtable[0][i]); if (error != 0) return (error); } for (i = 0; i < nitems(rfini_part1); i++) { error = zyd_rfwrite_cr(sc, rfini_part1[i]); if (error != 0) return (error); } if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) error = zyd_rfwrite(sc, 0x241000); else error = zyd_rfwrite(sc, 0x25a000); if (error != 0) goto fail; for (i = 0; i < nitems(rfini_part2); i++) { error = zyd_rfwrite_cr(sc, rfini_part2[i]); if (error != 0) return (error); } for (i = 0; i < nitems(phy2); i++) zyd_write16_m(sc, phy2[i].reg, phy2[i].val); for (i = 0; i < nitems(rfini_part3); i++) { error = zyd_rfwrite_cr(sc, rfini_part3[i]); if (error != 0) return (error); } for (i = 0; i < nitems(phy3); i++) zyd_write16_m(sc, phy3[i].reg, phy3[i].val); error = zyd_al2230_fini(rf); fail: return (error); } static int zyd_al2230_switch_radio(struct zyd_rf *rf, int on) { struct zyd_softc *sc = rf->rf_sc; int error, on251 = (sc->sc_macrev == ZYD_ZD1211) ? 0x3f : 0x7f; zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_write16_m(sc, ZYD_CR251, on ? on251 : 0x2f); fail: return (error); } static int zyd_al2230_set_channel(struct zyd_rf *rf, uint8_t chan) { int error, i; struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phy1[] = { { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 }, }; static const struct { uint32_t r1, r2, r3; } rfprog[] = ZYD_AL2230_CHANTABLE; error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r3); if (error != 0) goto fail; for (i = 0; i < nitems(phy1); i++) zyd_write16_m(sc, phy1[i].reg, phy1[i].val); fail: return (error); } static int zyd_al2230_set_channel_b(struct zyd_rf *rf, uint8_t chan) { int error, i; struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; static const struct { uint32_t r1, r2, r3; } rfprog[] = ZYD_AL2230_CHANTABLE_B; for (i = 0; i < nitems(phy1); i++) zyd_write16_m(sc, phy1[i].reg, phy1[i].val); error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r3); if (error != 0) goto fail; error = zyd_al2230_fini(rf); fail: return (error); } #define ZYD_AL2230_PHY_BANDEDGE6 \ { \ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ { ZYD_CR47, 0x1e } \ } static int zyd_al2230_bandedge6(struct zyd_rf *rf, struct ieee80211_channel *c) { int error = 0, i; struct zyd_softc *sc = rf->rf_sc; struct ieee80211com *ic = &sc->sc_ic; struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6; int chan = ieee80211_chan2ieee(ic, c); if (chan == 1 || chan == 11) r[0].val = 0x12; for (i = 0; i < nitems(r); i++) zyd_write16_m(sc, r[i].reg, r[i].val); fail: return (error); } /* * AL7230B RF methods. */ static int zyd_al7230B_init(struct zyd_rf *rf) { struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1; static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2; static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3; static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1; static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2; int i, error; /* for AL7230B, PHY and RF need to be initialized in "phases" */ /* init RF-dependent PHY registers, part one */ for (i = 0; i < nitems(phyini_1); i++) zyd_write16_m(sc, phyini_1[i].reg, phyini_1[i].val); /* init AL7230B radio, part one */ for (i = 0; i < nitems(rfini_1); i++) { if ((error = zyd_rfwrite(sc, rfini_1[i])) != 0) return (error); } /* init RF-dependent PHY registers, part two */ for (i = 0; i < nitems(phyini_2); i++) zyd_write16_m(sc, phyini_2[i].reg, phyini_2[i].val); /* init AL7230B radio, part two */ for (i = 0; i < nitems(rfini_2); i++) { if ((error = zyd_rfwrite(sc, rfini_2[i])) != 0) return (error); } /* init RF-dependent PHY registers, part three */ for (i = 0; i < nitems(phyini_3); i++) zyd_write16_m(sc, phyini_3[i].reg, phyini_3[i].val); fail: return (error); } static int zyd_al7230B_switch_radio(struct zyd_rf *rf, int on) { int error; struct zyd_softc *sc = rf->rf_sc; zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_write16_m(sc, ZYD_CR251, on ? 0x3f : 0x2f); fail: return (error); } static int zyd_al7230B_set_channel(struct zyd_rf *rf, uint8_t chan) { struct zyd_softc *sc = rf->rf_sc; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_AL7230B_CHANTABLE; static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL; int i, error; zyd_write16_m(sc, ZYD_CR240, 0x57); zyd_write16_m(sc, ZYD_CR251, 0x2f); for (i = 0; i < nitems(rfsc); i++) { if ((error = zyd_rfwrite(sc, rfsc[i])) != 0) return (error); } zyd_write16_m(sc, ZYD_CR128, 0x14); zyd_write16_m(sc, ZYD_CR129, 0x12); zyd_write16_m(sc, ZYD_CR130, 0x10); zyd_write16_m(sc, ZYD_CR38, 0x38); zyd_write16_m(sc, ZYD_CR136, 0xdf); error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; error = zyd_rfwrite(sc, 0x3c9000); if (error != 0) goto fail; zyd_write16_m(sc, ZYD_CR251, 0x3f); zyd_write16_m(sc, ZYD_CR203, 0x06); zyd_write16_m(sc, ZYD_CR240, 0x08); fail: return (error); } /* * AL2210 RF methods. */ static int zyd_al2210_init(struct zyd_rf *rf) { struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY; static const uint32_t rfini[] = ZYD_AL2210_RF; uint32_t tmp; int i, error; zyd_write32_m(sc, ZYD_CR18, 2); /* init RF-dependent PHY registers */ for (i = 0; i < nitems(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); /* init AL2210 radio */ for (i = 0; i < nitems(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } zyd_write16_m(sc, ZYD_CR47, 0x1e); zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); zyd_write16_m(sc, ZYD_CR47, 0x1e); zyd_write32_m(sc, ZYD_CR18, 3); fail: return (error); } static int zyd_al2210_switch_radio(struct zyd_rf *rf, int on) { /* vendor driver does nothing for this RF chip */ return (0); } static int zyd_al2210_set_channel(struct zyd_rf *rf, uint8_t chan) { int error; struct zyd_softc *sc = rf->rf_sc; static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE; uint32_t tmp; zyd_write32_m(sc, ZYD_CR18, 2); zyd_write16_m(sc, ZYD_CR47, 0x1e); zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp); zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05); zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00); zyd_write16_m(sc, ZYD_CR47, 0x1e); /* actually set the channel */ error = zyd_rfwrite(sc, rfprog[chan - 1]); if (error != 0) goto fail; zyd_write32_m(sc, ZYD_CR18, 3); fail: return (error); } /* * GCT RF methods. */ static int zyd_gct_init(struct zyd_rf *rf) { #define ZYD_GCT_INTR_REG 0x85c1 struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY; static const uint32_t rfini[] = ZYD_GCT_RF; static const uint16_t vco[11][7] = ZYD_GCT_VCO; int i, idx = -1, error; uint16_t data; /* init RF-dependent PHY registers */ for (i = 0; i < nitems(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); /* init cgt radio */ for (i = 0; i < nitems(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } error = zyd_gct_mode(rf); if (error != 0) return (error); for (i = 0; i < (int)(nitems(vco) - 1); i++) { error = zyd_gct_set_channel_synth(rf, 1, 0); if (error != 0) goto fail; error = zyd_gct_write(rf, vco[i][0]); if (error != 0) goto fail; zyd_write16_m(sc, ZYD_GCT_INTR_REG, 0xf); zyd_read16_m(sc, ZYD_GCT_INTR_REG, &data); if ((data & 0xf) == 0) { idx = i; break; } } if (idx == -1) { error = zyd_gct_set_channel_synth(rf, 1, 1); if (error != 0) goto fail; error = zyd_gct_write(rf, 0x6662); if (error != 0) goto fail; } rf->idx = idx; zyd_write16_m(sc, ZYD_CR203, 0x6); fail: return (error); #undef ZYD_GCT_INTR_REG } static int zyd_gct_mode(struct zyd_rf *rf) { struct zyd_softc *sc = rf->rf_sc; static const uint32_t mode[] = { 0x25f98, 0x25f9a, 0x25f94, 0x27fd4 }; int i, error; for (i = 0; i < nitems(mode); i++) { if ((error = zyd_rfwrite(sc, mode[i])) != 0) break; } return (error); } static int zyd_gct_set_channel_synth(struct zyd_rf *rf, int chan, int acal) { int error, idx = chan - 1; struct zyd_softc *sc = rf->rf_sc; static uint32_t acal_synth[] = ZYD_GCT_CHANNEL_ACAL; static uint32_t std_synth[] = ZYD_GCT_CHANNEL_STD; static uint32_t div_synth[] = ZYD_GCT_CHANNEL_DIV; error = zyd_rfwrite(sc, (acal == 1) ? acal_synth[idx] : std_synth[idx]); if (error != 0) return (error); return zyd_rfwrite(sc, div_synth[idx]); } static int zyd_gct_write(struct zyd_rf *rf, uint16_t value) { struct zyd_softc *sc = rf->rf_sc; return zyd_rfwrite(sc, 0x300000 | 0x40000 | value); } static int zyd_gct_switch_radio(struct zyd_rf *rf, int on) { int error; struct zyd_softc *sc = rf->rf_sc; error = zyd_rfwrite(sc, on ? 0x25f94 : 0x25f90); if (error != 0) return (error); zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_write16_m(sc, ZYD_CR251, on ? ((sc->sc_macrev == ZYD_ZD1211B) ? 0x7f : 0x3f) : 0x2f); fail: return (error); } static int zyd_gct_set_channel(struct zyd_rf *rf, uint8_t chan) { int error, i; struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair cmd[] = { { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, }; static const uint16_t vco[11][7] = ZYD_GCT_VCO; error = zyd_gct_set_channel_synth(rf, chan, 0); if (error != 0) goto fail; error = zyd_gct_write(rf, (rf->idx == -1) ? 0x6662 : vco[rf->idx][((chan - 1) / 2)]); if (error != 0) goto fail; error = zyd_gct_mode(rf); if (error != 0) return (error); for (i = 0; i < nitems(cmd); i++) zyd_write16_m(sc, cmd[i].reg, cmd[i].val); error = zyd_gct_txgain(rf, chan); if (error != 0) return (error); zyd_write16_m(sc, ZYD_CR203, 0x6); fail: return (error); } static int zyd_gct_txgain(struct zyd_rf *rf, uint8_t chan) { struct zyd_softc *sc = rf->rf_sc; static uint32_t txgain[] = ZYD_GCT_TXGAIN; uint8_t idx = sc->sc_pwrint[chan - 1]; if (idx >= nitems(txgain)) { device_printf(sc->sc_dev, "could not set TX gain (%d %#x)\n", chan, idx); return 0; } return zyd_rfwrite(sc, 0x700000 | txgain[idx]); } /* * Maxim2 RF methods. */ static int zyd_maxim2_init(struct zyd_rf *rf) { struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; static const uint32_t rfini[] = ZYD_MAXIM2_RF; uint16_t tmp; int i, error; /* init RF-dependent PHY registers */ for (i = 0; i < nitems(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); /* init maxim2 radio */ for (i = 0; i < nitems(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); fail: return (error); } static int zyd_maxim2_switch_radio(struct zyd_rf *rf, int on) { /* vendor driver does nothing for this RF chip */ return (0); } static int zyd_maxim2_set_channel(struct zyd_rf *rf, uint8_t chan) { struct zyd_softc *sc = rf->rf_sc; static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; static const uint32_t rfini[] = ZYD_MAXIM2_RF; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_MAXIM2_CHANTABLE; uint16_t tmp; int i, error; /* * Do the same as we do when initializing it, except for the channel * values coming from the two channel tables. */ /* init RF-dependent PHY registers */ for (i = 0; i < nitems(phyini); i++) zyd_write16_m(sc, phyini[i].reg, phyini[i].val); zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4)); /* first two values taken from the chantables */ error = zyd_rfwrite(sc, rfprog[chan - 1].r1); if (error != 0) goto fail; error = zyd_rfwrite(sc, rfprog[chan - 1].r2); if (error != 0) goto fail; /* init maxim2 radio - skipping the two first values */ for (i = 2; i < nitems(rfini); i++) { if ((error = zyd_rfwrite(sc, rfini[i])) != 0) return (error); } zyd_read16_m(sc, ZYD_CR203, &tmp); zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4)); fail: return (error); } static int zyd_rf_attach(struct zyd_softc *sc, uint8_t type) { struct zyd_rf *rf = &sc->sc_rf; rf->rf_sc = sc; rf->update_pwr = 1; switch (type) { case ZYD_RF_RFMD: rf->init = zyd_rfmd_init; rf->switch_radio = zyd_rfmd_switch_radio; rf->set_channel = zyd_rfmd_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2230: case ZYD_RF_AL2230S: if (sc->sc_macrev == ZYD_ZD1211B) { rf->init = zyd_al2230_init_b; rf->set_channel = zyd_al2230_set_channel_b; } else { rf->init = zyd_al2230_init; rf->set_channel = zyd_al2230_set_channel; } rf->switch_radio = zyd_al2230_switch_radio; rf->bandedge6 = zyd_al2230_bandedge6; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL7230B: rf->init = zyd_al7230B_init; rf->switch_radio = zyd_al7230B_switch_radio; rf->set_channel = zyd_al7230B_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2210: rf->init = zyd_al2210_init; rf->switch_radio = zyd_al2210_switch_radio; rf->set_channel = zyd_al2210_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_MAXIM_NEW: case ZYD_RF_GCT: rf->init = zyd_gct_init; rf->switch_radio = zyd_gct_switch_radio; rf->set_channel = zyd_gct_set_channel; rf->width = 24; /* 24-bit RF values */ rf->update_pwr = 0; break; case ZYD_RF_MAXIM_NEW2: rf->init = zyd_maxim2_init; rf->switch_radio = zyd_maxim2_switch_radio; rf->set_channel = zyd_maxim2_set_channel; rf->width = 18; /* 18-bit RF values */ break; default: device_printf(sc->sc_dev, "sorry, radio \"%s\" is not supported yet\n", zyd_rf_name(type)); return (EINVAL); } return (0); } static const char * zyd_rf_name(uint8_t type) { static const char * const zyd_rfs[] = { "unknown", "unknown", "UW2451", "UCHIP", "AL2230", "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", "PHILIPS" }; return zyd_rfs[(type > 15) ? 0 : type]; } static int zyd_hw_init(struct zyd_softc *sc) { int error; const struct zyd_phy_pair *phyp; struct zyd_rf *rf = &sc->sc_rf; uint16_t val; /* specify that the plug and play is finished */ zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); zyd_read16_m(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_fwbase); DPRINTF(sc, ZYD_DEBUG_FW, "firmware base address=0x%04x\n", sc->sc_fwbase); /* retrieve firmware revision number */ zyd_read16_m(sc, sc->sc_fwbase + ZYD_FW_FIRMWARE_REV, &sc->sc_fwrev); zyd_write32_m(sc, ZYD_CR_GPI_EN, 0); zyd_write32_m(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); /* set mandatory rates - XXX assumes 802.11b/g */ zyd_write32_m(sc, ZYD_MAC_MAN_RATE, 0x150f); /* disable interrupts */ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); if ((error = zyd_read_pod(sc)) != 0) { device_printf(sc->sc_dev, "could not read EEPROM\n"); goto fail; } /* PHY init (resetting) */ error = zyd_lock_phy(sc); if (error != 0) goto fail; phyp = (sc->sc_macrev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy; for (; phyp->reg != 0; phyp++) zyd_write16_m(sc, phyp->reg, phyp->val); if (sc->sc_macrev == ZYD_ZD1211 && sc->sc_fix_cr157 != 0) { zyd_read16_m(sc, ZYD_EEPROM_PHY_REG, &val); zyd_write32_m(sc, ZYD_CR157, val >> 8); } error = zyd_unlock_phy(sc); if (error != 0) goto fail; /* HMAC init */ zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000020); zyd_write32_m(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0x00000000); zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0x00000000); zyd_write32_m(sc, ZYD_MAC_GHTBL, 0x00000000); zyd_write32_m(sc, ZYD_MAC_GHTBH, 0x80000000); zyd_write32_m(sc, ZYD_MAC_MISC, 0x000000a4); zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); zyd_write32_m(sc, ZYD_MAC_BCNCFG, 0x00f00401); zyd_write32_m(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000080); zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); zyd_write32_m(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); zyd_write32_m(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); zyd_write32_m(sc, ZYD_CR_PS_CTRL, 0x10000000); zyd_write32_m(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1); zyd_write32_m(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); zyd_write32_m(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032); zyd_write32_m(sc, ZYD_MAC_CAM_MODE, 0x3); if (sc->sc_macrev == ZYD_ZD1211) { zyd_write32_m(sc, ZYD_MAC_RETRY, 0x00000002); zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); } else { zyd_write32_m(sc, ZYD_MACB_MAX_RETRY, 0x02020202); zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); zyd_write32_m(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); zyd_write32_m(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); zyd_write32_m(sc, ZYD_MACB_TXOP, 0x01800824); zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff); } /* init beacon interval to 100ms */ if ((error = zyd_set_beacon_interval(sc, 100)) != 0) goto fail; if ((error = zyd_rf_attach(sc, sc->sc_rfrev)) != 0) { device_printf(sc->sc_dev, "could not attach RF, rev 0x%x\n", sc->sc_rfrev); goto fail; } /* RF chip init */ error = zyd_lock_phy(sc); if (error != 0) goto fail; error = (*rf->init)(rf); if (error != 0) { device_printf(sc->sc_dev, "radio initialization failed, error %d\n", error); goto fail; } error = zyd_unlock_phy(sc); if (error != 0) goto fail; if ((error = zyd_read_eeprom(sc)) != 0) { device_printf(sc->sc_dev, "could not read EEPROM\n"); goto fail; } fail: return (error); } static int zyd_read_pod(struct zyd_softc *sc) { int error; uint32_t tmp; zyd_read32_m(sc, ZYD_EEPROM_POD, &tmp); sc->sc_rfrev = tmp & 0x0f; sc->sc_ledtype = (tmp >> 4) & 0x01; sc->sc_al2230s = (tmp >> 7) & 0x01; sc->sc_cckgain = (tmp >> 8) & 0x01; sc->sc_fix_cr157 = (tmp >> 13) & 0x01; sc->sc_parev = (tmp >> 16) & 0x0f; sc->sc_bandedge6 = (tmp >> 21) & 0x01; sc->sc_newphy = (tmp >> 31) & 0x01; sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1; fail: return (error); } static int zyd_read_eeprom(struct zyd_softc *sc) { uint16_t val; int error, i; /* read Tx power calibration tables */ for (i = 0; i < 7; i++) { zyd_read16_m(sc, ZYD_EEPROM_PWR_CAL + i, &val); sc->sc_pwrcal[i * 2] = val >> 8; sc->sc_pwrcal[i * 2 + 1] = val & 0xff; zyd_read16_m(sc, ZYD_EEPROM_PWR_INT + i, &val); sc->sc_pwrint[i * 2] = val >> 8; sc->sc_pwrint[i * 2 + 1] = val & 0xff; zyd_read16_m(sc, ZYD_EEPROM_36M_CAL + i, &val); sc->sc_ofdm36_cal[i * 2] = val >> 8; sc->sc_ofdm36_cal[i * 2 + 1] = val & 0xff; zyd_read16_m(sc, ZYD_EEPROM_48M_CAL + i, &val); sc->sc_ofdm48_cal[i * 2] = val >> 8; sc->sc_ofdm48_cal[i * 2 + 1] = val & 0xff; zyd_read16_m(sc, ZYD_EEPROM_54M_CAL + i, &val); sc->sc_ofdm54_cal[i * 2] = val >> 8; sc->sc_ofdm54_cal[i * 2 + 1] = val & 0xff; } fail: return (error); } static int zyd_get_macaddr(struct zyd_softc *sc) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = ZYD_READFWDATAREQ; USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1); USETW(req.wIndex, 0); USETW(req.wLength, IEEE80211_ADDR_LEN); error = zyd_do_request(sc, &req, sc->sc_ic.ic_macaddr); if (error != 0) { device_printf(sc->sc_dev, "could not read EEPROM: %s\n", usbd_errstr(error)); } return (error); } static int zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr) { int error; uint32_t tmp; tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; zyd_write32_m(sc, ZYD_MAC_MACADRL, tmp); tmp = addr[5] << 8 | addr[4]; zyd_write32_m(sc, ZYD_MAC_MACADRH, tmp); fail: return (error); } static int zyd_set_bssid(struct zyd_softc *sc, const uint8_t *addr) { int error; uint32_t tmp; tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]; zyd_write32_m(sc, ZYD_MAC_BSSADRL, tmp); tmp = addr[5] << 8 | addr[4]; zyd_write32_m(sc, ZYD_MAC_BSSADRH, tmp); fail: return (error); } static int zyd_switch_radio(struct zyd_softc *sc, int on) { struct zyd_rf *rf = &sc->sc_rf; int error; error = zyd_lock_phy(sc); if (error != 0) goto fail; error = (*rf->switch_radio)(rf, on); if (error != 0) goto fail; error = zyd_unlock_phy(sc); fail: return (error); } static int zyd_set_led(struct zyd_softc *sc, int which, int on) { int error; uint32_t tmp; zyd_read32_m(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); tmp &= ~which; if (on) tmp |= which; zyd_write32_m(sc, ZYD_MAC_TX_PE_CONTROL, tmp); fail: return (error); } static u_int zyd_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) { uint32_t *hash = arg; uint8_t v; v = ((uint8_t *)LLADDR(sdl))[5] >> 2; if (v < 32) hash[0] |= 1 << v; else hash[1] |= 1 << (v - 32); return (1); } static void zyd_set_multi(struct zyd_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t hash[2]; int error; if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0) return; hash[0] = 0x00000000; hash[1] = 0x80000000; if (ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_allmulti > 0 || ic->ic_promisc > 0) { hash[0] = 0xffffffff; hash[1] = 0xffffffff; } else { struct ieee80211vap *vap; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if_foreach_llmaddr(vap->iv_ifp, zyd_hash_maddr, &hash); } /* reprogram multicast global hash table */ zyd_write32_m(sc, ZYD_MAC_GHTBL, hash[0]); zyd_write32_m(sc, ZYD_MAC_GHTBH, hash[1]); fail: if (error != 0) device_printf(sc->sc_dev, "could not set multicast hash table\n"); } static void zyd_update_mcast(struct ieee80211com *ic) { struct zyd_softc *sc = ic->ic_softc; ZYD_LOCK(sc); zyd_set_multi(sc); ZYD_UNLOCK(sc); } static int zyd_set_rxfilter(struct zyd_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t rxfilter; switch (ic->ic_opmode) { case IEEE80211_M_STA: rxfilter = ZYD_FILTER_BSS; break; case IEEE80211_M_IBSS: case IEEE80211_M_HOSTAP: rxfilter = ZYD_FILTER_HOSTAP; break; case IEEE80211_M_MONITOR: rxfilter = ZYD_FILTER_MONITOR; break; default: /* should not get there */ return (EINVAL); } return zyd_write32(sc, ZYD_MAC_RXFILTER, rxfilter); } static void zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c) { int error; struct ieee80211com *ic = &sc->sc_ic; struct zyd_rf *rf = &sc->sc_rf; uint32_t tmp; int chan; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) { /* XXX should NEVER happen */ device_printf(sc->sc_dev, "%s: invalid channel %x\n", __func__, chan); return; } error = zyd_lock_phy(sc); if (error != 0) goto fail; error = (*rf->set_channel)(rf, chan); if (error != 0) goto fail; if (rf->update_pwr) { /* update Tx power */ zyd_write16_m(sc, ZYD_CR31, sc->sc_pwrint[chan - 1]); if (sc->sc_macrev == ZYD_ZD1211B) { zyd_write16_m(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]); zyd_write16_m(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]); zyd_write16_m(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]); zyd_write16_m(sc, ZYD_CR68, sc->sc_pwrcal[chan - 1]); zyd_write16_m(sc, ZYD_CR69, 0x28); zyd_write16_m(sc, ZYD_CR69, 0x2a); } } if (sc->sc_cckgain) { /* set CCK baseband gain from EEPROM */ if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0) zyd_write16_m(sc, ZYD_CR47, tmp & 0xff); } if (sc->sc_bandedge6 && rf->bandedge6 != NULL) { error = (*rf->bandedge6)(rf, c); if (error != 0) goto fail; } zyd_write32_m(sc, ZYD_CR_CONFIG_PHILIPS, 0); error = zyd_unlock_phy(sc); if (error != 0) goto fail; sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); fail: return; } static int zyd_set_beacon_interval(struct zyd_softc *sc, int bintval) { int error; uint32_t val; zyd_read32_m(sc, ZYD_CR_ATIM_WND_PERIOD, &val); sc->sc_atim_wnd = val; zyd_read32_m(sc, ZYD_CR_PRE_TBTT, &val); sc->sc_pre_tbtt = val; sc->sc_bcn_int = bintval; if (sc->sc_bcn_int <= 5) sc->sc_bcn_int = 5; if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int) sc->sc_pre_tbtt = sc->sc_bcn_int - 1; if (sc->sc_atim_wnd >= sc->sc_pre_tbtt) sc->sc_atim_wnd = sc->sc_pre_tbtt - 1; zyd_write32_m(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd); zyd_write32_m(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt); zyd_write32_m(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int); fail: return (error); } static void zyd_rx_data(struct usb_xfer *xfer, int offset, uint16_t len) { struct zyd_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct zyd_plcphdr plcp; struct zyd_rx_stat stat; struct usb_page_cache *pc; struct mbuf *m; int rlen, rssi; if (len < ZYD_MIN_FRAGSZ) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n", device_get_nameunit(sc->sc_dev), len); counter_u64_add(ic->ic_ierrors, 1); return; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, offset, &plcp, sizeof(plcp)); usbd_copy_out(pc, offset + len - sizeof(stat), &stat, sizeof(stat)); if (stat.flags & ZYD_RX_ERROR) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: RX status indicated error (%x)\n", device_get_nameunit(sc->sc_dev), stat.flags); counter_u64_add(ic->ic_ierrors, 1); return; } /* compute actual frame length */ rlen = len - sizeof(struct zyd_plcphdr) - sizeof(struct zyd_rx_stat) - IEEE80211_CRC_LEN; /* allocate a mbuf to store the frame */ if (rlen > (int)MCLBYTES) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too long (length=%d)\n", device_get_nameunit(sc->sc_dev), rlen); counter_u64_add(ic->ic_ierrors, 1); return; } else if (rlen > (int)MHLEN) m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); else m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n", device_get_nameunit(sc->sc_dev)); counter_u64_add(ic->ic_ierrors, 1); return; } m->m_pkthdr.len = m->m_len = rlen; usbd_copy_out(pc, offset + sizeof(plcp), mtod(m, uint8_t *), rlen); if (ieee80211_radiotap_active(ic)) { struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; if (stat.flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32)) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; /* XXX toss, no way to express errors */ if (stat.flags & ZYD_RX_DECRYPTERR) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; tap->wr_rate = ieee80211_plcp2rate(plcp.signal, (stat.flags & ZYD_RX_OFDM) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_antsignal = stat.rssi + -95; tap->wr_antnoise = -95; /* XXX */ } rssi = (stat.rssi > 63) ? 127 : 2 * stat.rssi; sc->sc_rx_data[sc->sc_rx_count].rssi = rssi; sc->sc_rx_data[sc->sc_rx_count].m = m; sc->sc_rx_count++; } static void zyd_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct zyd_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct zyd_rx_desc desc; struct mbuf *m; struct usb_page_cache *pc; uint32_t offset; uint8_t rssi; int8_t nf; int i; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); sc->sc_rx_count = 0; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, actlen - sizeof(desc), &desc, sizeof(desc)); offset = 0; if (UGETW(desc.tag) == ZYD_TAG_MULTIFRAME) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: received multi-frame transfer\n", __func__); for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) { uint16_t len16 = UGETW(desc.len[i]); if (len16 == 0 || len16 > actlen) break; zyd_rx_data(xfer, offset, len16); /* next frame is aligned on a 32-bit boundary */ len16 = (len16 + 3) & ~3; offset += len16; if (len16 > actlen) break; actlen -= len16; } } else { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: received single-frame transfer\n", __func__); zyd_rx_data(xfer, 0, actlen); } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ ZYD_UNLOCK(sc); for (i = 0; i < sc->sc_rx_count; i++) { rssi = sc->sc_rx_data[i].rssi; m = sc->sc_rx_data[i].m; sc->sc_rx_data[i].m = NULL; nf = -95; /* XXX */ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void)ieee80211_input(ni, m, rssi, nf); ieee80211_free_node(ni); } else (void)ieee80211_input_all(ic, m, rssi, nf); } ZYD_LOCK(sc); zyd_start(sc); break; default: /* Error */ DPRINTF(sc, ZYD_DEBUG_ANY, "frame error: %s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static uint8_t zyd_plcp_signal(struct zyd_softc *sc, int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (0xb); case 18: return (0xf); case 24: return (0xa); case 36: return (0xe); case 48: return (0x9); case 72: return (0xd); case 96: return (0x8); case 108: return (0xc); /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); } device_printf(sc->sc_dev, "unsupported rate %d\n", rate); return (0x0); } static void zyd_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct zyd_softc *sc = usbd_xfer_softc(xfer); struct ieee80211vap *vap; struct zyd_tx_data *data; struct mbuf *m; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF(sc, ZYD_DEBUG_ANY, "transfer complete, %u bytes\n", actlen); /* free resources */ data = usbd_xfer_get_priv(xfer); zyd_tx_free(data, 0); usbd_xfer_set_priv(xfer, NULL); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->tx_q); if (data) { STAILQ_REMOVE_HEAD(&sc->tx_q, next); m = data->m; if (m->m_pkthdr.len > (int)ZYD_MAX_TXBUFSZ) { DPRINTF(sc, ZYD_DEBUG_ANY, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = ZYD_MAX_TXBUFSZ; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &data->desc, ZYD_TX_DESC_SIZE); usbd_m_copy_in(pc, ZYD_TX_DESC_SIZE, m, 0, m->m_pkthdr.len); vap = data->ni->ni_vap; if (ieee80211_radiotap_active_vap(vap)) { struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = data->rate; ieee80211_radiotap_tx(vap, m); } usbd_xfer_set_frame_len(xfer, 0, ZYD_TX_DESC_SIZE + m->m_pkthdr.len); usbd_xfer_set_priv(xfer, data); usbd_transfer_submit(xfer); } zyd_start(sc); break; default: /* Error */ DPRINTF(sc, ZYD_DEBUG_ANY, "transfer error, %s\n", usbd_errstr(error)); counter_u64_add(sc->sc_ic.ic_oerrors, 1); data = usbd_xfer_get_priv(xfer); usbd_xfer_set_priv(xfer, NULL); if (data != NULL) zyd_tx_free(data, error); if (error != USB_ERR_CANCELLED) { if (error == USB_ERR_TIMEOUT) device_printf(sc->sc_dev, "device timeout\n"); /* * Try to clear stall first, also if other * errors occur, hence clearing stall * introduces a 50 ms delay: */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static int zyd_tx_start(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct zyd_tx_desc *desc; struct zyd_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp = ni->ni_txparms; struct ieee80211_key *k; int rate, totlen, type, ismcast; static const uint8_t ratediv[] = ZYD_TX_RATEDIV; uint8_t phy; uint16_t pktlen; uint32_t bits; wh = mtod(m0, struct ieee80211_frame *); data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; if (type == IEEE80211_FC0_TYPE_MGT || type == IEEE80211_FC0_TYPE_CTL || (m0->m_flags & M_EAPOL) != 0) { rate = tp->mgmtrate; } else { /* for data frames */ if (ismcast) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else { (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = ieee80211_node_get_txrate_dot11rate(ni); } } + ieee80211_output_seqno_assign(ni, -1, m0); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { return (ENOBUFS); } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } data->ni = ni; data->m = m0; data->rate = rate; /* fill Tx descriptor */ desc = &data->desc; phy = zyd_plcp_signal(sc, rate); desc->phy = phy; if (ZYD_RATE_IS_OFDM(rate)) { desc->phy |= ZYD_TX_PHY_OFDM; if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) desc->phy |= ZYD_TX_PHY_5GHZ; } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->phy |= ZYD_TX_PHY_SHPREAMBLE; totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN; desc->len = htole16(totlen); desc->flags = ZYD_TX_FLAG_BACKOFF; if (!ismcast) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (totlen > vap->iv_rtsthreshold) { desc->flags |= ZYD_TX_FLAG_RTS; } else if (ZYD_RATE_IS_OFDM(rate) && (ic->ic_flags & IEEE80211_F_USEPROT)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) desc->flags |= ZYD_TX_FLAG_RTS; } } else desc->flags |= ZYD_TX_FLAG_MULTICAST; if (IEEE80211_IS_CTL_PS_POLL(wh)) desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); /* actual transmit length (XXX why +10?) */ pktlen = ZYD_TX_DESC_SIZE + 10; if (sc->sc_macrev == ZYD_ZD1211) pktlen += totlen; desc->pktlen = htole16(pktlen); bits = (rate == 11) ? (totlen * 16) + 10 : ((rate == 22) ? (totlen * 8) + 10 : (totlen * 8)); desc->plcp_length = htole16(bits / ratediv[phy]); desc->plcp_service = 0; if (rate == 22 && (bits % 11) > 0 && (bits % 11) <= 3) desc->plcp_service |= ZYD_PLCP_LENGEXT; desc->nextlen = 0; if (ieee80211_radiotap_active_vap(vap)) { struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; ieee80211_radiotap_tx(vap, m0); } DPRINTF(sc, ZYD_DEBUG_XMIT, "%s: sending data frame len=%zu rate=%u\n", device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usbd_transfer_start(sc->sc_xfer[ZYD_BULK_WR]); return (0); } static int zyd_transmit(struct ieee80211com *ic, struct mbuf *m) { struct zyd_softc *sc = ic->ic_softc; int error; ZYD_LOCK(sc); if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0) { ZYD_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { ZYD_UNLOCK(sc); return (error); } zyd_start(sc); ZYD_UNLOCK(sc); return (0); } static void zyd_start(struct zyd_softc *sc) { struct ieee80211_node *ni; struct mbuf *m; ZYD_LOCK_ASSERT(sc, MA_OWNED); while (sc->tx_nfree > 0 && (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (zyd_tx_start(sc, m, ni) != 0) { m_freem(m); if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); break; } } } static int zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct zyd_softc *sc = ic->ic_softc; ZYD_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!(sc->sc_flags & ZYD_FLAG_RUNNING)) { ZYD_UNLOCK(sc); m_freem(m); return (ENETDOWN); } if (sc->tx_nfree == 0) { ZYD_UNLOCK(sc); m_freem(m); return (ENOBUFS); /* XXX */ } /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. * XXX raw path */ if (zyd_tx_start(sc, m, ni) != 0) { ZYD_UNLOCK(sc); m_freem(m); return (EIO); } ZYD_UNLOCK(sc); return (0); } static void zyd_parent(struct ieee80211com *ic) { struct zyd_softc *sc = ic->ic_softc; int startall = 0; ZYD_LOCK(sc); if (sc->sc_flags & ZYD_FLAG_DETACHED) { ZYD_UNLOCK(sc); return; } if (ic->ic_nrunning > 0) { if ((sc->sc_flags & ZYD_FLAG_RUNNING) == 0) { zyd_init_locked(sc); startall = 1; } else zyd_set_multi(sc); } else if (sc->sc_flags & ZYD_FLAG_RUNNING) zyd_stop(sc); ZYD_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } static void zyd_init_locked(struct zyd_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct usb_config_descriptor *cd; int error; uint32_t val; ZYD_LOCK_ASSERT(sc, MA_OWNED); if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) { error = zyd_loadfirmware(sc); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware (error=%d)\n", error); goto fail; } /* reset device */ cd = usbd_get_config_descriptor(sc->sc_udev); error = usbd_req_set_config(sc->sc_udev, &sc->sc_mtx, cd->bConfigurationValue); if (error) device_printf(sc->sc_dev, "reset failed, continuing\n"); error = zyd_hw_init(sc); if (error) { device_printf(sc->sc_dev, "hardware initialization failed\n"); goto fail; } device_printf(sc->sc_dev, "HMAC ZD1211%s, FW %02x.%02x, RF %s S%x, PA%x LED %x " "BE%x NP%x Gain%x F%x\n", (sc->sc_macrev == ZYD_ZD1211) ? "": "B", sc->sc_fwrev >> 8, sc->sc_fwrev & 0xff, zyd_rf_name(sc->sc_rfrev), sc->sc_al2230s, sc->sc_parev, sc->sc_ledtype, sc->sc_bandedge6, sc->sc_newphy, sc->sc_cckgain, sc->sc_fix_cr157); /* read regulatory domain (currently unused) */ zyd_read32_m(sc, ZYD_EEPROM_SUBID, &val); sc->sc_regdomain = val >> 16; DPRINTF(sc, ZYD_DEBUG_INIT, "regulatory domain %x\n", sc->sc_regdomain); /* we'll do software WEP decryption for now */ DPRINTF(sc, ZYD_DEBUG_INIT, "%s: setting encryption type\n", __func__); zyd_write32_m(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); sc->sc_flags |= ZYD_FLAG_INITONCE; } if (sc->sc_flags & ZYD_FLAG_RUNNING) zyd_stop(sc); DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %6D\n", vap ? vap->iv_myaddr : ic->ic_macaddr, ":"); error = zyd_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); if (error != 0) return; /* set basic rates */ if (ic->ic_curmode == IEEE80211_MODE_11B) zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x0003); else if (ic->ic_curmode == IEEE80211_MODE_11A) zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x1500); else /* assumes 802.11b/g */ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0xff0f); /* promiscuous mode */ zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0); /* multicast setup */ zyd_set_multi(sc); /* set RX filter */ error = zyd_set_rxfilter(sc); if (error != 0) goto fail; /* switch radio transmitter ON */ error = zyd_switch_radio(sc, 1); if (error != 0) goto fail; /* set default BSS channel */ zyd_set_chan(sc, ic->ic_curchan); /* * Allocate Tx and Rx xfer queues. */ zyd_setup_tx_list(sc); /* enable interrupts */ zyd_write32_m(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); sc->sc_flags |= ZYD_FLAG_RUNNING; usbd_xfer_set_stall(sc->sc_xfer[ZYD_BULK_WR]); usbd_transfer_start(sc->sc_xfer[ZYD_BULK_RD]); usbd_transfer_start(sc->sc_xfer[ZYD_INTR_RD]); return; fail: zyd_stop(sc); return; } static void zyd_stop(struct zyd_softc *sc) { int error; ZYD_LOCK_ASSERT(sc, MA_OWNED); sc->sc_flags &= ~ZYD_FLAG_RUNNING; zyd_drain_mbufq(sc); /* * Drain all the transfers, if not already drained: */ ZYD_UNLOCK(sc); usbd_transfer_drain(sc->sc_xfer[ZYD_BULK_WR]); usbd_transfer_drain(sc->sc_xfer[ZYD_BULK_RD]); ZYD_LOCK(sc); zyd_unsetup_tx_list(sc); /* Stop now if the device was never set up */ if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) return; /* switch radio transmitter OFF */ error = zyd_switch_radio(sc, 0); if (error != 0) goto fail; /* disable Rx */ zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0); /* disable interrupts */ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0); fail: return; } static int zyd_loadfirmware(struct zyd_softc *sc) { struct usb_device_request req; size_t size; u_char *fw; uint8_t stat; uint16_t addr; if (sc->sc_flags & ZYD_FLAG_FWLOADED) return (0); if (sc->sc_macrev == ZYD_ZD1211) { fw = (u_char *)zd1211_firmware; size = sizeof(zd1211_firmware); } else { fw = (u_char *)zd1211b_firmware; size = sizeof(zd1211b_firmware); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = ZYD_DOWNLOADREQ; USETW(req.wIndex, 0); addr = ZYD_FIRMWARE_START_ADDR; while (size > 0) { /* * When the transfer size is 4096 bytes, it is not * likely to be able to transfer it. * The cause is port or machine or chip? */ const int mlen = min(size, 64); DPRINTF(sc, ZYD_DEBUG_FW, "loading firmware block: len=%d, addr=0x%x\n", mlen, addr); USETW(req.wValue, addr); USETW(req.wLength, mlen); if (zyd_do_request(sc, &req, fw) != 0) return (EIO); addr += mlen / 2; fw += mlen; size -= mlen; } /* check whether the upload succeeded */ req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = ZYD_DOWNLOADSTS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(stat)); if (zyd_do_request(sc, &req, &stat) != 0) return (EIO); sc->sc_flags |= ZYD_FLAG_FWLOADED; return (stat & 0x80) ? (EIO) : (0); } static void zyd_scan_start(struct ieee80211com *ic) { struct zyd_softc *sc = ic->ic_softc; ZYD_LOCK(sc); /* want broadcast address while scanning */ zyd_set_bssid(sc, ieee80211broadcastaddr); ZYD_UNLOCK(sc); } static void zyd_scan_end(struct ieee80211com *ic) { struct zyd_softc *sc = ic->ic_softc; ZYD_LOCK(sc); /* restore previous bssid */ zyd_set_bssid(sc, sc->sc_bssid); ZYD_UNLOCK(sc); } static void zyd_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { uint8_t bands[IEEE80211_MODE_BYTES]; memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); } static void zyd_set_channel(struct ieee80211com *ic) { struct zyd_softc *sc = ic->ic_softc; ZYD_LOCK(sc); zyd_set_chan(sc, ic->ic_curchan); ZYD_UNLOCK(sc); } static device_method_t zyd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, zyd_match), DEVMETHOD(device_attach, zyd_attach), DEVMETHOD(device_detach, zyd_detach), DEVMETHOD_END }; static driver_t zyd_driver = { .name = "zyd", .methods = zyd_methods, .size = sizeof(struct zyd_softc) }; DRIVER_MODULE(zyd, uhub, zyd_driver, NULL, NULL); MODULE_DEPEND(zyd, usb, 1, 1, 1); MODULE_DEPEND(zyd, wlan, 1, 1, 1); MODULE_VERSION(zyd, 1); USB_PNP_HOST_INFO(zyd_devs);