Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -2833,6 +2833,14 @@ dev/rp/rp_isa.c optional rp isa dev/rp/rp_pci.c optional rp pci # +dev/rt2860/rt2860_fdt.c optional rt2860 fdt +dev/rt2860/rt2860.c optional rt2860 +dev/rt2860/rt2860_amrr.c optional rt2860 +dev/rt2860/rt2860_io.c optional rt2860 +dev/rt2860/rt2860_led.c optional rt2860 +dev/rt2860/rt2860_read_eeprom.c optional rt2860 +dev/rt2860/rt2860_rf.c optional rt2860 +# dev/rtwn/if_rtwn.c optional rtwn dev/rtwn/if_rtwn_beacon.c optional rtwn dev/rtwn/if_rtwn_calib.c optional rtwn Index: sys/dev/rt2860/rt2860.c =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860.c @@ -0,0 +1,7098 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Defines and macros + */ + +#define RT2860_MAX_AGG_SIZE 3840 + +#define RT2860_TX_DATA_SEG0_SIZE \ + (sizeof(struct rt2860_txwi) + sizeof(struct ieee80211_qosframe_addr4)) + +#define RT2860_NOISE_FLOOR -95 + +#define IEEE80211_HAS_ADDR4(wh) \ + (((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) + +#define RT2860_MS(_v, _f) (((_v) & _f) >> _f##_S) +#define RT2860_SM(_v, _f) (((_v) << _f##_S) & _f) + +#define RT2860_TX_WATCHDOG_TIMEOUT 5 + +#define RUN_AID2WCID(aid) ((aid) & 0xff) +#define RT2860_WCID_RESERVED 0xff +#define RT2860_WCID_MCAST 0xf7 + +/* + * Global function prototypes, used in bus depended interfaces + */ + +int rt2860_attach(device_t dev, int id); + +int rt2860_detach(device_t dev); + +int rt2860_shutdown(device_t dev); + +int rt2860_suspend(device_t dev); + +int rt2860_resume(device_t dev); + +/* + * Static function prototypes + */ + +static void rt2860_init_channels(struct rt2860_softc *sc); + +static void rt2860_init_channels_ht40(struct rt2860_softc *sc); + +static void rt2860_init_locked(void *priv); + +static void rt2860_init(void *priv); + +static int rt2860_init_bbp(struct rt2860_softc *sc); + +static void rt2860_stop_locked(void *priv); + +static void rt2860_stop(void *priv); + +static void rt2860_start(struct rt2860_softc *sc); + +static struct ieee80211vap *rt2860_vap_create(struct ieee80211com *ic, + const char name[IFNAMSIZ], int unit, enum ieee80211_opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]); + +static void rt2860_vap_delete(struct ieee80211vap *vap); + +static int rt2860_vap_reset(struct ieee80211vap *vap, u_long cmd); + +static int rt2860_vap_newstate(struct ieee80211vap *vap, + enum ieee80211_state nstate, int arg); + +#ifdef RT2860_HW_CRYPTO +static void rt2860_vap_key_update_begin(struct ieee80211vap *vap); + +static void rt2860_vap_key_update_end(struct ieee80211vap *vap); + +static int rt2860_vap_key_set(struct ieee80211vap *vap, + const struct ieee80211_key *k); + +static int rt2860_vap_key_delete(struct ieee80211vap *vap, + const struct ieee80211_key *k); +#endif + +static void rt2860_vap_update_beacon(struct ieee80211vap *vap, int what); + +static struct ieee80211_node *rt2860_node_alloc(struct ieee80211vap *vap, + const uint8_t mac[IEEE80211_ADDR_LEN]); + +static void rt2860_node_cleanup(struct ieee80211_node *ni); + +static int rt2860_setregdomain(struct ieee80211com *ic, + struct ieee80211_regdomain *reg, + int nchans, struct ieee80211_channel chans[]); + +static void rt2860_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]); + +static void rt2860_scan_start(struct ieee80211com *ic); + +static void rt2860_scan_end(struct ieee80211com *ic); + +static void rt2860_set_channel(struct ieee80211com *ic); + +static void rt2860_newassoc(struct ieee80211_node *ni, int isnew); + +static void rt2860_updateslot(struct ieee80211com *ic); + +static void rt2860_update_promisc(struct ieee80211com *ic); + +static int rt2860_wme_update(struct ieee80211com *ic); + +static int rt2860_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params); + +static int rt2860_recv_action(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm); + +static int rt2860_send_action(struct ieee80211_node *ni, + int cat, int act, void *sa); + +static int rt2860_addba_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int status, int baparamset, int batimeout); + +static void rt2860_addba_stop(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); + +static int rt2860_ampdu_rx_start(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap, + int baparamset, int batimeout, int baseqctl); + +static void rt2860_ampdu_rx_stop(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap); + +static int rt2860_send_bar(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, ieee80211_seq seqno); + +static void rt2860_amrr_update_iter_func(void *arg, struct ieee80211_node *ni); + +static void rt2860_periodic(void *arg); + +static void rt2860_tx_watchdog(void *arg); + +static int rt2860_staid_alloc(struct rt2860_softc *sc, int aid); + +static void rt2860_staid_delete(struct rt2860_softc *sc, int staid); + +static void rt2860_asic_set_bssid(struct rt2860_softc *sc, + const uint8_t *bssid); + +static void rt2860_asic_set_macaddr(struct rt2860_softc *sc, + const uint8_t *addr); + +static void rt2860_parent(struct ieee80211com *); +static int rt2860_transmit(struct ieee80211com *, struct mbuf *); + +static void rt2860_asic_enable_tsf_sync(struct rt2860_softc *sc); + +static void rt2860_asic_disable_tsf_sync(struct rt2860_softc *sc); + +static void rt2860_asic_enable_mrr(struct rt2860_softc *sc); + +static void rt2860_asic_set_txpreamble(struct rt2860_softc *sc); + +static void rt2860_asic_set_basicrates(struct rt2860_softc *sc); + +static void rt2860_asic_update_rtsthreshold(struct rt2860_softc *sc); + +static void rt2860_asic_update_txpower(struct rt2860_softc *sc); + +static void rt2860_asic_update_promisc(struct rt2860_softc *sc); + +static void rt2860_asic_updateprot(struct rt2860_softc *sc); + +static void rt2860_asic_updateslot(struct rt2860_softc *sc); + +static void rt2860_asic_wme_update(struct rt2860_softc *sc); + +static void rt2860_asic_update_beacon(struct rt2860_softc *sc, + struct ieee80211vap *vap); + +static void rt2860_asic_clear_keytables(struct rt2860_softc *sc); + +static void rt2860_asic_add_ba_session(struct rt2860_softc *sc, + uint8_t wcid, int tid); + +static void rt2860_asic_del_ba_session(struct rt2860_softc *sc, + uint8_t wcid, int tid); + +static int rt2860_beacon_alloc(struct rt2860_softc *sc, + struct ieee80211vap *vap); + +static uint8_t rt2860_rxrate(struct rt2860_rxwi *rxwi); + +static uint8_t rt2860_maxrssi_rxpath(struct rt2860_softc *sc, + const struct rt2860_rxwi *rxwi); + +static int8_t rt2860_rssi2dbm(struct rt2860_softc *sc, + uint8_t rssi, uint8_t rxpath); + +static uint8_t rt2860_rate2mcs(uint8_t rate); + +static int rt2860_tx_mgmt(struct rt2860_softc *sc, + struct mbuf *m, struct ieee80211_node *ni, int qid); + +static int rt2860_tx_data(struct rt2860_softc *sc, + struct mbuf *m, struct ieee80211_node *ni, int qid); + +/* +static int rt2860_tx_raw(struct rt2860_softc *sc, + struct mbuf *m, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params); +*/ + +static void rt2860_intr(void *arg); + +static void rt2860_tx_coherent_intr(struct rt2860_softc *sc); + +static void rt2860_rx_coherent_intr(struct rt2860_softc *sc); + +static void rt2860_txrx_coherent_intr(struct rt2860_softc *sc); + +static void rt2860_fifo_sta_full_intr(struct rt2860_softc *sc); + +static void rt2860_rx_intr(struct rt2860_softc *sc); + +static void rt2860_rx_delay_intr(struct rt2860_softc *sc); + +static void rt2860_tx_intr(struct rt2860_softc *sc, int qid); + +static void rt2860_tx_delay_intr(struct rt2860_softc *sc); + +static void rt2860_pre_tbtt_intr(struct rt2860_softc *sc); + +static void rt2860_tbtt_intr(struct rt2860_softc *sc); + +static void rt2860_mcu_cmd_intr(struct rt2860_softc *sc); + +static void rt2860_auto_wakeup_intr(struct rt2860_softc *sc); + +static void rt2860_gp_timer_intr(struct rt2860_softc *sc); + +static void rt2860_rx_done_task(void *context, int pending); + +static void rt2860_tx_done_task(void *context, int pending); + +static void rt2860_fifo_sta_full_task(void *context, int pending); + +static void rt2860_periodic_task(void *context, int pending); + +static int rt2860_rx_eof(struct rt2860_softc *sc, int limit); + +static void rt2860_tx_eof(struct rt2860_softc *sc, + struct rt2860_softc_tx_ring *ring); + +static void rt2860_update_stats(struct rt2860_softc *sc); + +static void rt2860_bbp_tuning(struct rt2860_softc *sc); + +static void rt2860_watchdog(struct rt2860_softc *sc); + +static void rt2860_drain_fifo_stats(struct rt2860_softc *sc); + +static void rt2860_update_raw_counters(struct rt2860_softc *sc); + +static void rt2860_intr_enable(struct rt2860_softc *sc, uint32_t intr_mask); + +static void rt2860_intr_disable(struct rt2860_softc *sc, uint32_t intr_mask); + +static int rt2860_txrx_enable(struct rt2860_softc *sc); + +static int rt2860_alloc_rx_ring(struct rt2860_softc *sc, + struct rt2860_softc_rx_ring *ring); + +static void rt2860_reset_rx_ring(struct rt2860_softc *sc, + struct rt2860_softc_rx_ring *ring); + +static void rt2860_free_rx_ring(struct rt2860_softc *sc, + struct rt2860_softc_rx_ring *ring); + +static int rt2860_alloc_tx_ring(struct rt2860_softc *sc, + struct rt2860_softc_tx_ring *ring, int qid); + +static void rt2860_reset_tx_ring(struct rt2860_softc *sc, + struct rt2860_softc_tx_ring *ring); + +static void rt2860_free_tx_ring(struct rt2860_softc *sc, + struct rt2860_softc_tx_ring *ring); + +static void rt2860_dma_map_addr(void *arg, bus_dma_segment_t *segs, + int nseg, int error); + +static void rt2860_sysctl_attach(struct rt2860_softc *sc); + +/* + * Static variables + */ + +static const struct +{ + uint32_t reg; + uint32_t val; +} rt2860_def_mac[] = +{ +#if 1 + { RT2860_REG_PBF_BCN_OFFSET0, 0xf8f0e8e0 }, + { RT2860_REG_PBF_BCN_OFFSET1, 0x6f77d0c8 }, + { RT2860_REG_LEGACY_BASIC_RATE, 0x0000013f }, + { RT2860_REG_HT_BASIC_RATE, 0x00008003 }, + { RT2860_REG_SYS_CTRL, 0x00000000 }, + { RT2860_REG_RX_FILTER_CFG, 0x00017f97 }, + { RT2860_REG_BKOFF_SLOT_CFG, 0x00000209 }, + { RT2860_REG_TX_SW_CFG0, 0x00000000 }, + { RT2860_REG_TX_SW_CFG1, 0x00080606 }, + { RT2860_REG_TX_LINK_CFG, 0x00001020 }, + { RT2860_REG_TX_TIMEOUT_CFG, 0x000a2090 }, + { RT2860_REG_MAX_LEN_CFG, (1 << 12) | RT2860_MAX_AGG_SIZE }, + { RT2860_REG_LED_CFG, 0x7f031e46 }, + { RT2860_REG_PBF_MAX_PCNT, 0x1f3fbf9f }, + { RT2860_REG_TX_RTY_CFG, 0x47d01f0f }, + { RT2860_REG_AUTO_RSP_CFG, 0x00000013 }, + { RT2860_REG_TX_CCK_PROT_CFG, 0x05740003 }, + { RT2860_REG_TX_OFDM_PROT_CFG, 0x05740003 }, + { RT2860_REG_TX_GF20_PROT_CFG, 0x01744004 }, + { RT2860_REG_TX_GF40_PROT_CFG, 0x03f44084 }, + { RT2860_REG_TX_MM20_PROT_CFG, 0x01744004 }, + { RT2860_REG_TX_MM40_PROT_CFG, 0x03f54084 }, + { RT2860_REG_TX_TXOP_CTRL_CFG, 0x0000583f }, + { RT2860_REG_TX_RTS_CFG, 0x00092b20 }, + { RT2860_REG_TX_EXP_ACK_TIME, 0x002400ca }, + { RT2860_REG_HCCAPSMP_TXOP_HLDR_ET, 0x00000002 }, + { RT2860_REG_XIFS_TIME_CFG, 0x33a41010 }, + { RT2860_REG_PWR_PIN_CFG, 0x00000003 }, + { RT2860_REG_SCHDMA_WMM_AIFSN_CFG, 0x00002273 }, + { RT2860_REG_SCHDMA_WMM_CWMIN_CFG, 0x00002344 }, + { RT2860_REG_SCHDMA_WMM_CWMAX_CFG, 0x000034aa }, +#else + + { RT2860_REG_PBF_BCN_OFFSET0, 0xf8f0e8e0}, /* 0x3800(e0), 0x3A00(e8), 0x3C00(f0), 0x3E00(f8), 512B for each beacon */ + { RT2860_REG_PBF_BCN_OFFSET1, 0x6f77d0c8}, /* 0x3200(c8), 0x3400(d0), 0x1DC0(77), 0x1BC0(6f), 512B for each beacon */ + { RT2860_REG_LEGACY_BASIC_RATE, 0x0000013f}, // Basic rate set bitmap + { RT2860_REG_HT_BASIC_RATE, 0x00008003}, // Basic HT rate set , 20M, MCS=3, MM. Format is the same as in TXWI. + { RT2860_REG_SYS_CTRL, 0x00000000}, // 0x1004, , default Disable RX + { RT2860_REG_RX_FILTER_CFG, 0x00017f97}, //0x1400 , RX filter control, + { RT2860_REG_BKOFF_SLOT_CFG, 0x00000209}, // default set short slot time, CC_DELAY_TIME should be 2 + { RT2860_REG_TX_SW_CFG0, 0x00000400}, // Gary,2008-05-21 0x0 for CWC test , 2008-06-19 0x400 for rf reason + { RT2860_REG_TX_SW_CFG1, 0x00000000}, // Gary,2008-06-18 + { RT2860_REG_TX_SW_CFG2, 0x00000030}, // Bruce, CwC IOT issue + { RT2860_REG_TX_LINK_CFG, 0x00001020}, // Gary,2006-08-23 + { RT2860_REG_TX_TIMEOUT_CFG, 0x000a2090}, // CCK has some problem. So increase timieout value. 2006-10-09// MArvek RT , Modify for 2860E ,2007-08-01 + { RT2860_REG_MAX_LEN_CFG, (1 << 12) | RT2860_MAX_AGG_SIZE }, // 0x3018, MAX frame length. Max PSDU = 16kbytes. + { RT2860_REG_LED_CFG, 0x7f031e46}, // Gary, 2006-08-23 + { RT2860_REG_PBF_MAX_PCNT, 0x1F3FBF9F}, //0x1F3f7f9f}, //Jan, 2006/04/20 + { RT2860_REG_TX_RTY_CFG, 0x47d01f0f}, // Jan, 2006/11/16, Set TxWI->ACK =0 in Probe Rsp Modify for 2860E ,2007-08-03 + { RT2860_REG_AUTO_RSP_CFG, 0x00000053}, // Initial Auto_Responder, because QA will turn off Auto-Responder + { RT2860_REG_TX_CCK_PROT_CFG, 0x05740003}, // Initial Auto_Responder, because QA will turn off Auto-Responder. And RTS threshold is enabled. + { RT2860_REG_TX_OFDM_PROT_CFG, 0x05740003}, // Initial Auto_Responder, because QA will turn off Auto-Responder. And RTS threshold is enabled. + { RT2860_REG_TX_GF20_PROT_CFG, 0x01744004}, // set 19:18 --> Short NAV for MIMO PS + { RT2860_REG_TX_GF40_PROT_CFG, 0x03F44084}, + { RT2860_REG_TX_MM20_PROT_CFG, 0x01744004}, + { RT2860_REG_TX_MM40_PROT_CFG, 0x03F54084}, + { RT2860_REG_TX_TXOP_CTRL_CFG, 0x0000583f}, //Extension channel backoff. + { RT2860_REG_TX_RTS_CFG, 0x00092b20}, + { RT2860_REG_TX_EXP_ACK_TIME, 0x002400ca}, // default value + { RT2860_REG_HCCAPSMP_TXOP_HLDR_ET, 0x00000002}, + { RT2860_REG_XIFS_TIME_CFG, 0x33a41010}, + { RT2860_REG_PWR_PIN_CFG, 0x00000003}, // patch for 2880-E +#if 1 /* CONFIG_AP_SUPPORT */ + { RT2860_REG_SCHDMA_WMM_AIFSN_CFG, 0x00001173}, + { RT2860_REG_SCHDMA_WMM_CWMIN_CFG, 0x00002344}, + { RT2860_REG_SCHDMA_WMM_CWMAX_CFG, 0x000034a6}, + { RT2860_REG_SCHDMA_WMM_TXOP0_CFG, 0x00100020}, + { RT2860_REG_SCHDMA_WMM_TXOP1_CFG, 0x002F0038}, + { RT2860_REG_TBTT_SYNC_CFG, 0x00012000}, +#else /* CONFIG_STA_SUPPORT */ + { RT2860_REG_SCHDMA_WMM_AIFSN_CFG, 0x00002273}, + { RT2860_REG_SCHDMA_WMM_CWMIN_CFG, 0x00002344}, + { RT2860_REG_SCHDMA_WMM_CWMAX_CFG, 0x000034aa}, +#endif // CONFIG_AP_SUPPORT // +#endif +}; + +#define RT2860_DEF_MAC_SIZE (sizeof(rt2860_def_mac) / sizeof(rt2860_def_mac[0])) + +static const struct +{ + uint8_t reg; + uint8_t val; +} rt2860_def_bbp[] = +{ +#if 1 /* orig */ + { 65, 0x2c }, + { 66, 0x38 }, + { 69, 0x12 }, + { 70, 0x0a }, + { 73, 0x10 }, + { 81, 0x37 }, + { 82, 0x62 }, + { 83, 0x6a }, + { 84, 0x99 }, + { 86, 0x00 }, + { 91, 0x04 }, + { 92, 0x00 }, + { 103, 0x00 }, + { 105, 0x05 }, + { 106, 0x35 }, +#else + { 31, 0x08}, //gary recommend for ACE + { 65, 0x2C}, // fix rssi issue + { 66, 0x38}, // Also set this default value to pAd->BbpTuning.R66CurrentValue at initial + { 68, 0x0B}, // improve Rx sensitivity. + { 69, 0x12}, + { 70, 0xa}, // BBP_R70 will change to 0x8 in ApStartUp and LinkUp for rt2860C, otherwise value is 0xa + { 73, 0x10}, + { 78, 0x0E}, + { 80, 0x08}, // requested by Gary for high power + { 81, 0x37}, + { 82, 0x62}, + { 83, 0x6A}, + { 84, 0x99}, // 0x19 is for rt2860E and after. This is for extension channel overlapping IOT. 0x99 is for rt2860D and before + { 86, 0x00}, // middle range issue, Rory @2008-01-28 + { 91, 0x04}, // middle range issue, Rory @2008-01-28 + { 92, 0x00}, // middle range issue, Rory @2008-01-28 + { 103, 0xC0}, + { 105, 0x01},/*kurtis:0x01 ori*/// 0x05 is for rt2860E to turn on FEQ control. It is safe for rt2860D and before, because Bit 7:2 are reserved in rt2860D and before. + { 106, 0x35}, // Optimizing the Short GI sampling request from Gray @2009-0409 +#endif +}; + +#define RT2860_DEF_BBP_SIZE (sizeof(rt2860_def_bbp) / sizeof(rt2860_def_bbp[0])) + +SYSCTL_NODE(_hw, OID_AUTO, rt2860, CTLFLAG_RD, 0, "RT2860 driver parameters"); + +static int rt2860_tx_stbc = 1; +SYSCTL_INT(_hw_rt2860, OID_AUTO, tx_stbc, CTLFLAG_RW, &rt2860_tx_stbc, 0, "RT2860 Tx STBC"); +TUNABLE_INT("hw.rt2860.tx_stbc", &rt2860_tx_stbc); + +#ifdef RT2860_DEBUG +static int rt2860_debug = 0; +SYSCTL_INT(_hw_rt2860, OID_AUTO, debug, CTLFLAG_RW, &rt2860_debug, 0, "RT2860 debug level"); +TUNABLE_INT("hw.rt2860.debug", &rt2860_debug); +#endif + +/* + * rt2860_attach + */ +int rt2860_attach(device_t dev, int id) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + int error, ntries, i; + + sc = device_get_softc(dev); + ic = &sc->sc_ic; + + sc->dev = dev; + sc->pid = id; + + mtx_init(&sc->lock, device_get_nameunit(dev), + MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); + + sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->mem_rid, RF_ACTIVE); + if (sc->mem == NULL) + { + printf("%s: could not allocate memory resource\n", + device_get_nameunit(dev)); + error = ENXIO; + goto fail; + } + + + sc->bst = rman_get_bustag(sc->mem); + sc->bsh = rman_get_bushandle(sc->mem); + + sc->irq_rid = 0; + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); + if (sc->irq == NULL) + { + printf("%s: could not allocate interrupt resource\n", + device_get_nameunit(dev)); + error = ENXIO; + goto fail; + } + + sc->tx_stbc = rt2860_tx_stbc; + +#ifdef RT2860_DEBUG + sc->debug = rt2860_debug; + + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "debug", CTLFLAG_RW, &sc->debug, 0, "rt2860 debug level"); +#endif + + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: attaching\n", + device_get_nameunit(sc->dev)); + + /* wait for NIC to initialize */ + + for (ntries = 0; ntries < 100; ntries++) + { + sc->mac_rev = rt2860_io_mac_read(sc, RT2860_REG_MAC_CSR0); + if (sc->mac_rev != 0x00000000 && sc->mac_rev != 0xffffffff) + break; + + DELAY(10); + } + + if (ntries == 100) + { + printf("%s: timeout waiting for NIC to initialize\n", + device_get_nameunit(dev)); + error = EIO; + goto fail; + } + + rt2860_read_eeprom(sc); + memcpy(ic->ic_macaddr, sc->mac_addr, 6); + + printf("%s: MAC/BBP RT2860 (rev 0x%08x), RF %s\n", + device_get_nameunit(sc->dev), sc->mac_rev, + rt2860_rf_name(sc->rf_rev)); + + RT2860_SOFTC_LOCK(sc); + /* clear key tables */ + rt2860_asic_clear_keytables(sc); + RT2860_SOFTC_UNLOCK(sc); + + /* allocate Tx and Rx rings */ + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + { + error = rt2860_alloc_tx_ring(sc, &sc->tx_ring[i], i); + if (error != 0) + { + printf("%s: could not allocate Tx ring #%d\n", + device_get_nameunit(sc->dev), i); + goto fail; + } + } + + sc->tx_ring_mgtqid = 5; + + error = rt2860_alloc_rx_ring(sc, &sc->rx_ring); + if (error != 0) + { + printf("%s: could not allocate Rx ring\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + callout_init(&sc->periodic_ch, 0); + callout_init_mtx(&sc->tx_watchdog_ch, &sc->lock, 0); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(dev); + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_phytype = IEEE80211_T_HT; + + ic->ic_caps = IEEE80211_C_MONITOR | + IEEE80211_C_IBSS | + IEEE80211_C_STA | + IEEE80211_C_AHDEMO | + IEEE80211_C_HOSTAP | + IEEE80211_C_WDS | + IEEE80211_C_MBSS | + IEEE80211_C_BGSCAN | + IEEE80211_C_TXPMGT | + IEEE80211_C_SHPREAMBLE | + IEEE80211_C_SHSLOT | + IEEE80211_C_TXFRAG | + IEEE80211_C_BURST | + IEEE80211_C_WME | + IEEE80211_C_WPA; + +#ifdef RT2860_HW_CRYPTO + ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP | + IEEE80211_CRYPTO_TKIP | + IEEE80211_CRYPTO_TKIPMIC | + IEEE80211_CRYPTO_AES_CCM; +#endif + ic->ic_htcaps = IEEE80211_HTC_HT | + IEEE80211_HTC_AMSDU | /* A-MSDU Tx */ + IEEE80211_HTC_AMPDU | /* A-MPDU Tx */ + IEEE80211_HTC_SMPS | /* MIMO power save */ + IEEE80211_HTCAP_MAXAMSDU_3839 | /* max. A-MSDU Rx length */ + IEEE80211_HTCAP_CHWIDTH40 | /* HT 40MHz channel width */ + IEEE80211_HTCAP_GREENFIELD | /* HT greenfield */ + IEEE80211_HTCAP_SHORTGI20 | /* HT 20MHz short GI */ + IEEE80211_HTCAP_SHORTGI40 | /* HT 40MHz short GI */ + IEEE80211_HTCAP_SMPS_OFF; /* MIMO power save disabled */ + + /* spatial streams */ + + if (sc->nrxpath == 2) + ic->ic_htcaps |= IEEE80211_HTCAP_RXSTBC_2STREAM; + else if (sc->nrxpath == 3) + ic->ic_htcaps |= IEEE80211_HTCAP_RXSTBC_3STREAM; + else + ic->ic_htcaps |= IEEE80211_HTCAP_RXSTBC_1STREAM; + + if (sc->ntxpath > 1) + ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC; + + /* delayed BA */ + +// if (sc->mac_rev != 0x28600100) + if (sc->mac_rev != 0x28600102) + ic->ic_htcaps |= IEEE80211_HTCAP_DELBA; + + /* init channels */ + + ic->ic_nchans = 0; + + rt2860_init_channels(sc); + + rt2860_init_channels_ht40(sc); + + ieee80211_ifattach(ic); + + ic->ic_vap_create = rt2860_vap_create; + ic->ic_vap_delete = rt2860_vap_delete; + ic->ic_parent = rt2860_parent; + ic->ic_transmit = rt2860_transmit; + + ic->ic_node_alloc = rt2860_node_alloc; + + sc->node_cleanup = ic->ic_node_cleanup; + ic->ic_node_cleanup = rt2860_node_cleanup; + + ic->ic_setregdomain = rt2860_setregdomain; + ic->ic_getradiocaps = rt2860_getradiocaps; + ic->ic_scan_start = rt2860_scan_start; + ic->ic_scan_end = rt2860_scan_end; + ic->ic_set_channel = rt2860_set_channel; + ic->ic_newassoc = rt2860_newassoc; + ic->ic_updateslot = rt2860_updateslot; + ic->ic_update_promisc = rt2860_update_promisc; + ic->ic_wme.wme_update = rt2860_wme_update; + ic->ic_raw_xmit = rt2860_raw_xmit; + + sc->recv_action = ic->ic_recv_action; + ic->ic_recv_action = rt2860_recv_action; + + sc->send_action = ic->ic_send_action; + ic->ic_send_action = rt2860_send_action; + + sc->addba_response = ic->ic_addba_response; + ic->ic_addba_response = rt2860_addba_response; + + sc->addba_stop = ic->ic_addba_stop; + ic->ic_addba_stop = rt2860_addba_stop; + + sc->ampdu_rx_start = ic->ic_ampdu_rx_start; + ic->ic_ampdu_rx_start = rt2860_ampdu_rx_start; + + sc->ampdu_rx_stop = ic->ic_ampdu_rx_stop; + ic->ic_ampdu_rx_stop = rt2860_ampdu_rx_stop; + + /* hardware requires padding between 802.11 frame header and body */ + + ic->ic_flags |= IEEE80211_F_DATAPAD | IEEE80211_F_DOTH; + + ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; + + ieee80211_radiotap_attach(ic, + &sc->txtap.ihdr, sizeof(sc->txtap), + RT2860_SOFTC_TX_RADIOTAP_PRESENT, + &sc->rxtap.ihdr, sizeof(sc->rxtap), + RT2860_SOFTC_RX_RADIOTAP_PRESENT); + + /* init task queue */ + + TASK_INIT(&sc->rx_done_task, 0, rt2860_rx_done_task, sc); + TASK_INIT(&sc->tx_done_task, 0, rt2860_tx_done_task, sc); + TASK_INIT(&sc->fifo_sta_full_task, 0, rt2860_fifo_sta_full_task, sc); + TASK_INIT(&sc->periodic_task, 0, rt2860_periodic_task, sc); + + sc->rx_process_limit = 100; + + sc->taskqueue = taskqueue_create("rt2860_taskq", M_NOWAIT, + taskqueue_thread_enqueue, &sc->taskqueue); + + taskqueue_start_threads(&sc->taskqueue, 1, PI_NET, "%s taskq", + device_get_nameunit(sc->dev)); + + rt2860_sysctl_attach(sc); + + if (bootverbose) + ieee80211_announce(ic); + + /* set up interrupt */ + + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, rt2860_intr, sc, &sc->irqh); + if (error != 0) + { + printf("%s: could not set up interrupt\n", + device_get_nameunit(dev)); + goto fail; + } + + return 0; + +fail: + + /* free Tx and Rx rings */ + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + rt2860_free_tx_ring(sc, &sc->tx_ring[i]); + + rt2860_free_rx_ring(sc, &sc->rx_ring); + + mtx_destroy(&sc->lock); + + if (sc->mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); + + if (sc->irq != NULL) + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); + + return error; +} + +/* + * rt2860_detach + */ +int rt2860_detach(device_t dev) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + int i; + + sc = device_get_softc(dev); + ic = &sc->sc_ic; + + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: detaching\n", + device_get_nameunit(sc->dev)); + + RT2860_SOFTC_LOCK(sc); + + sc->sc_flags &= ~RT2860_RUNNING; + + callout_stop(&sc->periodic_ch); + callout_stop(&sc->tx_watchdog_ch); + + taskqueue_drain(sc->taskqueue, &sc->rx_done_task); + taskqueue_drain(sc->taskqueue, &sc->tx_done_task); + taskqueue_drain(sc->taskqueue, &sc->fifo_sta_full_task); + taskqueue_drain(sc->taskqueue, &sc->periodic_task); + + /* free Tx and Rx rings */ + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + rt2860_free_tx_ring(sc, &sc->tx_ring[i]); + + rt2860_free_rx_ring(sc, &sc->rx_ring); + + RT2860_SOFTC_UNLOCK(sc); + + ieee80211_ifdetach(ic); + mbufq_drain(&sc->sc_snd); + + taskqueue_free(sc->taskqueue); + + mtx_destroy(&sc->lock); + + bus_generic_detach(dev); + + bus_teardown_intr(dev, sc->irq, sc->irqh); + + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); + + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); + + return 0; +} + +/* + * rt2860_shutdown + */ +int rt2860_shutdown(device_t dev) +{ + struct rt2860_softc *sc; + + sc = device_get_softc(dev); + + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: shutting down\n", + device_get_nameunit(sc->dev)); + + rt2860_stop(sc); + + sc->flags &= ~RT2860_SOFTC_FLAGS_UCODE_LOADED; + + return 0; +} + +/* + * rt2860_suspend + */ +int rt2860_suspend(device_t dev) +{ + struct rt2860_softc *sc; + + sc = device_get_softc(dev); + + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: suspending\n", + device_get_nameunit(sc->dev)); + + rt2860_stop(sc); + + sc->flags &= ~RT2860_SOFTC_FLAGS_UCODE_LOADED; + + return 0; +} + +/* + * rt2860_resume + */ +int rt2860_resume(device_t dev) +{ + struct rt2860_softc *sc; + + sc = device_get_softc(dev); + + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: resuming\n", + device_get_nameunit(sc->dev)); + + if (sc->sc_ic.ic_nrunning > 0) + rt2860_init(sc); + + return 0; +} + +/* + * rt2860_init_channels + */ +static void rt2860_init_channels(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211_channel *c; + int i, flags; + + ic = &sc->sc_ic; + + /* set supported channels for 2GHz band */ + + for (i = 1; i <= 14; i++) + { + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_B; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_B | IEEE80211_CHAN_HT20; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_G; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_G | IEEE80211_CHAN_HT20; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + } + + /* set supported channels for 5GHz band */ + + if (sc->rf_rev == RT2860_EEPROM_RF_2850 || + sc->rf_rev == RT2860_EEPROM_RF_2750 || + sc->rf_rev == RT2860_EEPROM_RF_3052) + { + for (i = 36; i <= 64; i += 4) + { + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_A; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + } + + for (i = 100; i <= 140; i += 4) + { + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_A; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + } + + for (i = 149; i <= 165; i += 4) + { + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_A; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + + c = &ic->ic_channels[ic->ic_nchans++]; + flags = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20; + + c->ic_freq = ieee80211_ieee2mhz(i, flags); + c->ic_ieee = i; + c->ic_flags = flags; + } + } +} + +/* + * rt2860_init_channels_ht40 + */ +static void rt2860_init_channels_ht40(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211_channel *c, *cent, *ext; + int i, flags; + + ic = &sc->sc_ic; + + /* set supported channels for 2GHz band */ + + for (i = 1; i <= 14; i++) + { + flags = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40; + + /* find the center channel */ + + cent = ieee80211_find_channel_byieee(ic, i, + flags & ~IEEE80211_CHAN_HT); + if (cent == NULL) + { + printf("%s: skip channel %d, could not find center channel\n", + device_get_nameunit(sc->dev), i); + continue; + } + + /* find the extension channel */ + + ext = ieee80211_find_channel(ic, cent->ic_freq + 20, + flags & ~IEEE80211_CHAN_HT); + if (ext == NULL) + { + printf("%s: skip channel %d, could not find extension channel\n", + device_get_nameunit(sc->dev), i); + continue; + } + + c = &ic->ic_channels[ic->ic_nchans++]; + + *c = *cent; + c->ic_extieee = ext->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40U; + + c = &ic->ic_channels[ic->ic_nchans++]; + + *c = *ext; + c->ic_extieee = cent->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40D; + } + + /* set supported channels for 5GHz band */ + + if (sc->rf_rev == RT2860_EEPROM_RF_2850 || + sc->rf_rev == RT2860_EEPROM_RF_2750 || + sc->rf_rev == RT2860_EEPROM_RF_3052) + { + for (i = 36; i <= 64; i += 4) + { + flags = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40; + + /* find the center channel */ + + cent = ieee80211_find_channel_byieee(ic, i, + flags & ~IEEE80211_CHAN_HT); + if (cent == NULL) + { + printf("%s: skip channel %d, could not find center channel\n", + device_get_nameunit(sc->dev), i); + continue; + } + + /* find the extension channel */ + + ext = ieee80211_find_channel(ic, cent->ic_freq + 20, + flags & ~IEEE80211_CHAN_HT); + if (ext == NULL) + { + printf("%s: skip channel %d, could not find extension channel\n", + device_get_nameunit(sc->dev), i); + continue; + } + + c = &ic->ic_channels[ic->ic_nchans++]; + + *c = *cent; + c->ic_extieee = ext->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40U; + + c = &ic->ic_channels[ic->ic_nchans++]; + + *c = *ext; + c->ic_extieee = cent->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40D; + } + + for (i = 100; i <= 140; i += 4) + { + flags = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40; + + /* find the center channel */ + + cent = ieee80211_find_channel_byieee(ic, i, + flags & ~IEEE80211_CHAN_HT); + if (cent == NULL) + { + printf("%s: skip channel %d, could not find center channel\n", + device_get_nameunit(sc->dev), i); + continue; + } + + /* find the extension channel */ + + ext = ieee80211_find_channel(ic, cent->ic_freq + 20, + flags & ~IEEE80211_CHAN_HT); + if (ext == NULL) + { + printf("%s: skip channel %d, could not find extension channel\n", + device_get_nameunit(sc->dev), i); + continue; + } + + c = &ic->ic_channels[ic->ic_nchans++]; + + *c = *cent; + c->ic_extieee = ext->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40U; + + c = &ic->ic_channels[ic->ic_nchans++]; + + *c = *ext; + c->ic_extieee = cent->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40D; + } + + for (i = 149; i <= 165; i += 4) + { + flags = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40; + + /* find the center channel */ + + cent = ieee80211_find_channel_byieee(ic, i, + flags & ~IEEE80211_CHAN_HT); + if (cent == NULL) + { + printf("%s: skip channel %d, could not find center channel\n", + device_get_nameunit(sc->dev), i); + continue; + } + + /* find the extension channel */ + + ext = ieee80211_find_channel(ic, cent->ic_freq + 20, + flags & ~IEEE80211_CHAN_HT); + if (ext == NULL) + { + printf("%s: skip channel %d, could not find extension channel\n", + device_get_nameunit(sc->dev), i); + continue; + } + + c = &ic->ic_channels[ic->ic_nchans++]; + + *c = *cent; + c->ic_extieee = ext->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40U; + + c = &ic->ic_channels[ic->ic_nchans++]; + + *c = *ext; + c->ic_extieee = cent->ic_ieee; + c->ic_flags &= ~IEEE80211_CHAN_HT; + c->ic_flags |= IEEE80211_CHAN_HT40D; + } + } +} + +/* + * rt2860_init_locked + */ +static void rt2860_init_locked(void *priv) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct ieee80211vap *vap; + int error, i, ntries; + uint32_t tmp, stacnt[6]; + const struct firmware *fp; + + sc = priv; + ic = &sc->sc_ic; + vap = TAILQ_FIRST(&ic->ic_vaps); + + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: initializing\n", + device_get_nameunit(sc->dev)); + + RT2860_SOFTC_ASSERT_LOCKED(sc); + + if (sc->mac_rev != 0x28720200) + { + if (!(sc->flags & RT2860_SOFTC_FLAGS_UCODE_LOADED)) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: loading 8051 microcode\n", + device_get_nameunit(sc->dev)); + + fp = firmware_get("rt2860fw"); + if (fp == NULL) { + device_printf(sc->dev, + "unable to receive rt2860fw firmware image\n"); + goto fail; + } + + error = rt2860_io_mcu_load_ucode(sc, fp->data, fp->datasize); + if (error != 0) + { + printf("%s: could not load 8051 microcode\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: 8051 microcode was successfully loaded\n", + device_get_nameunit(sc->dev)); + + sc->flags |= RT2860_SOFTC_FLAGS_UCODE_LOADED; + } + } + else + { + sc->flags |= RT2860_SOFTC_FLAGS_UCODE_LOADED; + + /* Blink every TX */ +#define LED_CFG_LED_POLARITY (1<<30) +#define LED_CFG_Y_LED_MODE_ONTX (1<<28) +#define LED_CFG_G_LED_MODE_ONTX (1<<26) +#define LED_CFG_R_LED_MODE_ONTX (1<<24) +#define LED_CFG_SLOW_BLK_TIME (0x03<<16) /* sec */ +#define LED_CFG_LED_OFF_TIME (0x1e<<8) /* msec */ +#define LED_CFG_LED_ON_TIME (0x46) /* msec */ + rt2860_io_mac_write(sc, RT2860_REG_LED_CFG, + LED_CFG_LED_POLARITY | + LED_CFG_Y_LED_MODE_ONTX | + LED_CFG_G_LED_MODE_ONTX | + LED_CFG_R_LED_MODE_ONTX | + LED_CFG_SLOW_BLK_TIME | + LED_CFG_LED_OFF_TIME | + LED_CFG_LED_ON_TIME); + } + + rt2860_io_mac_write(sc, RT2860_REG_PWR_PIN_CFG, 0x2); + + /* disable DMA engine */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG); + + tmp &= 0xff0; + tmp |= RT2860_REG_TX_WB_DDONE; + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG, tmp); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_RST_IDX, 0xffffffff); + + /* PBF hardware reset */ + + rt2860_io_mac_write(sc, RT2860_REG_PBF_SYS_CTRL, 0xe1f); + rt2860_io_mac_write(sc, RT2860_REG_PBF_SYS_CTRL, 0xe00); + + /* wait while DMA engine is busy */ + + for (ntries = 0; ntries < 100; ntries++) + { + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG); + if (!(tmp & (RT2860_REG_TX_DMA_BUSY | RT2860_REG_RX_DMA_BUSY))) + break; + + DELAY(1000); + } + + if (ntries == 100) + { + printf("%s: timeout waiting for DMA engine\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + tmp &= 0xff0; + tmp |= RT2860_REG_TX_WB_DDONE; + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG, tmp); + + /* reset Rx and Tx rings */ + + tmp = RT2860_REG_RST_IDX_RX | + RT2860_REG_RST_IDX_TX_MGMT | + RT2860_REG_RST_IDX_TX_HCCA | + RT2860_REG_RST_IDX_TX_AC3 | + RT2860_REG_RST_IDX_TX_AC2 | + RT2860_REG_RST_IDX_TX_AC1 | + RT2860_REG_RST_IDX_TX_AC0; + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_RST_IDX, tmp); + + /* PBF hardware reset */ + + rt2860_io_mac_write(sc, RT2860_REG_PBF_SYS_CTRL, 0xe1f); + rt2860_io_mac_write(sc, RT2860_REG_PBF_SYS_CTRL, 0xe00); + + rt2860_io_mac_write(sc, RT2860_REG_PWR_PIN_CFG, 0x3); + + rt2860_io_mac_write(sc, RT2860_REG_SYS_CTRL, + RT2860_REG_MAC_SRST | RT2860_REG_BBP_HRST); + rt2860_io_mac_write(sc, RT2860_REG_SYS_CTRL, 0); + + /* init Tx power per rate */ + + for (i = 0; i < RT2860_SOFTC_TXPOW_RATE_COUNT; i++) + { + if (sc->txpow_rate_20mhz[i] == 0xffffffff) + continue; + + rt2860_io_mac_write(sc, RT2860_REG_TX_PWR_CFG(i), + sc->txpow_rate_20mhz[i]); + } + + for (i = 0; i < RT2860_DEF_MAC_SIZE; i++) + rt2860_io_mac_write(sc, rt2860_def_mac[i].reg, + rt2860_def_mac[i].val); + + /* wait while MAC is busy */ + + for (ntries = 0; ntries < 100; ntries++) + { + if (!(rt2860_io_mac_read(sc, RT2860_REG_STATUS_CFG) & + (RT2860_REG_STATUS_TX_BUSY | RT2860_REG_STATUS_RX_BUSY))) + break; + + DELAY(1000); + } + + if (ntries == 100) + { + printf("%s: timeout waiting for MAC\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + /* clear Host to MCU mailbox */ + + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX_BBP_AGENT, 0); + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX, 0); + + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_BOOT, + RT2860_REG_H2M_TOKEN_NO_INTR, 0); + + DELAY(1000); + + error = rt2860_init_bbp(sc); + if (error != 0) + goto fail; + + /* set up maximum buffer sizes */ + + tmp = (1 << 12) | RT2860_MAX_AGG_SIZE; + + rt2860_io_mac_write(sc, RT2860_REG_MAX_LEN_CFG, tmp); + + if (sc->mac_rev == 0x28720200) + { + /* set max. PSDU length from 16K to 32K bytes */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_MAX_LEN_CFG); + + tmp &= ~(3 << 12); + tmp |= (2 << 12); + + rt2860_io_mac_write(sc, RT2860_REG_MAX_LEN_CFG, tmp); + } + + if (sc->mac_rev >= 0x28720200 && sc->mac_rev < 0x30700200) + { + tmp = rt2860_io_mac_read(sc, RT2860_REG_MAX_LEN_CFG); + + tmp &= 0xfff; + tmp |= 0x2000; + + rt2860_io_mac_write(sc, RT2860_REG_MAX_LEN_CFG, tmp); + } + + /* set mac address */ + + rt2860_asic_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + + /* clear statistic registers */ + + rt2860_io_mac_read_multi(sc, RT2860_REG_RX_STA_CNT0, + stacnt, sizeof(stacnt)); + + /* set RTS threshold */ + + rt2860_asic_update_rtsthreshold(sc); + + /* set Tx power */ + + rt2860_asic_update_txpower(sc); + + /* set up protection mode */ + + sc->tx_ampdu_sessions = 0; + + rt2860_asic_updateprot(sc); + + /* clear beacon frame space (entries = 8, entry size = 512) */ + + rt2860_io_mac_set_region_4(sc, RT2860_REG_BEACON_BASE(0), 0, 1024); + + taskqueue_unblock(sc->taskqueue); + + /* init Tx rings (4 EDCAs + HCCA + MGMT) */ + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + rt2860_reset_tx_ring(sc, &sc->tx_ring[i]); + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + { + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_BASE_PTR(i), + sc->tx_ring[i].desc_phys_addr); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_MAX_CNT(i), + RT2860_SOFTC_TX_RING_DESC_COUNT); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_CTX_IDX(i), 0); + } + + /* init Rx ring */ + + rt2860_reset_rx_ring(sc, &sc->rx_ring); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_BASE_PTR, + sc->rx_ring.desc_phys_addr); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_MAX_CNT, + RT2860_SOFTC_RX_RING_DATA_COUNT); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_CALC_IDX, + RT2860_SOFTC_RX_RING_DATA_COUNT - 1); + + /* wait while DMA engine is busy */ + + for (ntries = 0; ntries < 100; ntries++) + { + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG); + if (!(tmp & (RT2860_REG_TX_DMA_BUSY | RT2860_REG_RX_DMA_BUSY))) + break; + + DELAY(1000); + } + + if (ntries == 100) + { + printf("%s: timeout waiting for DMA engine\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + tmp &= 0xff0; + tmp |= RT2860_REG_TX_WB_DDONE; + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG, tmp); + + /* disable interrupts mitigation */ + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_DELAY_INT_CFG, 0); + + /* select Main antenna for 1T1R devices */ + if (sc->rf_rev == RT2860_EEPROM_RF_2020 || + sc->rf_rev == RT2860_EEPROM_RF_3020 || + sc->rf_rev == RT2860_EEPROM_RF_3320) + rt3090_set_rx_antenna(sc, 0); + + /* send LEDs operating mode to microcontroller */ + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_LED1, + RT2860_REG_H2M_TOKEN_NO_INTR, sc->led_off[0]); + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_LED2, + RT2860_REG_H2M_TOKEN_NO_INTR, sc->led_off[1]); + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_LED3, + RT2860_REG_H2M_TOKEN_NO_INTR, sc->led_off[2]); + + /* turn radio LED on */ + + rt2860_led_cmd(sc, RT2860_LED_CMD_RADIO_ON); + + /* write vendor-specific BBP values (from EEPROM) */ + + for (i = 0; i < RT2860_SOFTC_BBP_EEPROM_COUNT; i++) + { + if (sc->bbp_eeprom[i].reg == 0x00 || + sc->bbp_eeprom[i].reg == 0xff) + continue; + + rt2860_io_bbp_write(sc, sc->bbp_eeprom[i].reg, + sc->bbp_eeprom[i].val); + } + + if ((sc->mac_rev & 0xffff0000) >= 0x30710000) + rt3090_rf_init(sc); + + if (sc->mac_rev != 0x28720200) { + /* 0x28720200 don`t have RT2860_REG_SCHDMA_GPIO_CTRL_CFG */ + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_GPIO_CTRL_CFG); + if (tmp & (1 << 2)) { + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_SLEEP, + RT2860_REG_H2M_TOKEN_RADIOOFF, 0x02ff); + rt2860_io_mcu_cmd_check(sc, RT2860_REG_H2M_TOKEN_RADIOOFF); + + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_WAKEUP, + RT2860_REG_H2M_TOKEN_WAKEUP, 0); + rt2860_io_mcu_cmd_check(sc, RT2860_REG_H2M_TOKEN_WAKEUP); + } + } + + if ((sc->mac_rev & 0xffff0000) >= 0x30710000) + rt3090_rf_wakeup(sc); + + /* disable non-existing Rx chains */ + + tmp = rt2860_io_bbp_read(sc, 3); + + tmp &= ~((1 << 4) | (1 << 3)); + + if (sc->nrxpath == 3) + tmp |= (1 << 4); + else if (sc->nrxpath == 2) + tmp |= (1 << 3); + + rt2860_io_bbp_write(sc, 3, tmp); + + /* disable non-existing Tx chains */ + + tmp = rt2860_io_bbp_read(sc, 1); + + tmp &= ~((1 << 4) | (1 << 3)); + + if (sc->ntxpath == 2) + tmp |= (1 << 4); + + rt2860_io_bbp_write(sc, 1, tmp); + + if ((sc->mac_rev & 0xffff0000) >= 0x30710000) + rt3090_rf_setup(sc); + + /* RT3050 and RT3052 */ + if (sc->rf_rev == RT2860_EEPROM_RF_3020 || + sc->rf_rev == RT2860_EEPROM_RF_3022) + { + /* calibrate RF */ + tmp = rt2860_io_rf_read(sc, 30); + tmp |= 0x80; + rt2860_io_rf_write(sc, 30, tmp); + DELAY(1000); + tmp &= 0x7F; + rt2860_io_rf_write(sc, 30, tmp); + + /* Initialize RF register to default value */ + rt2860_io_rf_load_defaults(sc); + } + + /* set current channel */ + rt2860_rf_set_chan(sc, ic->ic_curchan); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WMM_TXOP0_CFG, 0); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WMM_TXOP1_CFG, + (48 << 16) | 96); + + if ((sc->mac_rev & 0xffff) != 0x0101) + rt2860_io_mac_write(sc, RT2860_REG_TX_TXOP_CTRL_CFG, 0x583f); + + /* clear pending interrupts */ + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_INT_STATUS, 0xffffffff); + + /* enable interrupts */ + + tmp = RT2860_REG_INT_TX_COHERENT | + RT2860_REG_INT_RX_COHERENT | + RT2860_REG_INT_GP_TIMER | + RT2860_REG_INT_AUTO_WAKEUP | + RT2860_REG_INT_FIFO_STA_FULL | + RT2860_REG_INT_PRE_TBTT | + RT2860_REG_INT_TBTT | + RT2860_REG_INT_TXRX_COHERENT | + RT2860_REG_INT_MCU_CMD | + RT2860_REG_INT_TX_MGMT_DONE | + RT2860_REG_INT_TX_HCCA_DONE | + RT2860_REG_INT_TX_AC3_DONE | + RT2860_REG_INT_TX_AC2_DONE | + RT2860_REG_INT_TX_AC1_DONE | + RT2860_REG_INT_TX_AC0_DONE | + RT2860_REG_INT_RX_DONE; + + sc->intr_enable_mask = tmp; + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_INT_MASK, tmp); + + if (rt2860_txrx_enable(sc) != 0) + goto fail; + + /* clear garbage interrupts */ + + tmp = rt2860_io_mac_read(sc, 0x1300); + + sc->sc_flags |= RT2860_RUNNING; + + sc->periodic_round = 0; + + callout_reset(&sc->periodic_ch, hz / 10, rt2860_periodic, sc); + + return; + +fail: + + rt2860_stop_locked(sc); +} + +/* + * rt2860_init + */ +static void rt2860_init(void *priv) +{ + struct rt2860_softc *sc; + + sc = priv; + + RT2860_SOFTC_LOCK(sc); + + rt2860_init_locked(sc); + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_init_bbp + */ +static int rt2860_init_bbp(struct rt2860_softc *sc) +{ + int ntries, i; + uint8_t tmp; + + for (ntries = 0; ntries < 20; ntries++) + { + tmp = rt2860_io_bbp_read(sc, 0); + if (tmp != 0x00 && tmp != 0xff) + break; + } + + if (tmp == 0x00 || tmp == 0xff) + { + printf("%s: timeout waiting for BBP to wakeup\n", + device_get_nameunit(sc->dev)); + return ETIMEDOUT; + } + + for (i = 0; i < RT2860_DEF_BBP_SIZE; i++) + rt2860_io_bbp_write(sc, rt2860_def_bbp[i].reg, + rt2860_def_bbp[i].val); + + if ((sc->mac_rev & 0xffff) != 0x0101) + rt2860_io_bbp_write(sc, 84, 0x19); + +// if (sc->mac_rev == 0x28600100) + if (sc->mac_rev == 0x28600102) + { + rt2860_io_bbp_write(sc, 69, 0x16); + rt2860_io_bbp_write(sc, 73, 0x12); + } + + return 0; +} + +/* + * rt2860_stop_locked + */ +static void rt2860_stop_locked(void *priv) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + uint32_t tmp; + + sc = priv; + ic = &sc->sc_ic; + + RT2860_DPRINTF(sc, RT2860_DEBUG_ANY, + "%s: stopping\n", + device_get_nameunit(sc->dev)); + + RT2860_SOFTC_ASSERT_LOCKED(sc); + + sc->tx_timer = 0; + + if (sc->sc_flags & RT2860_RUNNING) + rt2860_led_cmd(sc, RT2860_LED_CMD_RADIO_OFF); + + sc->sc_flags &= ~RT2860_RUNNING; + + callout_stop(&sc->periodic_ch); + callout_stop(&sc->tx_watchdog_ch); + + RT2860_SOFTC_UNLOCK(sc); + + taskqueue_block(sc->taskqueue); + + taskqueue_drain(sc->taskqueue, &sc->rx_done_task); + taskqueue_drain(sc->taskqueue, &sc->tx_done_task); + taskqueue_drain(sc->taskqueue, &sc->fifo_sta_full_task); + taskqueue_drain(sc->taskqueue, &sc->periodic_task); + + RT2860_SOFTC_LOCK(sc); + + /* clear key tables */ + + rt2860_asic_clear_keytables(sc); + + /* disable interrupts */ + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_INT_MASK, 0); + + /* disable Tx/Rx */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_SYS_CTRL); + + tmp &= ~(RT2860_REG_RX_ENABLE | RT2860_REG_TX_ENABLE); + + rt2860_io_mac_write(sc, RT2860_REG_SYS_CTRL, tmp); + + /* reset adapter */ + + rt2860_io_mac_write(sc, RT2860_REG_SYS_CTRL, + RT2860_REG_MAC_SRST | RT2860_REG_BBP_HRST); + rt2860_io_mac_write(sc, RT2860_REG_SYS_CTRL, 0); +} + +/* + * rt2860_stop + */ +static void rt2860_stop(void *priv) +{ + struct rt2860_softc *sc; + + sc = priv; + + RT2860_SOFTC_LOCK(sc); + + rt2860_stop_locked(sc); + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_start + */ +static void rt2860_start(struct rt2860_softc *sc) +{ + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct ieee80211_key *k; + struct mbuf *m; + int qid; + + if (!(sc->sc_flags & RT2860_RUNNING)) + return; + + RT2860_SOFTC_LOCK(sc); + + for (;;) + { + m = mbufq_dequeue(&sc->sc_snd); + if (m == NULL) + break; + + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + + KASSERT(ni != NULL, ("%s:%d: fail", __func__, __LINE__)); + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + ieee80211_free_node(ni); + m_freem(m); + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + continue; + } + } + wh = NULL; /* Catch any invalid use */ + + m->m_pkthdr.rcvif = NULL; + + qid = M_WME_GETAC(m); + + RT2860_SOFTC_TX_RING_LOCK(&sc->tx_ring[qid]); + + if (sc->tx_ring[qid].data_queued >= RT2860_SOFTC_TX_RING_DATA_COUNT) + { + RT2860_SOFTC_TX_RING_UNLOCK(&sc->tx_ring[qid]); + + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: if_start: Tx ring with qid=%d is full\n", + device_get_nameunit(sc->dev), qid); + + m_freem(m); + ieee80211_free_node(ni); + + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + + sc->tx_data_queue_full[qid]++; + + break; + } + + if (rt2860_tx_data(sc, m, ni, qid) != 0) + { + RT2860_SOFTC_TX_RING_UNLOCK(&sc->tx_ring[qid]); + + ieee80211_free_node(ni); + + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + + break; + } + + RT2860_SOFTC_TX_RING_UNLOCK(&sc->tx_ring[qid]); + + rt2860_drain_fifo_stats(sc); + + sc->tx_timer = RT2860_TX_WATCHDOG_TIMEOUT; + + callout_reset(&sc->tx_watchdog_ch, hz, rt2860_tx_watchdog, sc); + } + RT2860_SOFTC_UNLOCK(sc); + +} + +/* + * rt2860_vap_create + */ +static struct ieee80211vap *rt2860_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 rt2860_softc *sc; + struct rt2860_softc_vap *rvap; + struct ieee80211vap *vap; + + sc = ic->ic_softc; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: VAP create: opmode=%s\n", + device_get_nameunit(sc->dev), + ieee80211_opmode_name[opmode]); + + switch (opmode) + { + case IEEE80211_M_IBSS: + case IEEE80211_M_STA: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + if ((sc->napvaps + sc->nadhocvaps + sc->nstavaps) != 0) + { + device_printf(sc->dev, "only 1 VAP supported\n"); + return NULL; + } + + if (opmode == IEEE80211_M_STA) + flags |= IEEE80211_CLONE_NOBEACONS; + break; + + case IEEE80211_M_WDS: + if (sc->napvaps == 0) + { + device_printf(sc->dev, "WDS only supported in AP mode\n"); + return NULL; + } + break; + + case IEEE80211_M_MONITOR: + break; + + default: + device_printf(sc->dev, "unknown opmode %d\n", opmode); + return NULL; + } + + rvap = (struct rt2860_softc_vap *) malloc(sizeof(struct rt2860_softc_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (rvap == NULL) + return NULL; + + vap = &rvap->vap; + + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); + + rvap->newstate = vap->iv_newstate; + vap->iv_newstate = rt2860_vap_newstate; + + vap->iv_reset = rt2860_vap_reset; +#ifdef RT2860_HW_CRYPTO + vap->iv_key_update_begin = rt2860_vap_key_update_begin; + vap->iv_key_update_end = rt2860_vap_key_update_end; + vap->iv_key_set = rt2860_vap_key_set; + vap->iv_key_delete = rt2860_vap_key_delete; +#endif + vap->iv_update_beacon = rt2860_vap_update_beacon; + + rt2860_amrr_init(&rvap->amrr, vap, + sc->ntxpath, + RT2860_AMRR_MIN_SUCCESS_THRESHOLD, + RT2860_AMRR_MAX_SUCCESS_THRESHOLD, + 500); + + vap->iv_max_aid = RT2860_SOFTC_STAID_COUNT; + + /* overwrite default Rx A-MPDU factor */ + + vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K; + vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; + vap->iv_ampdu_limit = vap->iv_ampdu_rxmax; + + ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, + mac); + + switch (vap->iv_opmode) + { + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + case IEEE80211_M_AHDEMO: + sc->napvaps++; + break; + + case IEEE80211_M_IBSS: + sc->nadhocvaps++; + break; + + case IEEE80211_M_STA: + sc->nstavaps++; + break; + + case IEEE80211_M_WDS: + sc->nwdsvaps++; + break; + + default: + break; + } + + sc->nvaps++; + + if (sc->napvaps > 0) + ic->ic_opmode = IEEE80211_M_HOSTAP; + else if (sc->nadhocvaps > 0) + ic->ic_opmode = IEEE80211_M_IBSS; + else if (sc->nstavaps > 0) + ic->ic_opmode = IEEE80211_M_STA; + else + ic->ic_opmode = opmode; + + return vap; +} + +/* + * rt2860_vap_delete + */ +static void rt2860_vap_delete(struct ieee80211vap *vap) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_vap *rvap; + enum ieee80211_opmode opmode; + + ic = vap->iv_ic; + sc = ic->ic_softc; + rvap = (struct rt2860_softc_vap *) vap; + opmode = vap->iv_opmode; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: VAP delete: opmode=%s\n", + device_get_nameunit(sc->dev), ieee80211_opmode_name[opmode]); + + rt2860_amrr_cleanup(&rvap->amrr); + + ieee80211_vap_detach(vap); + + if (rvap->beacon_mbuf != NULL) + { + m_free(rvap->beacon_mbuf); + rvap->beacon_mbuf = NULL; + } + + switch (opmode) + { + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + case IEEE80211_M_AHDEMO: + sc->napvaps--; + break; + + case IEEE80211_M_IBSS: + sc->nadhocvaps--; + break; + + case IEEE80211_M_STA: + sc->nstavaps--; + break; + + case IEEE80211_M_WDS: + sc->nwdsvaps--; + break; + + default: + break; + } + + sc->nvaps--; + + if (sc->napvaps > 0) + ic->ic_opmode = IEEE80211_M_HOSTAP; + else if (sc->nadhocvaps > 0) + ic->ic_opmode = IEEE80211_M_IBSS; + else if (sc->nstavaps > 0) + ic->ic_opmode = IEEE80211_M_STA; + + free(rvap, M_80211_VAP); +} + +/* + * rt2860_reset_vap + */ +static int rt2860_vap_reset(struct ieee80211vap *vap, u_long cmd) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_vap *rvap; + int error; + + ic = vap->iv_ic; + sc = ic->ic_softc; + rvap = (struct rt2860_softc_vap *) vap; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: VAP reset: cmd=%lu\n", + device_get_nameunit(sc->dev), cmd); + + error = 0; + + RT2860_SOFTC_LOCK(sc); + + switch (cmd) + { + case IEEE80211_IOC_RTSTHRESHOLD: + case IEEE80211_IOC_AMSDU: + rt2860_asic_update_rtsthreshold(sc); + break; + + case IEEE80211_IOC_PROTMODE: + case IEEE80211_IOC_HTPROTMODE: + rt2860_asic_updateprot(sc); + break; + + case IEEE80211_IOC_TXPOWER: + rt2860_asic_update_txpower(sc); + break; + + case IEEE80211_IOC_BURST: + rt2860_asic_updateslot(sc); + break; + + case IEEE80211_IOC_SHORTGI: + case IEEE80211_IOC_AMPDU_DENSITY: + case IEEE80211_IOC_SMPS: + break; + + default: + error = ENETRESET; + break; + } + + RT2860_SOFTC_UNLOCK(sc); + + return error; +} + +/* + * rt2860_vap_newstate + */ +static int rt2860_vap_newstate(struct ieee80211vap *vap, + enum ieee80211_state nstate, int arg) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_vap *rvap; + struct ieee80211_node *ni; + enum ieee80211_state ostate; + int error; + + ic = vap->iv_ic; + sc = ic->ic_softc; + rvap = (struct rt2860_softc_vap *) vap; + + ostate = vap->iv_state; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: VAP newstate: %s -> %s\n", + device_get_nameunit(sc->dev), + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); + + error = rvap->newstate(vap, nstate, arg); + if (error != 0) + return error; + + IEEE80211_UNLOCK(ic); + + RT2860_SOFTC_LOCK(sc); + + /* turn link LED off */ + + if (nstate != IEEE80211_S_RUN) + rt2860_led_cmd(sc, RT2860_LED_CMD_RADIO_OFF); + + switch (nstate) + { + case IEEE80211_S_INIT: + rt2860_asic_disable_tsf_sync(sc); + break; + + case IEEE80211_S_RUN: + ni = vap->iv_bss; + + rt2860_rf_set_chan(sc, ni->ni_chan); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) + { + rt2860_asic_enable_mrr(sc); + rt2860_asic_set_txpreamble(sc); + rt2860_asic_set_basicrates(sc); + rt2860_asic_update_txpower(sc); + rt2860_asic_set_bssid(sc, ni->ni_bssid); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_MBSS) + { + error = rt2860_beacon_alloc(sc, vap); + if (error != 0) + break; + + rt2860_asic_update_beacon(sc, vap); + } + + if (vap->iv_opmode != IEEE80211_M_MONITOR) + rt2860_asic_enable_tsf_sync(sc); + + /* turn link LED on */ + + if (vap->iv_opmode != IEEE80211_M_MONITOR) + { + rt2860_led_cmd(sc, RT2860_LED_CMD_RADIO_ON | + (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) ? + RT2860_LED_CMD_LINK_2GHZ : RT2860_LED_CMD_LINK_5GHZ)); + } + break; + + case IEEE80211_S_SLEEP: + break; + + default: + break; + } + + RT2860_SOFTC_UNLOCK(sc); + + IEEE80211_LOCK(ic); + + return error; +} + +#ifdef RT2860_HW_CRYPTO +/* + * rt2860_vap_key_update_begin + */ +static void rt2860_vap_key_update_begin(struct ieee80211vap *vap) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + + ic = vap->iv_ic; + sc = ic->ic_softc; + + RT2860_DPRINTF(sc, RT2860_DEBUG_KEY, + "%s: VAP key update begin\n", + device_get_nameunit(sc->dev)); + + taskqueue_block(sc->taskqueue); + +// IF_LOCK(&ifp->if_snd); +} + +/* + * rt2860_vap_key_update_end + */ +static void rt2860_vap_key_update_end(struct ieee80211vap *vap) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + + ic = vap->iv_ic; + sc = ic->ic_softc; + + RT2860_DPRINTF(sc, RT2860_DEBUG_KEY, + "%s: VAP key update end\n", + device_get_nameunit(sc->dev)); + +// IF_UNLOCK(&ifp->if_snd); + + taskqueue_unblock(sc->taskqueue); +} + +/* + * rt2860_vap_key_set + */ +static int rt2860_vap_key_set(struct ieee80211vap *vap, + const struct ieee80211_key *k) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct ieee80211_node *ni; + struct rt2860_softc_node *rni; + uint16_t key_base, keymode_base; + uint8_t mode, vapid, wcid, iv[8]; + uint32_t tmp; + + switch (k->wk_cipher->ic_cipher) + { + case IEEE80211_CIPHER_WEP: + if(k->wk_keylen < 8) + mode = RT2860_REG_CIPHER_MODE_WEP40; + else + mode = RT2860_REG_CIPHER_MODE_WEP104; + break; + + case IEEE80211_CIPHER_TKIP: + mode = RT2860_REG_CIPHER_MODE_TKIP; + break; + + case IEEE80211_CIPHER_AES_CCM: + mode = RT2860_REG_CIPHER_MODE_AES_CCMP; + break; + + default: + printf("Wrong key CIPHER (%d)\n", k->wk_cipher->ic_cipher); + return 0; + } + + ic = vap->iv_ic; + sc = ic->ic_softc; + + RT2860_DPRINTF(sc, RT2860_DEBUG_KEY, + "%s: VAP key set: keyix=%d, keylen=%d, macaddr=%s, mode=%d, group=%d\n", + device_get_nameunit(sc->dev), k->wk_keyix, k->wk_keylen, ether_sprintf(k->wk_macaddr), + mode, (k->wk_flags & IEEE80211_KEY_GROUP) ? 1 : 0); + + if (!(k->wk_flags & IEEE80211_KEY_GROUP)) + { + /* install pairwise key */ + + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + ni = ieee80211_find_vap_node(&ic->ic_sta, vap, ic->ic_macaddr); + else + ni = vap->iv_bss; + + rni = (struct rt2860_softc_node *) ni; + + vapid = 0; + wcid = (ni != NULL) ? rni->staid : 0; + key_base = RT2860_REG_PKEY(wcid); + + if (ni != NULL) + ieee80211_free_node(ni); + + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_WEP) + { + memset(iv, 0, 8); + + iv[3] = (k->wk_keyix << 6); + } + else + { + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) + { + iv[0] = (k->wk_keytsc >> 8); + iv[1] = ((iv[0] | 0x20) & 0x7f); + iv[2] = k->wk_keytsc; + } + else + { + /* AES 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); + + RT2860_DPRINTF(sc, RT2860_DEBUG_KEY, + "%s: VAP key set: iv=%02x %02x %02x %02x %02x %02x %02x %02x\n", + device_get_nameunit(sc->dev), + iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]); + } + + rt2860_io_mac_write_multi(sc, RT2860_REG_IVEIV(wcid), iv, 8); + + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) + { + rt2860_io_mac_write_multi(sc, key_base, k->wk_key, 16); + + if (vap->iv_opmode != IEEE80211_M_HOSTAP) + { + rt2860_io_mac_write_multi(sc, key_base + 16, &k->wk_key[16], 8); + rt2860_io_mac_write_multi(sc, key_base + 24, &k->wk_key[24], 8); + } + else + { + rt2860_io_mac_write_multi(sc, key_base + 16, &k->wk_key[24], 8); + rt2860_io_mac_write_multi(sc, key_base + 24, &k->wk_key[16], 8); + } + } + else + { + rt2860_io_mac_write_multi(sc, key_base, k->wk_key, k->wk_keylen); + } + tmp = ((vapid & RT2860_REG_VAP_MASK) << RT2860_REG_VAP_SHIFT) | + (mode << RT2860_REG_CIPHER_MODE_SHIFT) | RT2860_REG_PKEY_ENABLE; + + rt2860_io_mac_write(sc, RT2860_REG_WCID_ATTR(wcid), tmp); + } + + if ((k->wk_flags & IEEE80211_KEY_GROUP) || + (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_WEP)) + { + /* install group key */ + + vapid = 0; + wcid = RT2860_WCID_MCAST; + key_base = RT2860_REG_SKEY(vapid, k->wk_keyix); + keymode_base = RT2860_REG_SKEY_MODE(vapid); + + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) + { + rt2860_io_mac_write_multi(sc, key_base, k->wk_key, 16); + + if (vap->iv_opmode != IEEE80211_M_HOSTAP) + { + rt2860_io_mac_write_multi(sc, key_base + 16, &k->wk_key[16], 8); + rt2860_io_mac_write_multi(sc, key_base + 24, &k->wk_key[24], 8); + } + else + { + rt2860_io_mac_write_multi(sc, key_base + 16, &k->wk_key[24], 8); + rt2860_io_mac_write_multi(sc, key_base + 24, &k->wk_key[16], 8); + } + } + else + { + rt2860_io_mac_write_multi(sc, key_base, k->wk_key, k->wk_keylen); + } + + tmp = rt2860_io_mac_read(sc, keymode_base); + + tmp &= ~(0xf << (k->wk_keyix * 4 + 16 * (vapid % 2))); + tmp |= (mode << (k->wk_keyix * 4 + 16 * (vapid % 2))); + + rt2860_io_mac_write(sc, keymode_base, tmp); + + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + { + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_WEP) + { + memset(iv, 0, 8); + + iv[3] = (k->wk_keyix << 6); + } + else + { + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) + { + iv[0] = (k->wk_keytsc >> 8); + iv[1] = ((iv[0] | 0x20) & 0x7f); + iv[2] = k->wk_keytsc; + } + else + { + /* AES 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); + + RT2860_DPRINTF(sc, RT2860_DEBUG_KEY, + "%s: VAP key set: iv=%02x %02x %02x %02x %02x %02x %02x %02x\n", + device_get_nameunit(sc->dev), + iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]); + } + + rt2860_io_mac_write_multi(sc, RT2860_REG_IVEIV(wcid), iv, 8); + + tmp = ((vapid & RT2860_REG_VAP_MASK) << RT2860_REG_VAP_SHIFT) | + (mode << RT2860_REG_CIPHER_MODE_SHIFT); + + rt2860_io_mac_write(sc, RT2860_REG_WCID_ATTR(wcid), tmp); + } + } + + return 1; +} + +/* + * rt2860_vap_key_delete + */ +static int rt2860_vap_key_delete(struct ieee80211vap *vap, + const struct ieee80211_key *k) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + uint8_t vapid, wcid; + uint32_t tmp; + + ic = vap->iv_ic; + sc = ic->ic_softc; + + RT2860_DPRINTF(sc, RT2860_DEBUG_KEY, + "%s: VAP key delete: keyix=%d, keylen=%d, macaddr=%s, group=%d\n", + device_get_nameunit(sc->dev), k->wk_keyix, k->wk_keylen, ether_sprintf(k->wk_macaddr), + (k->wk_flags & IEEE80211_KEY_GROUP) ? 1 : 0); + + if (k->wk_flags & IEEE80211_KEY_GROUP) + { + /* remove group key */ + + vapid = 0; + wcid = RT2860_WCID_MCAST; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_SKEY_MODE(vapid)); + + tmp &= ~(0xf << (k->wk_keyix * 4 + 16 * (vapid % 2))); + tmp |= (RT2860_REG_CIPHER_MODE_NONE << (k->wk_keyix * 4 + 16 * (vapid % 2))); + + rt2860_io_mac_write(sc, RT2860_REG_SKEY_MODE(vapid), tmp); + + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + { + tmp = ((vapid & RT2860_REG_VAP_MASK) << RT2860_REG_VAP_SHIFT) | + (RT2860_REG_CIPHER_MODE_NONE << RT2860_REG_CIPHER_MODE_SHIFT) | RT2860_REG_PKEY_ENABLE; + + rt2860_io_mac_write(sc, RT2860_REG_WCID_ATTR(wcid), tmp); + } + } + + return 1; +} +#endif + +/* + * rt2860_vap_update_beacon + */ +static void rt2860_vap_update_beacon(struct ieee80211vap *vap, int what) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_vap *rvap; + struct mbuf *m; + struct ieee80211_beacon_offsets *bo; + int error; + + ic = vap->iv_ic; + sc = ic->ic_softc; + rvap = (struct rt2860_softc_vap *) vap; + m = rvap->beacon_mbuf; + bo = &rvap->beacon_offsets; + + RT2860_DPRINTF(sc, RT2860_DEBUG_BEACON, + "%s: VAP update beacon: what=%d\n", + device_get_nameunit(sc->dev), what); + + setbit(bo->bo_flags, what); + + if (m == NULL) + { + error = rt2860_beacon_alloc(sc, vap); + if (error != 0) + return; + + m = rvap->beacon_mbuf; + } + + ieee80211_beacon_update(vap->iv_bss, m, 0); + + rt2860_asic_update_beacon(sc, vap); +} + +/* + * rt2860_node_alloc + */ +static struct ieee80211_node *rt2860_node_alloc(struct ieee80211vap *vap, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + return malloc(sizeof(struct rt2860_softc_node), + M_80211_NODE, M_NOWAIT | M_ZERO); +} + +/* + * rt2860_node_cleanup + */ +static void rt2860_node_cleanup(struct ieee80211_node *ni) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_node *rni; + uint8_t vapid, wcid; + uint32_t tmp; + + ic = ni->ni_ic; + sc = ic->ic_softc; + rni = (struct rt2860_softc_node *) ni; + + RT2860_DPRINTF(sc, RT2860_DEBUG_NODE, + "%s: node cleanup: macaddr=%s, associd=0x%04x, staid=0x%02x\n", + device_get_nameunit(sc->dev), ether_sprintf(ni->ni_macaddr), + ni->ni_associd, rni->staid); + + if (rni->staid != 0) + { + vapid = 0; + wcid = rni->staid; + + tmp = ((vapid & RT2860_REG_VAP_MASK) << RT2860_REG_VAP_SHIFT) | + (RT2860_REG_CIPHER_MODE_NONE << RT2860_REG_CIPHER_MODE_SHIFT) | RT2860_REG_PKEY_ENABLE; + + rt2860_io_mac_write(sc, RT2860_REG_WCID_ATTR(wcid), tmp); + + rt2860_io_mac_write(sc, RT2860_REG_WCID(wcid), 0x00000000); + rt2860_io_mac_write(sc, RT2860_REG_WCID(wcid) + 4, 0x00000000); + + rt2860_staid_delete(sc, rni->staid); + + rni->staid = 0; + } + + sc->node_cleanup(ni); +} + +/* + * rt2860_setregdomain + */ +static int rt2860_setregdomain(struct ieee80211com *ic, + struct ieee80211_regdomain *reg, + int nchans, struct ieee80211_channel chans[]) +{ + struct rt2860_softc *sc; + + sc = ic->ic_softc; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: set regulatory domain: country=%d, country code string=%c%c, location=%c\n", + device_get_nameunit(sc->dev), + reg->country, reg->isocc[0], reg->isocc[1], reg->location); + + return 0; +} + +/* + * rt2860_getradiocaps + */ +static void rt2860_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + *nchans = (ic->ic_nchans >= maxchans) ? maxchans : ic->ic_nchans; + + memcpy(chans, ic->ic_channels, (*nchans) * sizeof(struct ieee80211_channel)); +} + +/* + * rt2860_scan_start + */ +static void rt2860_scan_start(struct ieee80211com *ic) +{ + struct rt2860_softc *sc; + + sc = ic->ic_softc; + + RT2860_SOFTC_LOCK(sc); + + rt2860_asic_disable_tsf_sync(sc); + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_scan_end + */ +static void rt2860_scan_end(struct ieee80211com *ic) +{ + struct rt2860_softc *sc; + + sc = ic->ic_softc; + + RT2860_SOFTC_LOCK(sc); + + rt2860_asic_enable_tsf_sync(sc); + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_set_channel + */ +static void rt2860_set_channel(struct ieee80211com *ic) +{ + struct rt2860_softc *sc; + + sc = ic->ic_softc; + + RT2860_DPRINTF(sc, RT2860_DEBUG_CHAN, + "%s: set channel: channel=%u, HT%s%s\n", + device_get_nameunit(sc->dev), + ieee80211_chan2ieee(ic, ic->ic_curchan), + !IEEE80211_IS_CHAN_HT(ic->ic_curchan) ? " disabled" : + IEEE80211_IS_CHAN_HT20(ic->ic_curchan) ? "20": + IEEE80211_IS_CHAN_HT40U(ic->ic_curchan) ? "40U" : "40D", + (ic->ic_flags & IEEE80211_F_SCAN) ? ", scanning" : ""); + + RT2860_SOFTC_LOCK(sc); + + rt2860_rf_set_chan(sc, ic->ic_curchan); + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_newassoc + */ +static void rt2860_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct ieee80211vap *vap; + struct rt2860_softc_vap *rvap; + struct rt2860_softc_node *rni; + uint16_t aid; + uint8_t wcid; + uint32_t tmp; + + vap = ni->ni_vap; + ic = vap->iv_ic; + sc = ic->ic_softc; + rvap = (struct rt2860_softc_vap *) vap; + rni = (struct rt2860_softc_node *) ni; + + if (isnew) + { + aid = IEEE80211_AID(ni->ni_associd); + rni->staid = rt2860_staid_alloc(sc, aid); + wcid = rni->staid; + + tmp = (ni->ni_macaddr[3] << 24) | + (ni->ni_macaddr[2] << 16) | + (ni->ni_macaddr[1] << 8) | + ni->ni_macaddr[0]; + + rt2860_io_mac_write(sc, RT2860_REG_WCID(wcid), tmp); + + tmp = (ni->ni_macaddr[5] << 8) | + ni->ni_macaddr[4]; + + rt2860_io_mac_write(sc, RT2860_REG_WCID(wcid) + 4, tmp); + + rt2860_amrr_node_init(&rvap->amrr, &sc->amrr_node[wcid], ni); + + RT2860_DPRINTF(sc, RT2860_DEBUG_RATE, + "%s: initial%s node Tx rate: associd=0x%04x, rate=0x%02x, max rate=0x%02x\n", + device_get_nameunit(sc->dev), + (ni->ni_flags & IEEE80211_NODE_HT) ? " HT" : "", + ni->ni_associd, ni->ni_txrate, + (ni->ni_flags & IEEE80211_NODE_HT) ? + (ni->ni_htrates.rs_rates[ni->ni_htrates.rs_nrates - 1] | IEEE80211_RATE_MCS) : + (ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates - 1] & IEEE80211_RATE_VAL)); + + rt2860_asic_updateprot(sc); + rt2860_asic_updateslot(sc); + rt2860_asic_set_txpreamble(sc); + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_NODE, + "%s: new association: isnew=%d, macaddr=%s, associd=0x%04x, staid=0x%02x, QoS %s, ERP %s, HT %s\n", + device_get_nameunit(sc->dev), isnew, ether_sprintf(ni->ni_macaddr), + ni->ni_associd, rni->staid, + (ni->ni_flags & IEEE80211_NODE_QOS) ? "enabled" : "disabled", + (ni->ni_flags & IEEE80211_NODE_ERP) ? "enabled" : "disabled", + (ni->ni_flags & IEEE80211_NODE_HT) ? "enabled" : "disabled"); +} + +/* + * rt2860_updateslot + */ +static void rt2860_updateslot(struct ieee80211com *ic) +{ + struct rt2860_softc *sc; + + sc = ic->ic_softc; + + rt2860_asic_updateslot(sc); +} + +/* + * rt2860_update_promisc + */ +static void rt2860_update_promisc(struct ieee80211com *ic) +{ + struct rt2860_softc *sc; + + sc = ic->ic_softc; + + RT2860_SOFTC_LOCK(sc); + rt2860_asic_update_promisc(sc); + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_wme_update + */ +static int rt2860_wme_update(struct ieee80211com *ic) +{ + struct rt2860_softc *sc; + + sc = ic->ic_softc; + + rt2860_asic_wme_update(sc); + + return 0; +} + +/* + * rt2860_raw_xmit + */ +static int rt2860_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + + ic = ni->ni_ic; + sc = ic->ic_softc; + + if (!(sc->sc_flags & RT2860_RUNNING)) + { + m_freem(m); + ieee80211_free_node(ni); + + return ENETDOWN; + } + + RT2860_SOFTC_TX_RING_LOCK(&sc->tx_ring[sc->tx_ring_mgtqid]); + + if (sc->tx_ring[sc->tx_ring_mgtqid].data_queued >= RT2860_SOFTC_TX_RING_DATA_COUNT) + { + RT2860_SOFTC_TX_RING_UNLOCK(&sc->tx_ring[sc->tx_ring_mgtqid]); + + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: raw xmit: Tx ring with qid=%d is full\n", + device_get_nameunit(sc->dev), sc->tx_ring_mgtqid); + + m_freem(m); + ieee80211_free_node(ni); + + sc->tx_data_queue_full[sc->tx_ring_mgtqid]++; + + return ENOBUFS; + } + + if (rt2860_tx_mgmt(sc, m, ni, sc->tx_ring_mgtqid) != 0) + { + RT2860_SOFTC_TX_RING_UNLOCK(&sc->tx_ring[sc->tx_ring_mgtqid]); + + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + + ieee80211_free_node(ni); + + return EIO; + } + + RT2860_SOFTC_TX_RING_UNLOCK(&sc->tx_ring[sc->tx_ring_mgtqid]); + + sc->tx_timer = RT2860_TX_WATCHDOG_TIMEOUT; + + return 0; +} + +/* + * rt2860_recv_action + */ + +#define LE_READ_2(p) \ + ((uint16_t) \ + ((((const uint8_t *)(p))[0] ) | \ + (((const uint8_t *)(p))[1] << 8))) + +static int rt2860_recv_action(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_node *rni; + const struct ieee80211_action *ia; + uint16_t baparamset; + uint8_t wcid; + int tid; + + ic = ni->ni_ic; + sc = ic->ic_softc; + rni = (struct rt2860_softc_node *) ni; + + ia = (const struct ieee80211_action *) frm; + + if (ia->ia_category == IEEE80211_ACTION_CAT_BA) + { + switch (ia->ia_action) + { + /* IEEE80211_ACTION_BA_DELBA */ + case IEEE80211_ACTION_BA_DELBA: + baparamset = LE_READ_2(frm + 2); + tid = RT2860_MS(baparamset, IEEE80211_BAPS_TID); + wcid = rni->staid; + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: received DELBA request: associd=0x%04x, staid=0x%02x, tid=%d\n", + device_get_nameunit(sc->dev), ni->ni_associd, rni->staid, tid); + + if (rni->staid != 0) { + RT2860_SOFTC_LOCK(sc); + + rt2860_asic_del_ba_session(sc, wcid, tid); + RT2860_SOFTC_UNLOCK(sc); + + } + break; + } + } + + return sc->recv_action(ni, wh, frm, efrm); +} + +/* + * rt2860_send_action + */ +static int rt2860_send_action(struct ieee80211_node *ni, + int cat, int act, void *sa) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_node *rni; + uint16_t *args, status, baparamset; + uint8_t wcid; + int tid, bufsize; + + ic = ni->ni_ic; + sc = ic->ic_softc; + rni = (struct rt2860_softc_node *) ni; + + wcid = rni->staid; + args = sa; + + if (cat == IEEE80211_ACTION_CAT_BA) + { + switch (act) + { + /* IEEE80211_ACTION_BA_ADDBA_RESPONSE */ + case IEEE80211_ACTION_BA_ADDBA_RESPONSE: + status = args[1]; + baparamset = args[2]; + tid = RT2860_MS(baparamset, IEEE80211_BAPS_TID); + bufsize = RT2860_MS(baparamset, IEEE80211_BAPS_BUFSIZ); + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: sending ADDBA response: associd=0x%04x, staid=0x%02x, status=%d, tid=%d, bufsize=%d\n", + device_get_nameunit(sc->dev), ni->ni_associd, rni->staid, status, tid, bufsize); + + if (status == IEEE80211_STATUS_SUCCESS) { + RT2860_SOFTC_LOCK(sc); + + rt2860_asic_add_ba_session(sc, wcid, tid); + RT2860_SOFTC_UNLOCK(sc); + + } + break; + + /* IEEE80211_ACTION_BA_DELBA */ + case IEEE80211_ACTION_BA_DELBA: + baparamset = RT2860_SM(args[0], IEEE80211_DELBAPS_TID) | args[1]; + tid = RT2860_MS(baparamset, IEEE80211_DELBAPS_TID); + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: sending DELBA request: associd=0x%04x, staid=0x%02x, tid=%d\n", + device_get_nameunit(sc->dev), ni->ni_associd, rni->staid, tid); + + if (RT2860_MS(baparamset, IEEE80211_DELBAPS_INIT) != IEEE80211_DELBAPS_INIT) { + RT2860_SOFTC_LOCK(sc); + + rt2860_asic_del_ba_session(sc, wcid, tid); + RT2860_SOFTC_UNLOCK(sc); + + } + break; + } + } + + return sc->send_action(ni, cat, act, sa); +} + +/* + * rt2860_addba_response + */ +static int rt2860_addba_response(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int status, int baparamset, int batimeout) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_node *rni; + ieee80211_seq seqno; + int ret, tid, old_bufsize, new_bufsize; + + ic = ni->ni_ic; + sc = ic->ic_softc; + rni = (struct rt2860_softc_node *) ni; + + tid = RT2860_MS(baparamset, IEEE80211_BAPS_TID); + old_bufsize = RT2860_MS(baparamset, IEEE80211_BAPS_BUFSIZ); + new_bufsize = old_bufsize; + + if (status == IEEE80211_STATUS_SUCCESS) + { + if (sc->mac_rev >= 0x28830300) + { + if (sc->mac_rev >= 0x30700200) + new_bufsize = 13; + else + new_bufsize = 31; + } + else if (sc->mac_rev >= 0x28720200) + { + new_bufsize = 13; + } + else + { + new_bufsize = 7; + } + + if (old_bufsize > new_bufsize) + { + baparamset &= ~IEEE80211_BAPS_BUFSIZ; + baparamset = RT2860_SM(new_bufsize, IEEE80211_BAPS_BUFSIZ); + } + + if (!(tap->txa_flags & IEEE80211_AGGR_RUNNING)) + { + sc->tx_ampdu_sessions++; + + if (sc->tx_ampdu_sessions == 1) { + RT2860_SOFTC_LOCK(sc); + + rt2860_asic_updateprot(sc); + RT2860_SOFTC_UNLOCK(sc); + + } + } + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: received ADDBA response: associd=0x%04x, staid=0x%02x, status=%d, tid=%d, " + "old bufsize=%d, new bufsize=%d\n", + device_get_nameunit(sc->dev), ni->ni_associd, rni->staid, status, tid, + old_bufsize, new_bufsize); + + ret = sc->addba_response(ni, tap, status, baparamset, batimeout); + + if (status == IEEE80211_STATUS_SUCCESS) + { + seqno = ni->ni_txseqs[tid]; + + rt2860_send_bar(ni, tap, seqno); + } + + return ret; +} + +/* + * rt2860_addba_stop + */ +static void rt2860_addba_stop(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_node *rni; + int tid; + + ic = ni->ni_ic; + sc = ic->ic_softc; + rni = (struct rt2860_softc_node *) ni; + +// tid = WME_AC_TO_TID(tap->txa_ac); + tid = WME_AC_TO_TID(tap->txa_tid); + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: stopping A-MPDU Tx: associd=0x%04x, staid=0x%02x, tid=%d\n", + device_get_nameunit(sc->dev), ni->ni_associd, rni->staid, tid); + + if (tap->txa_flags & IEEE80211_AGGR_RUNNING) + { + if (sc->tx_ampdu_sessions > 0) + { + sc->tx_ampdu_sessions--; + + if (sc->tx_ampdu_sessions == 0) { + RT2860_SOFTC_LOCK(sc); + + rt2860_asic_updateprot(sc); + RT2860_SOFTC_UNLOCK(sc); + + } + } + else + { + printf("%s: number of A-MPDU Tx sessions cannot be negative\n", + device_get_nameunit(sc->dev)); + } + } + + sc->addba_stop(ni, tap); +} + +/* + * rt2860_ampdu_rx_start + */ +static int rt2860_ampdu_rx_start(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap, + int baparamset, int batimeout, int baseqctl) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_node *rni; + int tid; + + ic = ni->ni_ic; + sc = ic->ic_softc; + rni = (struct rt2860_softc_node *) ni; + + tid = RT2860_MS(baparamset, IEEE80211_BAPS_TID); + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: starting A-MPDU Rx: associd=0x%04x, staid=0x%02x, tid=%d\n", + device_get_nameunit(sc->dev), ni->ni_associd, rni->staid, tid); + + if (!(rap->rxa_flags & IEEE80211_AGGR_RUNNING)) + sc->rx_ampdu_sessions++; + + return sc->ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl); +} + +/* + * rt2860_ampdu_rx_stop + */ +static void rt2860_ampdu_rx_stop(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct rt2860_softc_node *rni; + + ic = ni->ni_ic; + sc = ic->ic_softc; + rni = (struct rt2860_softc_node *) ni; + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: stopping A-MPDU Rx: associd=0x%04x, staid=0x%02x\n", + device_get_nameunit(sc->dev), ni->ni_associd, rni->staid); + + if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) + { + if (sc->rx_ampdu_sessions > 0) + sc->rx_ampdu_sessions--; + else + printf("%s: number of A-MPDU Rx sessions cannot be negative\n", + device_get_nameunit(sc->dev)); + } + + sc->ampdu_rx_stop(ni, rap); +} + +/* + * rt2860_send_bar + */ +static int rt2860_send_bar(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, ieee80211_seq seqno) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct ieee80211vap *vap; + struct ieee80211_frame_bar *bar; + struct rt2860_softc_node *rni; + struct mbuf *m; + uint16_t barctl, barseqctl; + uint8_t *frm; + int ret, tid; + + ic = ni->ni_ic; + sc = ic->ic_softc; + vap = ni->ni_vap; + rni = (struct rt2860_softc_node *) ni; + + if (!(tap->txa_flags & IEEE80211_AGGR_RUNNING)) + return EINVAL; + + m = ieee80211_getmgtframe(&frm, ic->ic_headroom, sizeof(struct ieee80211_frame_bar)); + if (m == NULL) + return ENOMEM; + + bar = mtod(m, struct ieee80211_frame_bar *); + + bar->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; + bar->i_fc[1] = 0; + + IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr); + IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr); + +// tid = WME_AC_TO_TID(tap->txa_ac); + tid = WME_AC_TO_TID(tap->txa_tid); + + barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? 0 : IEEE80211_BAR_NOACK) | + IEEE80211_BAR_COMP | + RT2860_SM(tid, IEEE80211_BAR_TID); + barseqctl = RT2860_SM(seqno, IEEE80211_BAR_SEQ_START); + + bar->i_ctl = htole16(barctl); + bar->i_seq = htole16(barseqctl); + + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_bar); + + tap->txa_start = seqno; + + ieee80211_ref_node(ni); + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: sending BAR: associd=0x%04x, staid=0x%02x, tid=%d, seqno=%d\n", + device_get_nameunit(sc->dev), ni->ni_associd, rni->staid, tid, seqno); + + ret = ic->ic_raw_xmit(ni, m, NULL); + if (ret != 0) + ieee80211_free_node(ni); + + return ret; +} + +/* + * rt2860_amrr_update_iter_func + */ +static void rt2860_amrr_update_iter_func(void *arg, struct ieee80211_node *ni) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct ieee80211vap *vap; + struct rt2860_softc_vap *rvap; + struct rt2860_softc_node *rni; + uint8_t wcid; + + vap = arg; + ic = vap->iv_ic; + sc = ic->ic_softc; + rvap = (struct rt2860_softc_vap *) vap; + rni = (struct rt2860_softc_node *) ni; + + /* only associated stations */ + + if ((ni->ni_vap == vap) && (rni->staid != 0)) + { + wcid = rni->staid; + + RT2860_DPRINTF(sc, RT2860_DEBUG_RATE, + "%s: AMRR node: staid=0x%02x, txcnt=%d, success=%d, retrycnt=%d\n", + device_get_nameunit(sc->dev), + rni->staid, sc->amrr_node[wcid].txcnt, sc->amrr_node[wcid].success, sc->amrr_node[wcid].retrycnt); + + rt2860_amrr_choose(ni, &sc->amrr_node[wcid]); + + RT2860_DPRINTF(sc, RT2860_DEBUG_RATE, + "%s:%s node Tx rate: associd=0x%04x, staid=0x%02x, rate=0x%02x, max rate=0x%02x\n", + device_get_nameunit(sc->dev), + (ni->ni_flags & IEEE80211_NODE_HT) ? " HT" : "", + ni->ni_associd, rni->staid, ni->ni_txrate, + (ni->ni_flags & IEEE80211_NODE_HT) ? + (ni->ni_htrates.rs_rates[ni->ni_htrates.rs_nrates - 1] | IEEE80211_RATE_MCS) : + (ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates - 1] & IEEE80211_RATE_VAL)); + } +} + +/* + * rt2860_periodic + */ +static void rt2860_periodic(void *arg) +{ + struct rt2860_softc *sc; + + sc = arg; + + RT2860_DPRINTF(sc, RT2860_DEBUG_PERIODIC, + "%s: periodic\n", + device_get_nameunit(sc->dev)); + + taskqueue_enqueue(sc->taskqueue, &sc->periodic_task); +} + +/* + * rt2860_tx_watchdog + */ +static void rt2860_tx_watchdog(void *arg) +{ + struct rt2860_softc *sc; + + sc = arg; + + if (sc->tx_timer == 0) + return; + + if (--sc->tx_timer == 0) + { + printf("%s: Tx watchdog timeout: resetting\n", + device_get_nameunit(sc->dev)); + + rt2860_stop_locked(sc); + rt2860_init_locked(sc); + + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + + sc->tx_watchdog_timeouts++; + } + + callout_reset(&sc->tx_watchdog_ch, hz, rt2860_tx_watchdog, sc); +} + +/* + * rt2860_staid_alloc + */ +static int rt2860_staid_alloc(struct rt2860_softc *sc, int aid) +{ + int staid; + + if ((aid > 0 && aid < RT2860_SOFTC_STAID_COUNT) && isclr(sc->staid_mask, aid)) + { + staid = aid; + } + else + { + for (staid = 1; staid < RT2860_SOFTC_STAID_COUNT; staid++) + { + if (isclr(sc->staid_mask, staid)) + break; + } + } + + setbit(sc->staid_mask, staid); + + return staid; +} + +/* + * rt2860_staid_delete + */ +static void rt2860_staid_delete(struct rt2860_softc *sc, int staid) +{ + clrbit(sc->staid_mask, staid); +} + +/* + * rt2860_asic_set_bssid + */ +static void rt2860_asic_set_bssid(struct rt2860_softc *sc, + const uint8_t *bssid) +{ + uint32_t tmp; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: set bssid: bssid=%s\n", + device_get_nameunit(sc->dev), + ether_sprintf(bssid)); + + tmp = bssid[0] | (bssid[1]) << 8 | (bssid[2] << 16) | (bssid[3] << 24); + + rt2860_io_mac_write(sc, RT2860_REG_BSSID_DW0, tmp); + + tmp = bssid[4] | (bssid[5] << 8); + + rt2860_io_mac_write(sc, RT2860_REG_BSSID_DW1, tmp); +} + +/* + * rt2860_asic_set_macaddr + */ +static void rt2860_asic_set_macaddr(struct rt2860_softc *sc, + const uint8_t *addr) +{ + uint32_t tmp; + + tmp = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24); + + rt2860_io_mac_write(sc, RT2860_REG_ADDR_DW0, tmp); + + tmp = addr[4] | (addr[5] << 8) | (0xff << 16); + + rt2860_io_mac_write(sc, RT2860_REG_ADDR_DW1, tmp); +} + +/* + * rt2860_asic_enable_tsf_sync + */ +static void rt2860_asic_enable_tsf_sync(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211vap *vap; + uint32_t tmp; + + ic = &sc->sc_ic; + vap = TAILQ_FIRST(&ic->ic_vaps); + + RT2860_DPRINTF(sc, RT2860_DEBUG_BEACON, + "%s: enabling TSF\n", + device_get_nameunit(sc->dev)); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_BCN_TIME_CFG); + + tmp &= ~0x1fffff; + tmp |= vap->iv_bss->ni_intval * 16; + tmp |= (RT2860_REG_TSF_TIMER_ENABLE | RT2860_REG_TBTT_TIMER_ENABLE); + + if (vap->iv_opmode == IEEE80211_M_STA) + { + tmp |= (RT2860_REG_TSF_SYNC_MODE_STA << RT2860_REG_TSF_SYNC_MODE_SHIFT); + } + else if (vap->iv_opmode == IEEE80211_M_IBSS) + { + tmp |= RT2860_REG_BCN_TX_ENABLE; + tmp |= (RT2860_REG_TSF_SYNC_MODE_IBSS << RT2860_REG_TSF_SYNC_MODE_SHIFT); + } + else if (vap->iv_opmode == IEEE80211_M_HOSTAP) + { + tmp |= RT2860_REG_BCN_TX_ENABLE; + tmp |= (RT2860_REG_TSF_SYNC_MODE_HOSTAP << RT2860_REG_TSF_SYNC_MODE_SHIFT); + } + + rt2860_io_mac_write(sc, RT2860_REG_BCN_TIME_CFG, tmp); +} + +/* + * rt2860_asic_disable_tsf_sync + */ +static void rt2860_asic_disable_tsf_sync(struct rt2860_softc *sc) +{ + uint32_t tmp; + + RT2860_DPRINTF(sc, RT2860_DEBUG_BEACON, + "%s: disabling TSF\n", + device_get_nameunit(sc->dev)); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_BCN_TIME_CFG); + + tmp &= ~(RT2860_REG_BCN_TX_ENABLE | + RT2860_REG_TSF_TIMER_ENABLE | + RT2860_REG_TBTT_TIMER_ENABLE); + + tmp &= ~(RT2860_REG_TSF_SYNC_MODE_MASK << RT2860_REG_TSF_SYNC_MODE_SHIFT); + tmp |= (RT2860_REG_TSF_SYNC_MODE_DISABLE << RT2860_REG_TSF_SYNC_MODE_SHIFT); + + rt2860_io_mac_write(sc, RT2860_REG_BCN_TIME_CFG, tmp); +} + +/* + * rt2860_asic_enable_mrr + */ +static void rt2860_asic_enable_mrr(struct rt2860_softc *sc) +{ +#define CCK(mcs) (mcs) +#define OFDM(mcs) ((1 << 3) | (mcs)) +#define HT(mcs) (mcs) + + rt2860_io_mac_write(sc, RT2860_REG_TX_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 */ + + rt2860_io_mac_write(sc, RT2860_REG_TX_LG_FBK_CFG1, + (CCK(2) << 12) | /* 11 -> 5.5 */ + (CCK(1) << 8) | /* 5.5 -> 2 */ + (CCK(0) << 4) | /* 2 -> 1 */ + CCK(0)); /* 1 -> 1 */ + + rt2860_io_mac_write(sc, RT2860_REG_TX_HT_FBK_CFG0, + (HT(6) << 28) | + (HT(5) << 24) | + (HT(4) << 20) | + (HT(3) << 16) | + (HT(2) << 12) | + (HT(1) << 8) | + (HT(0) << 4) | + HT(0)); + + rt2860_io_mac_write(sc, RT2860_REG_TX_HT_FBK_CFG1, + (HT(14) << 28) | + (HT(13) << 24) | + (HT(12) << 20) | + (HT(11) << 16) | + (HT(10) << 12) | + (HT(9) << 8) | + (HT(8) << 4) | + HT(7)); + +#undef HT +#undef OFDM +#undef CCK +} + +/* + * rt2860_asic_set_txpreamble + */ +static void rt2860_asic_set_txpreamble(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + uint32_t tmp; + + ic = &sc->sc_ic; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: %s short Tx preamble\n", + device_get_nameunit(sc->dev), + (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "enabling" : "disabling"); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_AUTO_RSP_CFG); + + tmp &= ~RT2860_REG_CCK_SHORT_ENABLE; + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RT2860_REG_CCK_SHORT_ENABLE; + + rt2860_io_mac_write(sc, RT2860_REG_AUTO_RSP_CFG, tmp); +} + +/* + * rt2860_asic_set_basicrates + */ +static void rt2860_asic_set_basicrates(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + + ic = &sc->sc_ic; + + if (ic->ic_curmode == IEEE80211_MODE_11B) + rt2860_io_mac_write(sc, RT2860_REG_LEGACY_BASIC_RATE, 0xf); + else if (ic->ic_curmode == IEEE80211_MODE_11A) + rt2860_io_mac_write(sc, RT2860_REG_LEGACY_BASIC_RATE, 0x150); + else + rt2860_io_mac_write(sc, RT2860_REG_LEGACY_BASIC_RATE, 0x15f); +} + +/* + * rt2860_asic_update_rtsthreshold + */ +static void rt2860_asic_update_rtsthreshold(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211vap *vap; + uint32_t tmp; + uint16_t threshold; + + ic = &sc->sc_ic; + vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap == NULL) + threshold = IEEE80211_RTS_MAX; + else if (vap->iv_flags_ht & IEEE80211_FHT_AMSDU_TX) + threshold = 0x1000; + else + threshold = vap->iv_rtsthreshold; + + RT2860_DPRINTF(sc, RT2860_DEBUG_PROT, + "%s: updating RTS threshold: %d\n", + device_get_nameunit(sc->dev), threshold); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_RTS_CFG); + + tmp &= ~(RT2860_REG_TX_RTS_THRESHOLD_MASK << RT2860_REG_TX_RTS_THRESHOLD_SHIFT); + + tmp |= ((threshold & RT2860_REG_TX_RTS_THRESHOLD_MASK) << + RT2860_REG_TX_RTS_THRESHOLD_SHIFT); + + rt2860_io_mac_write(sc, RT2860_REG_TX_RTS_CFG, tmp); +} + +/* + * rt2860_asic_update_txpower + */ +static void rt2860_asic_update_txpower(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + uint32_t *txpow_rate; + int8_t delta; + uint8_t val; + uint32_t tmp; + int i; + + ic = &sc->sc_ic; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: updating Tx power: %d\n", + device_get_nameunit(sc->dev), ic->ic_txpowlimit); + + if (!IEEE80211_IS_CHAN_HT40(ic->ic_curchan)) + { + txpow_rate = sc->txpow_rate_20mhz; + } + else + { + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) + txpow_rate = sc->txpow_rate_40mhz_2ghz; + else + txpow_rate = sc->txpow_rate_40mhz_5ghz; + } + + delta = 0; + + val = rt2860_io_bbp_read(sc, 1); + val &= 0xfc; + + if (ic->ic_txpowlimit > 90) + { + /* do nothing */ + } + else if (ic->ic_txpowlimit > 60) + { + delta -= 1; + } + else if (ic->ic_txpowlimit > 30) + { + delta -= 3; + } + else if (ic->ic_txpowlimit > 15) + { + val |= 0x1; + } + else if (ic->ic_txpowlimit > 9) + { + val |= 0x1; + delta -= 3; + } + else + { + val |= 0x2; + } + + rt2860_io_bbp_write(sc, 1, val); + + for (i = 0; i < RT2860_SOFTC_TXPOW_RATE_COUNT; i++) + { + if (txpow_rate[i] == 0xffffffff) + continue; + + tmp = rt2860_read_eeprom_txpow_rate_add_delta(txpow_rate[i], delta); + + rt2860_io_mac_write(sc, RT2860_REG_TX_PWR_CFG(i), tmp); + } +} + +/* + * rt2860_asic_update_promisc + */ +static void rt2860_asic_update_promisc(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + uint32_t tmp; + + ic = &sc->sc_ic; + + printf("%s: %s promiscuous mode\n", + device_get_nameunit(sc->dev), + (ic->ic_promisc == 1) ? "entering" : "leaving"); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_RX_FILTER_CFG); + + tmp &= ~RT2860_REG_RX_FILTER_DROP_UC_NOME; + + if (ic->ic_promisc == 0) + tmp |= RT2860_REG_RX_FILTER_DROP_UC_NOME; + + rt2860_io_mac_write(sc, RT2860_REG_RX_FILTER_CFG, tmp); +} + +/* + * rt2860_asic_updateprot + */ +static void rt2860_asic_updateprot(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211vap *vap; + uint32_t cck_prot, ofdm_prot, mm20_prot, mm40_prot, gf20_prot, gf40_prot; + uint8_t htopmode; + enum ieee80211_protmode htprotmode; + + ic = &sc->sc_ic; + vap = TAILQ_FIRST(&ic->ic_vaps); + + /* CCK frame protection */ + + cck_prot = RT2860_REG_RTSTH_ENABLE | RT2860_REG_PROT_NAV_SHORT | + RT2860_REG_TXOP_ALLOW_ALL | RT2860_REG_PROT_CTRL_NONE; + + /* set up protection frame phy mode and rate (MCS code) */ + + if (ic->ic_curmode == IEEE80211_MODE_11A) + cck_prot |= (RT2860_REG_PROT_PHYMODE_OFDM << RT2860_REG_PROT_PHYMODE_SHIFT) | + (0 << RT2860_REG_PROT_MCS_SHIFT); + else + cck_prot |= ((RT2860_REG_PROT_PHYMODE_CCK << RT2860_REG_PROT_PHYMODE_SHIFT) | + (3 << RT2860_REG_PROT_MCS_SHIFT)); + + rt2860_io_mac_write(sc, RT2860_REG_TX_CCK_PROT_CFG, cck_prot); + + /* OFDM frame protection */ + + ofdm_prot = RT2860_REG_RTSTH_ENABLE | RT2860_REG_PROT_NAV_SHORT | + RT2860_REG_TXOP_ALLOW_ALL; + + if (ic->ic_flags & IEEE80211_F_USEPROT) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_PROT, + "%s: updating protection mode: b/g protection mode=%s\n", + device_get_nameunit(sc->dev), + (ic->ic_protmode == IEEE80211_PROT_RTSCTS) ? "RTS/CTS" : + ((ic->ic_protmode == IEEE80211_PROT_CTSONLY) ? "CTS-to-self" : "none")); + + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + ofdm_prot |= RT2860_REG_PROT_CTRL_RTS_CTS; + else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + ofdm_prot |= RT2860_REG_PROT_CTRL_CTS; + else + ofdm_prot |= RT2860_REG_PROT_CTRL_NONE; + } + else + { + RT2860_DPRINTF(sc, RT2860_DEBUG_PROT, + "%s: updating protection mode: b/g protection mode=%s\n", + device_get_nameunit(sc->dev), "none"); + + ofdm_prot |= RT2860_REG_PROT_CTRL_NONE; + } + + rt2860_io_mac_write(sc, RT2860_REG_TX_OFDM_PROT_CFG, ofdm_prot); + + /* HT frame protection */ + + if ((vap != NULL) && (vap->iv_opmode == IEEE80211_M_STA) && (vap->iv_state == IEEE80211_S_RUN)) + htopmode = vap->iv_bss->ni_htopmode; + else + htopmode = ic->ic_curhtprotmode; + + htprotmode = ic->ic_htprotmode; + + /* force HT mixed mode and RTS/CTS protection if A-MPDU Tx aggregation is enabled */ + + if (sc->tx_ampdu_sessions > 0) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_PROT, + "%s: updating protection mode: forcing HT mixed mode and RTS/CTS protection\n", + device_get_nameunit(sc->dev)); + + htopmode = IEEE80211_HTINFO_OPMODE_MIXED; + htprotmode = IEEE80211_PROT_RTSCTS; + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_PROT, + "%s: updating protection mode: HT operation mode=0x%02x, protection mode=%s\n", + device_get_nameunit(sc->dev), + htopmode & IEEE80211_HTINFO_OPMODE, + (htprotmode == IEEE80211_PROT_RTSCTS) ? "RTS/CTS" : + ((htprotmode == IEEE80211_PROT_CTSONLY) ? "CTS-to-self" : "none")); + + switch (htopmode & IEEE80211_HTINFO_OPMODE) + { + /* IEEE80211_HTINFO_OPMODE_HT20PR */ + case IEEE80211_HTINFO_OPMODE_HT20PR: + mm20_prot = RT2860_REG_PROT_NAV_SHORT | RT2860_REG_PROT_CTRL_NONE | + RT2860_REG_TXOP_ALLOW_CCK | RT2860_REG_TXOP_ALLOW_OFDM | + RT2860_REG_TXOP_ALLOW_MM20 | RT2860_REG_TXOP_ALLOW_GF20 | + (RT2860_REG_PROT_PHYMODE_OFDM << RT2860_REG_PROT_PHYMODE_SHIFT) | + (4 << RT2860_REG_PROT_MCS_SHIFT); + + gf20_prot = mm20_prot; + + mm40_prot = RT2860_REG_PROT_NAV_SHORT | RT2860_REG_TXOP_ALLOW_ALL | + (RT2860_REG_PROT_PHYMODE_OFDM << RT2860_REG_PROT_PHYMODE_SHIFT) | + (0x84 << RT2860_REG_PROT_MCS_SHIFT); + + if (htprotmode == IEEE80211_PROT_RTSCTS) + mm40_prot |= RT2860_REG_PROT_CTRL_RTS_CTS; + else if (htprotmode == IEEE80211_PROT_CTSONLY) + mm40_prot |= RT2860_REG_PROT_CTRL_CTS; + else + mm40_prot |= RT2860_REG_PROT_CTRL_NONE; + + gf40_prot = mm40_prot; + break; + + /* IEEE80211_HTINFO_OPMODE_MIXED */ + case IEEE80211_HTINFO_OPMODE_MIXED: + mm20_prot = RT2860_REG_PROT_NAV_SHORT | + RT2860_REG_TXOP_ALLOW_CCK | RT2860_REG_TXOP_ALLOW_OFDM | + RT2860_REG_TXOP_ALLOW_MM20 | RT2860_REG_TXOP_ALLOW_GF20; + + if (ic->ic_flags & IEEE80211_F_USEPROT) + mm20_prot |= (RT2860_REG_PROT_PHYMODE_CCK << RT2860_REG_PROT_PHYMODE_SHIFT) | + (3 << RT2860_REG_PROT_MCS_SHIFT); + else + mm20_prot |= (RT2860_REG_PROT_PHYMODE_OFDM << RT2860_REG_PROT_PHYMODE_SHIFT) | + (4 << RT2860_REG_PROT_MCS_SHIFT); + + if (htprotmode == IEEE80211_PROT_RTSCTS) + mm20_prot |= RT2860_REG_PROT_CTRL_RTS_CTS; + else if (htprotmode == IEEE80211_PROT_CTSONLY) + mm20_prot |= RT2860_REG_PROT_CTRL_CTS; + else + mm20_prot |= RT2860_REG_PROT_CTRL_NONE; + + gf20_prot = mm20_prot; + + mm40_prot = RT2860_REG_PROT_NAV_SHORT | RT2860_REG_TXOP_ALLOW_ALL; + + if (ic->ic_flags & IEEE80211_F_USEPROT) + mm40_prot |= (RT2860_REG_PROT_PHYMODE_CCK << RT2860_REG_PROT_PHYMODE_SHIFT) | + (3 << RT2860_REG_PROT_MCS_SHIFT); + else + mm40_prot |= (RT2860_REG_PROT_PHYMODE_OFDM << RT2860_REG_PROT_PHYMODE_SHIFT) | + (0x84 << RT2860_REG_PROT_MCS_SHIFT); + + if (htprotmode == IEEE80211_PROT_RTSCTS) + mm40_prot |= RT2860_REG_PROT_CTRL_RTS_CTS; + else if (htprotmode == IEEE80211_PROT_CTSONLY) + mm40_prot |= RT2860_REG_PROT_CTRL_CTS; + else + mm40_prot |= RT2860_REG_PROT_CTRL_NONE; + + gf40_prot = mm40_prot; + break; + + /* + * IEEE80211_HTINFO_OPMODE_PURE + * IEEE80211_HTINFO_OPMODE_PROTOPT + */ + case IEEE80211_HTINFO_OPMODE_PURE: + case IEEE80211_HTINFO_OPMODE_PROTOPT: + default: + mm20_prot = RT2860_REG_PROT_NAV_SHORT | RT2860_REG_PROT_CTRL_NONE | + RT2860_REG_TXOP_ALLOW_CCK | RT2860_REG_TXOP_ALLOW_OFDM | + RT2860_REG_TXOP_ALLOW_MM20 | RT2860_REG_TXOP_ALLOW_GF20 | + (RT2860_REG_PROT_PHYMODE_OFDM << RT2860_REG_PROT_PHYMODE_SHIFT) | + (4 << RT2860_REG_PROT_MCS_SHIFT); + + gf20_prot = mm20_prot; + + mm40_prot = RT2860_REG_PROT_NAV_SHORT | RT2860_REG_PROT_CTRL_NONE | + RT2860_REG_TXOP_ALLOW_ALL | + (RT2860_REG_PROT_PHYMODE_OFDM << RT2860_REG_PROT_PHYMODE_SHIFT) | + (0x84 << RT2860_REG_PROT_MCS_SHIFT); + + gf40_prot = mm40_prot; + break; + } + + rt2860_io_mac_write(sc, RT2860_REG_TX_MM20_PROT_CFG, mm20_prot); + rt2860_io_mac_write(sc, RT2860_REG_TX_MM40_PROT_CFG, mm40_prot); + rt2860_io_mac_write(sc, RT2860_REG_TX_GF20_PROT_CFG, gf20_prot); + rt2860_io_mac_write(sc, RT2860_REG_TX_GF40_PROT_CFG, gf40_prot); +} + +/* + * rt2860_asic_updateslot + */ +static void rt2860_asic_updateslot(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211vap *vap; + uint32_t tmp; + + ic = &sc->sc_ic; + vap = TAILQ_FIRST(&ic->ic_vaps); + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATE, + "%s: %s short slot time\n", + device_get_nameunit(sc->dev), + ((ic->ic_flags & IEEE80211_F_SHSLOT) || + ((vap != NULL) && (vap->iv_flags & IEEE80211_F_BURST))) ? "enabling" : "disabling"); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_BKOFF_SLOT_CFG); + + tmp &= ~0xff; + + if ((ic->ic_flags & IEEE80211_F_SHSLOT) || + ((vap != NULL) && (vap->iv_flags & IEEE80211_F_BURST))) + tmp |= IEEE80211_DUR_SHSLOT; + else + tmp |= IEEE80211_DUR_SLOT; + + rt2860_io_mac_write(sc, RT2860_REG_BKOFF_SLOT_CFG, tmp); +} + +/* + * rt2860_asic_wme_update + */ +static void rt2860_asic_wme_update(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211_wme_state *wme; + const struct wmeParams *wmep; + int i; + + ic = &sc->sc_ic; + wme = &ic->ic_wme; + wmep = wme->wme_chanParams.cap_wmeParams; + + RT2860_DPRINTF(sc, RT2860_DEBUG_WME, + "%s: wme update: WME_AC_VO=%d/%d/%d/%d, WME_AC_VI=%d/%d/%d/%d, " + "WME_AC_BK=%d/%d/%d/%d, WME_AC_BE=%d/%d/%d/%d\n", + device_get_nameunit(sc->dev), + wmep[WME_AC_VO].wmep_aifsn, + wmep[WME_AC_VO].wmep_logcwmin, wmep[WME_AC_VO].wmep_logcwmax, + wmep[WME_AC_VO].wmep_txopLimit, + wmep[WME_AC_VI].wmep_aifsn, + wmep[WME_AC_VI].wmep_logcwmin, wmep[WME_AC_VI].wmep_logcwmax, + wmep[WME_AC_VI].wmep_txopLimit, + wmep[WME_AC_BK].wmep_aifsn, + wmep[WME_AC_BK].wmep_logcwmin, wmep[WME_AC_BK].wmep_logcwmax, + wmep[WME_AC_BK].wmep_txopLimit, + wmep[WME_AC_BE].wmep_aifsn, + wmep[WME_AC_BE].wmep_logcwmin, wmep[WME_AC_BE].wmep_logcwmax, + wmep[WME_AC_BE].wmep_txopLimit); + + for (i = 0; i < WME_NUM_AC; i++) + rt2860_io_mac_write(sc, RT2860_REG_TX_EDCA_AC_CFG(i), + (wmep[i].wmep_logcwmax << 16) | (wmep[i].wmep_logcwmin << 12) | + (wmep[i].wmep_aifsn << 8) | wmep[i].wmep_txopLimit); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WMM_AIFSN_CFG, + (wmep[WME_AC_VO].wmep_aifsn << 12) | (wmep[WME_AC_VI].wmep_aifsn << 8) | + (wmep[WME_AC_BK].wmep_aifsn << 4) | wmep[WME_AC_BE].wmep_aifsn); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WMM_CWMIN_CFG, + (wmep[WME_AC_VO].wmep_logcwmin << 12) | (wmep[WME_AC_VI].wmep_logcwmin << 8) | + (wmep[WME_AC_BK].wmep_logcwmin << 4) | wmep[WME_AC_BE].wmep_logcwmin); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WMM_CWMAX_CFG, + (wmep[WME_AC_VO].wmep_logcwmax << 12) | (wmep[WME_AC_VI].wmep_logcwmax << 8) | + (wmep[WME_AC_BK].wmep_logcwmax << 4) | wmep[WME_AC_BE].wmep_logcwmax); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WMM_TXOP0_CFG, + (wmep[WME_AC_BK].wmep_txopLimit << 16) | wmep[WME_AC_BE].wmep_txopLimit); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WMM_TXOP1_CFG, + (wmep[WME_AC_VO].wmep_txopLimit << 16) | wmep[WME_AC_VI].wmep_txopLimit); +} + +/* + * rt2860_asic_update_beacon + */ +static void rt2860_asic_update_beacon(struct rt2860_softc *sc, + struct ieee80211vap *vap) +{ + struct rt2860_softc_vap *rvap; + struct mbuf *m; + struct rt2860_txwi *txwi; + uint32_t tmp; + + rvap = (struct rt2860_softc_vap *) vap; + + m = rvap->beacon_mbuf; + txwi = &rvap->beacon_txwi; + + /* disable temporarily TSF sync */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_BCN_TIME_CFG); + + tmp &= ~(RT2860_REG_BCN_TX_ENABLE | + RT2860_REG_TSF_TIMER_ENABLE | + RT2860_REG_TBTT_TIMER_ENABLE); + + rt2860_io_mac_write(sc, RT2860_REG_BCN_TIME_CFG, tmp); + + /* write Tx wireless info and beacon frame to on-chip memory */ + + rt2860_io_mac_write_multi(sc, RT2860_REG_BEACON_BASE(0), + txwi, sizeof(struct rt2860_txwi)); + + rt2860_io_mac_write_multi(sc, RT2860_REG_BEACON_BASE(0) + sizeof(struct rt2860_txwi), + mtod(m, uint8_t *), m->m_pkthdr.len); + + /* enable again TSF sync */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_BCN_TIME_CFG); + + tmp |= (RT2860_REG_BCN_TX_ENABLE | + RT2860_REG_TSF_TIMER_ENABLE | + RT2860_REG_TBTT_TIMER_ENABLE); + + rt2860_io_mac_write(sc, RT2860_REG_BCN_TIME_CFG, tmp); +} + +/* + * rt2860_asic_clear_keytables + */ +static void rt2860_asic_clear_keytables(struct rt2860_softc *sc) +{ + int i; + + /* clear Rx WCID search table (entries = 256, entry size = 8) */ + + for (i = 0; i < 256; i++) + { + rt2860_io_mac_write(sc, RT2860_REG_WCID(i), 0xffffffff); + rt2860_io_mac_write(sc, RT2860_REG_WCID(i) + 4, 0x0000ffff); + } + + /* clear WCID attribute table (entries = 256, entry size = 4) */ + + rt2860_io_mac_set_region_4(sc, RT2860_REG_WCID_ATTR(0), RT2860_REG_PKEY_ENABLE, 256); + + /* clear IV/EIV table (entries = 256, entry size = 8) */ + + rt2860_io_mac_set_region_4(sc, RT2860_REG_IVEIV(0), 0, 2 * 256); + + /* clear pairwise key table (entries = 64, entry size = 32) */ + + rt2860_io_mac_set_region_4(sc, RT2860_REG_PKEY(0), 0, 8 * 64); + + /* clear shared key table (entries = 32, entry size = 32) */ + + rt2860_io_mac_set_region_4(sc, RT2860_REG_SKEY(0, 0), 0, 8 * 32); + + /* clear shared key mode (entries = 32, entry size = 2) */ + + rt2860_io_mac_set_region_4(sc, RT2860_REG_SKEY_MODE(0), 0, 16); +} + +/* + * rt2860_asic_add_ba_session + */ +static void rt2860_asic_add_ba_session(struct rt2860_softc *sc, + uint8_t wcid, int tid) +{ + uint32_t tmp; + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: adding BA session: wcid=0x%02x, tid=%d\n", + device_get_nameunit(sc->dev), wcid, tid); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_WCID(wcid) + 4); + + tmp |= (0x10000 << tid); + + rt2860_io_mac_write(sc, RT2860_REG_WCID(wcid) + 4, tmp); +} + +/* + * rt2860_asic_del_ba_session + */ +static void rt2860_asic_del_ba_session(struct rt2860_softc *sc, + uint8_t wcid, int tid) +{ + uint32_t tmp; + + RT2860_DPRINTF(sc, RT2860_DEBUG_BA, + "%s: deleting BA session: wcid=0x%02x, tid=%d\n", + device_get_nameunit(sc->dev), wcid, tid); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_WCID(wcid) + 4); + + tmp &= ~(0x10000 << tid); + + rt2860_io_mac_write(sc, RT2860_REG_WCID(wcid) + 4, tmp); +} + +/* + * rt2860_beacon_alloc + */ +static int rt2860_beacon_alloc(struct rt2860_softc *sc, + struct ieee80211vap *vap) +{ + struct ieee80211com *ic; + struct rt2860_softc_vap *rvap; + struct mbuf *m; + struct rt2860_txwi txwi; + uint8_t rate, mcs; + + ic = vap->iv_ic; + rvap = (struct rt2860_softc_vap *) vap; + + m = ieee80211_beacon_alloc(vap->iv_bss); + if (m == NULL) + return ENOMEM; + + rate = IEEE80211_IS_CHAN_5GHZ(vap->iv_bss->ni_chan) ? 12 : 2; + mcs = rt2860_rate2mcs(rate); + + RT2860_DPRINTF(sc, RT2860_DEBUG_BEACON, + "%s: beacon allocate: mcs=0x%02x\n", + device_get_nameunit(sc->dev), mcs); + + memset(&txwi, 0, sizeof(struct rt2860_txwi)); + + txwi.wcid = RT2860_WCID_RESERVED; + txwi.pid_mpdu_len = ((htole16(m->m_pkthdr.len) & RT2860_TXWI_MPDU_LEN_MASK) << + RT2860_TXWI_MPDU_LEN_SHIFT); + txwi.txop = (RT2860_TXWI_TXOP_HT << RT2860_TXWI_TXOP_SHIFT); + txwi.mpdu_density_flags |= + (RT2860_TXWI_FLAGS_TS << RT2860_TXWI_FLAGS_SHIFT); + txwi.bawin_size_xflags |= + (RT2860_TXWI_XFLAGS_NSEQ << RT2860_TXWI_XFLAGS_SHIFT); + + if (rate == 2) + { + txwi.phymode_ifs_stbc_shortgi = + (RT2860_TXWI_PHYMODE_CCK << RT2860_TXWI_PHYMODE_SHIFT); + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_TXWI_MCS_SHOTPRE; + } + else + { + txwi.phymode_ifs_stbc_shortgi = + (RT2860_TXWI_PHYMODE_OFDM << RT2860_TXWI_PHYMODE_SHIFT); + } + + txwi.bw_mcs = (RT2860_TXWI_BW_20 << RT2860_TXWI_BW_SHIFT) | + ((mcs & RT2860_TXWI_MCS_MASK) << RT2860_TXWI_MCS_SHIFT); + + if (rvap->beacon_mbuf != NULL) + { + m_free(rvap->beacon_mbuf); + rvap->beacon_mbuf = NULL; + } + + rvap->beacon_mbuf = m; + rvap->beacon_txwi = txwi; + + return 0; +} + +/* + * rt2860_rxrate + */ +static uint8_t rt2860_rxrate(struct rt2860_rxwi *rxwi) +{ + uint8_t mcs, phymode; + uint8_t rate; + + mcs = (rxwi->bw_mcs >> RT2860_RXWI_MCS_SHIFT) & RT2860_RXWI_MCS_MASK; + phymode = (rxwi->phymode_stbc_shortgi >> RT2860_RXWI_PHYMODE_SHIFT) & + RT2860_RXWI_PHYMODE_MASK; + + rate = 2; + + switch (phymode) { + case RT2860_RXWI_PHYMODE_CCK: + switch (mcs & ~RT2860_RXWI_MCS_SHOTPRE) + { + case 0: rate = 2; break; /* 1 Mbps */ + case 1: rate = 4; break; /* 2 MBps */ + case 2: rate = 11; break; /* 5.5 Mbps */ + case 3: rate = 22; break; /* 11 Mbps */ + } + break; + + case RT2860_RXWI_PHYMODE_OFDM: + switch (mcs) + { + case 0: rate = 12; break; /* 6 Mbps */ + case 1: rate = 18; break; /* 9 Mbps */ + case 2: rate = 24; break; /* 12 Mbps */ + case 3: rate = 36; break; /* 18 Mbps */ + case 4: rate = 48; break; /* 24 Mbps */ + case 5: rate = 72; break; /* 36 Mbps */ + case 6: rate = 96; break; /* 48 Mbps */ + case 7: rate = 108; break; /* 54 Mbps */ + } + break; + + case RT2860_RXWI_PHYMODE_HT_MIXED: + case RT2860_RXWI_PHYMODE_HT_GF: + break; + } + + return rate; +} + +/* + * rt2860_maxrssi_rxpath + */ +static uint8_t rt2860_maxrssi_rxpath(struct rt2860_softc *sc, + const struct rt2860_rxwi *rxwi) +{ + uint8_t rxpath; + + rxpath = 0; + + if (sc->nrxpath > 1) + if (rxwi->rssi[1] > rxwi->rssi[rxpath]) + rxpath = 1; + + if (sc->nrxpath > 2) + if (rxwi->rssi[2] > rxwi->rssi[rxpath]) + rxpath = 2; + + return rxpath; +} + +/* + * rt2860_rssi2dbm + */ +static int8_t rt2860_rssi2dbm(struct rt2860_softc *sc, + uint8_t rssi, uint8_t rxpath) +{ + struct ieee80211com *ic; + struct ieee80211_channel *c; + int chan; + int8_t rssi_off, lna_gain; + + if (rssi == 0) + return -99; + + ic = &sc->sc_ic; + c = ic->ic_curchan; + chan = ieee80211_chan2ieee(ic, c); + + if (IEEE80211_IS_CHAN_5GHZ(c)) + { + rssi_off = sc->rssi_off_5ghz[rxpath]; + + if (chan <= 64) + lna_gain = sc->lna_gain[1]; + else if (chan <= 128) + lna_gain = sc->lna_gain[2]; + else + lna_gain = sc->lna_gain[3]; + } + else + { + rssi_off = sc->rssi_off_2ghz[rxpath] - sc->lna_gain[0]; + lna_gain = sc->lna_gain[0]; + } + + return (-12 - rssi_off - lna_gain - rssi); +} + +/* + * rt2860_rate2mcs + */ +static uint8_t rt2860_rate2mcs(uint8_t rate) +{ + switch (rate) + { + /* CCK rates */ + case 2: return 0; + case 4: return 1; + case 11: return 2; + case 22: return 3; + + /* OFDM rates */ + case 12: return 0; + case 18: return 1; + case 24: return 2; + case 36: return 3; + case 48: return 4; + case 72: return 5; + case 96: return 6; + case 108: return 7; + } + + return 0; +} + +/* + * rt2860_tx_mgmt + */ +static int rt2860_tx_mgmt(struct rt2860_softc *sc, + struct mbuf *m, struct ieee80211_node *ni, int qid) +{ + struct ieee80211com *ic; + struct ieee80211vap *vap; + const struct ieee80211_txparam *tp; + struct rt2860_softc_node *rni; + struct rt2860_softc_tx_ring *ring; + struct rt2860_softc_tx_data *data; + struct rt2860_txdesc *desc; + struct rt2860_txwi *txwi; + struct ieee80211_frame *wh; + struct rt2860_softc_tx_radiotap_header *tap; + bus_dma_segment_t dma_seg[RT2860_SOFTC_MAX_SCATTER]; + struct mbuf *m_d; + u_int hdrsize, hdrspace; + uint8_t rate, mcs, pid, qsel; + uint16_t len, dmalen, mpdu_len, dur; + int error, mimops, ndmasegs, ndescs, i, j; + + KASSERT(qid >= 0 && qid < RT2860_SOFTC_TX_RING_COUNT, + ("%s: Tx MGMT: invalid qid=%d\n", + device_get_nameunit(sc->dev), qid)); + + RT2860_SOFTC_TX_RING_ASSERT_LOCKED(&sc->tx_ring[qid]); + + ic = &sc->sc_ic; + vap = ni->ni_vap; + rni = (struct rt2860_softc_node *) ni; + tp = ni->ni_txparms; + + ring = &sc->tx_ring[qid]; + desc = &ring->desc[ring->desc_cur]; + data = &ring->data[ring->data_cur]; + txwi = (struct rt2860_txwi *) (ring->seg0 + ring->data_cur * RT2860_TX_DATA_SEG0_SIZE); + + wh = mtod(m, struct ieee80211_frame *); + + rate = tp->mgmtrate & IEEE80211_RATE_VAL; +/* XXX */ + if (!rate) + return EFBIG; + + /* fill Tx wireless info */ + + if (ni->ni_flags & IEEE80211_NODE_HT) + mcs = rate; + else + mcs = rt2860_rate2mcs(rate); + + /* calculate MPDU length without padding */ + + hdrsize = ieee80211_anyhdrsize(wh); + hdrspace = ieee80211_anyhdrspace(ic, wh); + mpdu_len = m->m_pkthdr.len - hdrspace + hdrsize; + + memset(txwi, 0, sizeof(struct rt2860_txwi)); + + /* management frames do not need encryption */ + + txwi->wcid = RT2860_WCID_RESERVED; + + /* MIMO power save */ + + if ((ni->ni_flags & IEEE80211_NODE_HT) && (ni->ni_flags & IEEE80211_NODE_MIMO_PS)) + { + if (mcs > 7) + { + if (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) + { + /* dynamic MIMO power save */ + + txwi->mpdu_density_flags |= + (RT2860_TXWI_FLAGS_MIMOPS << RT2860_TXWI_FLAGS_SHIFT); + } + else + { + /* static MIMO power save */ + + mcs = 7; + } + } + + mimops = 1; + } + else + { + mimops = 0; + } + + pid = (mcs < 0xf) ? (mcs + 1) : mcs; + + txwi->pid_mpdu_len = ((htole16(pid) & RT2860_TXWI_PID_MASK) << + RT2860_TXWI_PID_SHIFT) | ((htole16(mpdu_len) & RT2860_TXWI_MPDU_LEN_MASK) << + RT2860_TXWI_MPDU_LEN_SHIFT); + + if (ni->ni_flags & IEEE80211_NODE_HT) + { + txwi->phymode_ifs_stbc_shortgi |= + (RT2860_TXWI_PHYMODE_HT_MIXED << RT2860_TXWI_PHYMODE_SHIFT); + } + else + { + if (ieee80211_rate2phytype(ic->ic_rt, rate) != IEEE80211_T_OFDM) + { + txwi->phymode_ifs_stbc_shortgi |= + (RT2860_TXWI_PHYMODE_CCK << RT2860_TXWI_PHYMODE_SHIFT); + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_TXWI_MCS_SHOTPRE; + } + else + { + txwi->phymode_ifs_stbc_shortgi |= + (RT2860_TXWI_PHYMODE_OFDM << RT2860_TXWI_PHYMODE_SHIFT); + } + } + + txwi->bw_mcs = (RT2860_TXWI_BW_20 << RT2860_TXWI_BW_SHIFT) | + ((mcs & RT2860_TXWI_MCS_MASK) << RT2860_TXWI_MCS_SHIFT); + + txwi->txop = (RT2860_TXWI_TXOP_BACKOFF << RT2860_TXWI_TXOP_SHIFT); + + /* skip ACKs for multicast frames */ + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) + { + txwi->bawin_size_xflags |= + (RT2860_TXWI_XFLAGS_ACK << RT2860_TXWI_XFLAGS_SHIFT); + + if (ni->ni_flags & IEEE80211_NODE_HT) + { + /* preamble + plcp + signal extension + SIFS */ + + dur = 16 + 4 + 6 + 10; + } + else + { + dur = ieee80211_ack_duration(ic->ic_rt, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + } + + *(uint16_t *) wh->i_dur = htole16(dur); + } + + /* ask MAC to insert timestamp into probe responses */ + + if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) + txwi->mpdu_density_flags |= + (RT2860_TXWI_FLAGS_TS << RT2860_TXWI_FLAGS_SHIFT); + + if (ieee80211_radiotap_active_vap(vap)) + { + tap = &sc->txtap; + + tap->flags = IEEE80211_RADIOTAP_F_DATAPAD; + tap->chan_flags = htole32(ic->ic_curchan->ic_flags); + tap->chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->chan_ieee = ic->ic_curchan->ic_ieee; + tap->chan_maxpow = 0; + + if (ni->ni_flags & IEEE80211_NODE_HT) + tap->rate = mcs | IEEE80211_RATE_MCS; + else + tap->rate = rate; + + if (mcs & RT2860_TXWI_MCS_SHOTPRE) + tap->flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) + tap->flags |= IEEE80211_RADIOTAP_F_WEP; + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) + { + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + + ieee80211_radiotap_tx(vap, m); + + wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; + } + else + { + ieee80211_radiotap_tx(vap, m); + } + } + + /* copy and trim 802.11 header */ + + m_copydata(m, 0, hdrsize, (caddr_t) (txwi + 1)); + m_adj(m, hdrspace); + + error = bus_dmamap_load_mbuf_sg(ring->data_dma_tag, data->dma_map, m, + dma_seg, &ndmasegs, BUS_DMA_NOWAIT); + if (error != 0) + { + /* too many fragments, linearize */ + + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: could not load mbuf DMA map, trying to linearize mbuf: ndmasegs=%d, len=%d, error=%d\n", + device_get_nameunit(sc->dev), ndmasegs, m->m_pkthdr.len, error); + + m_d = m_collapse(m, M_NOWAIT, 16); + if (m_d == NULL) { + m_freem(m); + m = NULL; + return (ENOMEM); + } + m = m_d; + + sc->tx_defrag_packets++; + + error = bus_dmamap_load_mbuf_sg(ring->data_dma_tag, data->dma_map, m, + dma_seg, &ndmasegs, BUS_DMA_NOWAIT); + if (error != 0) + { + printf("%s: could not load mbuf DMA map: ndmasegs=%d, len=%d, error=%d\n", + device_get_nameunit(sc->dev), ndmasegs, m->m_pkthdr.len, error); + m_freem(m); + return error; + } + } + + if (m->m_pkthdr.len == 0) + ndmasegs = 0; + + /* determine how many Tx descs are required */ + + ndescs = 1 + ndmasegs / 2; + if ((ring->desc_queued + ndescs) > (RT2860_SOFTC_TX_RING_DESC_COUNT - 2)) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: there are not enough Tx descs\n", + device_get_nameunit(sc->dev)); + + sc->no_tx_desc_avail++; + + bus_dmamap_unload(ring->data_dma_tag, data->dma_map); + m_freem(m); + return EFBIG; + } + + data->m = m; + data->ni = ni; + + /* set up Tx descs */ + + /* first segment is Tx wireless info and 802.11 header */ + + len = sizeof(struct rt2860_txwi) + hdrsize; + + /* align end on a 4-bytes boundary */ + + dmalen = (len + 3) & ~ 3; + + memset((caddr_t) txwi + len, 0, dmalen - len); + + qsel = RT2860_TXDESC_QSEL_EDCA; + + desc->sdp0 = htole32(ring->seg0_phys_addr + ring->data_cur * RT2860_TX_DATA_SEG0_SIZE); + desc->sdl0 = htole16(dmalen); + desc->qsel_flags = (qsel << RT2860_TXDESC_QSEL_SHIFT); + + /* set up payload segments */ + + for (i = ndmasegs, j = 0; i >= 2; i -= 2) + { + desc->sdp1 = htole32(dma_seg[j].ds_addr); + desc->sdl1 = htole16(dma_seg[j].ds_len); + + ring->desc_queued++; + ring->desc_cur = (ring->desc_cur + 1) % RT2860_SOFTC_TX_RING_DESC_COUNT; + + j++; + + desc = &ring->desc[ring->desc_cur]; + + desc->sdp0 = htole32(dma_seg[j].ds_addr); + desc->sdl0 = htole16(dma_seg[j].ds_len); + desc->qsel_flags = (qsel << RT2860_TXDESC_QSEL_SHIFT); + + j++; + } + + /* finalize last payload segment */ + + if (i > 0) + { + desc->sdp1 = htole32(dma_seg[j].ds_addr); + desc->sdl1 = htole16(dma_seg[j].ds_len | RT2860_TXDESC_SDL1_LASTSEG); + } + else + { + desc->sdl0 |= htole16(RT2860_TXDESC_SDL0_LASTSEG); + desc->sdl1 = 0; + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: sending MGMT frame: qid=%d, hdrsize=%d, hdrspace=%d, len=%d, " + "mcs=%d, mimops=%d, DMA len=%d, ndmasegs=%d, DMA ds_len=%d/%d/%d/%d/%d\n", + device_get_nameunit(sc->dev), + qid, hdrsize, hdrspace, m->m_pkthdr.len + hdrsize, + mcs, mimops, dmalen, ndmasegs, + (int) dma_seg[0].ds_len, (int) dma_seg[1].ds_len, (int) dma_seg[2].ds_len, (int) dma_seg[3].ds_len, (int) dma_seg[4].ds_len); + + bus_dmamap_sync(ring->seg0_dma_tag, ring->seg0_dma_map, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->data_dma_tag, data->dma_map, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_PREWRITE); + + ring->desc_queued++; + ring->desc_cur = (ring->desc_cur + 1) % RT2860_SOFTC_TX_RING_DESC_COUNT; + + ring->data_queued++; + ring->data_cur = (ring->data_cur + 1) % RT2860_SOFTC_TX_RING_DATA_COUNT; + + /* kick Tx */ + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_CTX_IDX(qid), ring->desc_cur); + + return 0; +} + +/* + * rt2860_tx_data + */ +static int rt2860_tx_data(struct rt2860_softc *sc, + struct mbuf *m, struct ieee80211_node *ni, int qid) +{ + struct ieee80211com *ic; + struct ieee80211vap *vap; + const struct ieee80211_txparam *tp; + struct rt2860_softc_node *rni; + struct rt2860_softc_tx_ring *ring; + struct rt2860_softc_tx_data *data; + struct rt2860_txdesc *desc; + struct rt2860_txwi *txwi; + struct ieee80211_frame *wh; + struct ieee80211_tx_ampdu *tx_ampdu; + ieee80211_seq seqno; + struct rt2860_softc_tx_radiotap_header *tap; + bus_dma_segment_t dma_seg[RT2860_SOFTC_MAX_SCATTER]; + u_int hdrsize, hdrspace; + uint8_t type, rate, bw, stbc, shortgi, mcs, pid, wcid, mpdu_density, bawin_size, qsel; + uint16_t qos, len, dmalen, mpdu_len, dur; + int error, hasqos, ac, tid, ampdu, mimops, ndmasegs, ndescs, i, j; + + KASSERT(qid >= 0 && qid < RT2860_SOFTC_TX_RING_COUNT, + ("%s: Tx data: invalid qid=%d\n", + device_get_nameunit(sc->dev), qid)); + + RT2860_SOFTC_TX_RING_ASSERT_LOCKED(&sc->tx_ring[qid]); + + ic = &sc->sc_ic; + vap = ni->ni_vap; + rni = (struct rt2860_softc_node *) ni; + tp = ni->ni_txparms; + + ring = &sc->tx_ring[qid]; + desc = &ring->desc[ring->desc_cur]; + data = &ring->data[ring->data_cur]; + txwi = (struct rt2860_txwi *) (ring->seg0 + ring->data_cur * RT2860_TX_DATA_SEG0_SIZE); + + wh = mtod(m, struct ieee80211_frame *); + + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + hasqos = IEEE80211_QOS_HAS_SEQ(wh); + if (hasqos) + { + if (IEEE80211_HAS_ADDR4(wh)) + qos = le16toh(*(const uint16_t *) + (((struct ieee80211_qosframe_addr4 *) wh)->i_qos)); + else + qos = le16toh(*(const uint16_t *) + (((struct ieee80211_qosframe *) wh)->i_qos)); + } + else + { + qos = 0; + } + + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (m->m_flags & M_EAPOL) + rate = tp->mgmtrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else + rate = ni->ni_txrate; + + rate &= IEEE80211_RATE_VAL; +/* XXX */ + if (!rate) + return EFBIG; + + /* fill Tx wireless info */ + + if (ni->ni_flags & IEEE80211_NODE_HT) + mcs = rate; + else + mcs = rt2860_rate2mcs(rate); + + if (type == IEEE80211_FC0_TYPE_DATA) + wcid = !IEEE80211_IS_MULTICAST(wh->i_addr1) ? rni->staid : RT2860_WCID_MCAST; + else + wcid = RT2860_WCID_RESERVED; + + /* calculate MPDU length without padding */ + + hdrsize = ieee80211_anyhdrsize(wh); + hdrspace = ieee80211_anyhdrspace(ic, wh); + mpdu_len = m->m_pkthdr.len - hdrspace + hdrsize; + + memset(txwi, 0, sizeof(struct rt2860_txwi)); + + txwi->wcid = wcid; + + /* MIMO power save */ + + if ((ni->ni_flags & IEEE80211_NODE_HT) && (ni->ni_flags & IEEE80211_NODE_MIMO_PS)) + { + if (mcs > 7) + { + if (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) + { + /* dynamic MIMO power save */ + + txwi->mpdu_density_flags |= + (RT2860_TXWI_FLAGS_MIMOPS << RT2860_TXWI_FLAGS_SHIFT); + } + else + { + /* static MIMO power save */ + + mcs = 7; + } + } + + mimops = 1; + } + else + { + mimops = 0; + } + + pid = (mcs < 0xf) ? (mcs + 1) : mcs; + + txwi->pid_mpdu_len = ((htole16(pid) & RT2860_TXWI_PID_MASK) << + RT2860_TXWI_PID_SHIFT) | ((htole16(mpdu_len) & RT2860_TXWI_MPDU_LEN_MASK) << + RT2860_TXWI_MPDU_LEN_SHIFT); + + stbc = sc->tx_stbc && (mcs <= 7) && (vap->iv_htcaps & IEEE80211_HTCAP_TXSTBC) && + (ni->ni_flags & IEEE80211_NODE_HT) && (ni->ni_htcap & IEEE80211_HTCAP_RXSTBC); + + shortgi = ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) && (ni->ni_flags & IEEE80211_NODE_SGI20) && (ni->ni_chw == 20)) || + ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) && (ni->ni_flags & IEEE80211_NODE_SGI40) && (ni->ni_chw == 40)); + + txwi->phymode_ifs_stbc_shortgi |= + ((stbc & RT2860_TXWI_STBC_MASK) << RT2860_TXWI_STBC_SHIFT) | + ((shortgi & RT2860_TXWI_SHORTGI_MASK) << RT2860_TXWI_SHORTGI_SHIFT); + + if (ni->ni_flags & IEEE80211_NODE_HT) + { + txwi->phymode_ifs_stbc_shortgi |= + (RT2860_TXWI_PHYMODE_HT_MIXED << RT2860_TXWI_PHYMODE_SHIFT); + } + else + { + if (ieee80211_rate2phytype(ic->ic_rt, rate) != IEEE80211_T_OFDM) + { + txwi->phymode_ifs_stbc_shortgi |= + (RT2860_TXWI_PHYMODE_CCK << RT2860_TXWI_PHYMODE_SHIFT); + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_TXWI_MCS_SHOTPRE; + } + else + { + txwi->phymode_ifs_stbc_shortgi |= + (RT2860_TXWI_PHYMODE_OFDM << RT2860_TXWI_PHYMODE_SHIFT); + } + } + + if ((ni->ni_flags & IEEE80211_NODE_HT) && (ni->ni_chw == 40)) + bw = RT2860_TXWI_BW_40; + else + bw = RT2860_TXWI_BW_20; + + txwi->bw_mcs = ((bw & RT2860_TXWI_BW_MASK) << RT2860_TXWI_BW_SHIFT) | + ((mcs & RT2860_TXWI_MCS_MASK) << RT2860_TXWI_MCS_SHIFT); + + txwi->txop = (RT2860_TXWI_TXOP_HT << RT2860_TXWI_TXOP_SHIFT); + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK)) + { + txwi->bawin_size_xflags |= + (RT2860_TXWI_XFLAGS_ACK << RT2860_TXWI_XFLAGS_SHIFT); + + if (ni->ni_flags & IEEE80211_NODE_HT) + { + /* preamble + plcp + signal extension + SIFS */ + + dur = 16 + 4 + 6 + 10; + } + else + { + dur = ieee80211_ack_duration(ic->ic_rt, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + } + + *(uint16_t *) wh->i_dur = htole16(dur); + } + + /* check for A-MPDU */ + + if (m->m_flags & M_AMPDU_MPDU) + { + ac = M_WME_GETAC(m); + tid = WME_AC_TO_TID(ac); + tx_ampdu = &ni->ni_tx_ampdu[ac]; + + mpdu_density = RT2860_MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); + bawin_size = tx_ampdu->txa_wnd; + + txwi->mpdu_density_flags |= + ((mpdu_density & RT2860_TXWI_MPDU_DENSITY_MASK) << RT2860_TXWI_MPDU_DENSITY_SHIFT) | + (RT2860_TXWI_FLAGS_AMPDU << RT2860_TXWI_FLAGS_SHIFT); + + txwi->bawin_size_xflags |= + ((bawin_size & RT2860_TXWI_BAWIN_SIZE_MASK) << RT2860_TXWI_BAWIN_SIZE_SHIFT); + + seqno = ni->ni_txseqs[tid]++; + + *(uint16_t *) &wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + + ampdu = 1; + } + else + { + mpdu_density = 0; + bawin_size = 0; + ampdu = 0; + } + + /* ask MAC to insert timestamp into probe responses */ + + if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) + txwi->mpdu_density_flags |= + (RT2860_TXWI_FLAGS_TS << RT2860_TXWI_FLAGS_SHIFT); + + if (ieee80211_radiotap_active_vap(vap)) + { + tap = &sc->txtap; + + tap->flags = IEEE80211_RADIOTAP_F_DATAPAD; + tap->chan_flags = htole32(ic->ic_curchan->ic_flags); + tap->chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->chan_ieee = ic->ic_curchan->ic_ieee; + tap->chan_maxpow = 0; + + if (ni->ni_flags & IEEE80211_NODE_HT) + tap->rate = mcs | IEEE80211_RATE_MCS; + else + tap->rate = rate; + + if (mcs & RT2860_TXWI_MCS_SHOTPRE) + tap->flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + if (shortgi) + tap->flags |= IEEE80211_RADIOTAP_F_SHORTGI; + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) + tap->flags |= IEEE80211_RADIOTAP_F_WEP; + + /* XXX use temporarily radiotap CFP flag as A-MPDU flag */ + + if (ampdu) + tap->flags |= IEEE80211_RADIOTAP_F_CFP; + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) + { + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + + ieee80211_radiotap_tx(vap, m); + + wh->i_fc[1] |= IEEE80211_FC1_PROTECTED; + } + else + { + ieee80211_radiotap_tx(vap, m); + } + } + + /* copy and trim 802.11 header */ + + m_copydata(m, 0, hdrsize, (caddr_t) (txwi + 1)); + m_adj(m, hdrspace); + + error = bus_dmamap_load_mbuf_sg(ring->data_dma_tag, data->dma_map, m, + dma_seg, &ndmasegs, BUS_DMA_NOWAIT); + if (error != 0) + { + /* too many fragments, linearize */ + + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: could not load mbuf DMA map, trying to linearize mbuf: ndmasegs=%d, len=%d, error=%d\n", + device_get_nameunit(sc->dev), ndmasegs, m->m_pkthdr.len, error); + + m = m_defrag(m, M_NOWAIT); + if (m == NULL) + return ENOMEM; + + sc->tx_defrag_packets++; + + error = bus_dmamap_load_mbuf_sg(ring->data_dma_tag, data->dma_map, m, + dma_seg, &ndmasegs, BUS_DMA_NOWAIT); + if (error != 0) + { + printf("%s: could not load mbuf DMA map: ndmasegs=%d, len=%d, error=%d\n", + device_get_nameunit(sc->dev), ndmasegs, m->m_pkthdr.len, error); + m_freem(m); + return error; + } + } + + if (m->m_pkthdr.len == 0) + ndmasegs = 0; + + /* determine how many Tx descs are required */ + + ndescs = 1 + ndmasegs / 2; + if ((ring->desc_queued + ndescs) > (RT2860_SOFTC_TX_RING_DESC_COUNT - 2)) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: there are not enough Tx descs\n", + device_get_nameunit(sc->dev)); + + sc->no_tx_desc_avail++; + + bus_dmamap_unload(ring->data_dma_tag, data->dma_map); + m_freem(m); + return EFBIG; + } + + data->m = m; + data->ni = ni; + + /* set up Tx descs */ + + /* first segment is Tx wireless info and 802.11 header */ + + len = sizeof(struct rt2860_txwi) + hdrsize; + + /* align end on a 4-bytes boundary */ + + dmalen = (len + 3) & ~ 3; + + memset((caddr_t) txwi + len, 0, dmalen - len); + + qsel = RT2860_TXDESC_QSEL_EDCA; + + desc->sdp0 = htole32(ring->seg0_phys_addr + ring->data_cur * RT2860_TX_DATA_SEG0_SIZE); + desc->sdl0 = htole16(dmalen); + desc->qsel_flags = (qsel << RT2860_TXDESC_QSEL_SHIFT); + + /* set up payload segments */ + + for (i = ndmasegs, j = 0; i >= 2; i -= 2) + { + desc->sdp1 = htole32(dma_seg[j].ds_addr); + desc->sdl1 = htole16(dma_seg[j].ds_len); + + ring->desc_queued++; + ring->desc_cur = (ring->desc_cur + 1) % RT2860_SOFTC_TX_RING_DESC_COUNT; + + j++; + + desc = &ring->desc[ring->desc_cur]; + + desc->sdp0 = htole32(dma_seg[j].ds_addr); + desc->sdl0 = htole16(dma_seg[j].ds_len); + desc->qsel_flags = (qsel << RT2860_TXDESC_QSEL_SHIFT); + + j++; + } + + /* finalize last payload segment */ + + if (i > 0) + { + desc->sdp1 = htole32(dma_seg[j].ds_addr); + desc->sdl1 = htole16(dma_seg[j].ds_len | RT2860_TXDESC_SDL1_LASTSEG); + } + else + { + desc->sdl0 |= htole16(RT2860_TXDESC_SDL0_LASTSEG); + desc->sdl1 = 0; + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: sending data: qid=%d, hdrsize=%d, hdrspace=%d, len=%d, " + "bw=%d, stbc=%d, shortgi=%d, mcs=%d, wcid=0x%02x, " + "ampdu=%d (density=%d, winsize=%d), mimops=%d, DMA len=%d, ndmasegs=%d, DMA ds_len=%d/%d/%d/%d/%d\n", + device_get_nameunit(sc->dev), + qid, hdrsize, hdrspace, m->m_pkthdr.len + hdrsize, + bw, stbc, shortgi, mcs, wcid, ampdu, mpdu_density, bawin_size, mimops, dmalen, ndmasegs, + (int) dma_seg[0].ds_len, (int) dma_seg[1].ds_len, (int) dma_seg[2].ds_len, (int) dma_seg[3].ds_len, (int) dma_seg[4].ds_len); + + bus_dmamap_sync(ring->seg0_dma_tag, ring->seg0_dma_map, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->data_dma_tag, data->dma_map, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_PREWRITE); + + ring->desc_queued++; + ring->desc_cur = (ring->desc_cur + 1) % RT2860_SOFTC_TX_RING_DESC_COUNT; + + ring->data_queued++; + ring->data_cur = (ring->data_cur + 1) % RT2860_SOFTC_TX_RING_DATA_COUNT; + + /* kick Tx */ + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_CTX_IDX(qid), ring->desc_cur); + + return 0; +} + +/* + * rt2860_tx_raw +static int rt2860_tx_raw(struct rt2860_softc *sc, + struct mbuf *m, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: Tx raw\n", + device_get_nameunit(sc->dev)); + + return 0; +} + */ + +/* + * rt2860_intr + */ +static void rt2860_intr(void *arg) +{ + struct rt2860_softc *sc; + uint32_t status; + + sc = arg; + + /* acknowledge interrupts */ + + status = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_INT_STATUS); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_INT_STATUS, status); + + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: interrupt: status = 0x%08x\n", + device_get_nameunit(sc->dev), status); + + if (status == 0xffffffff || /* device likely went away */ + status == 0) /* not for us */ + return; + + sc->interrupts++; + + if (!(sc->sc_flags & RT2860_RUNNING)) + return; + + if (status & RT2860_REG_INT_TX_COHERENT) + rt2860_tx_coherent_intr(sc); + + if (status & RT2860_REG_INT_RX_COHERENT) + rt2860_rx_coherent_intr(sc); + + if (status & RT2860_REG_INT_TXRX_COHERENT) + rt2860_txrx_coherent_intr(sc); + + if (status & RT2860_REG_INT_FIFO_STA_FULL) + rt2860_fifo_sta_full_intr(sc); + + if (status & RT2860_REG_INT_TX_MGMT_DONE) + rt2860_tx_intr(sc, 5); + + if (status & RT2860_REG_INT_RX_DONE) + rt2860_rx_intr(sc); + + if (status & RT2860_REG_INT_RX_DELAY_DONE) + rt2860_rx_delay_intr(sc); + + if (status & RT2860_REG_INT_TX_HCCA_DONE) + rt2860_tx_intr(sc, 4); + + if (status & RT2860_REG_INT_TX_AC3_DONE) + rt2860_tx_intr(sc, 3); + + if (status & RT2860_REG_INT_TX_AC2_DONE) + rt2860_tx_intr(sc, 2); + + if (status & RT2860_REG_INT_TX_AC1_DONE) + rt2860_tx_intr(sc, 1); + + if (status & RT2860_REG_INT_TX_AC0_DONE) + rt2860_tx_intr(sc, 0); + + if (status & RT2860_REG_INT_TX_DELAY_DONE) + rt2860_tx_delay_intr(sc); + + if (status & RT2860_REG_INT_PRE_TBTT) + rt2860_pre_tbtt_intr(sc); + + if (status & RT2860_REG_INT_TBTT) + rt2860_tbtt_intr(sc); + + if (status & RT2860_REG_INT_MCU_CMD) + rt2860_mcu_cmd_intr(sc); + + if (status & RT2860_REG_INT_AUTO_WAKEUP) + rt2860_auto_wakeup_intr(sc); + + if (status & RT2860_REG_INT_GP_TIMER) + rt2860_gp_timer_intr(sc); + +} + +/* + * rt2860_tx_coherent_intr + */ +static void rt2860_tx_coherent_intr(struct rt2860_softc *sc) +{ + uint32_t tmp; + int i; + + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: Tx coherent interrupt\n", + device_get_nameunit(sc->dev)); + + sc->tx_coherent_interrupts++; + + /* restart DMA engine */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG); + + tmp &= ~(RT2860_REG_TX_WB_DDONE | + RT2860_REG_RX_DMA_ENABLE | + RT2860_REG_TX_DMA_ENABLE); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG, tmp); + + /* init Tx rings (4 EDCAs + HCCA + MGMT) */ + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + rt2860_reset_tx_ring(sc, &sc->tx_ring[i]); + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + { + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_BASE_PTR(i), + sc->tx_ring[i].desc_phys_addr); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_MAX_CNT(i), + RT2860_SOFTC_TX_RING_DESC_COUNT); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_CTX_IDX(i), 0); + } + + /* init Rx ring */ + + rt2860_reset_rx_ring(sc, &sc->rx_ring); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_BASE_PTR, + sc->rx_ring.desc_phys_addr); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_MAX_CNT, + RT2860_SOFTC_RX_RING_DATA_COUNT); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_CALC_IDX, + RT2860_SOFTC_RX_RING_DATA_COUNT - 1); + + rt2860_txrx_enable(sc); +} + +/* + * rt2860_rx_coherent_intr + */ +static void rt2860_rx_coherent_intr(struct rt2860_softc *sc) +{ + uint32_t tmp; + int i; + + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: Rx coherent interrupt\n", + device_get_nameunit(sc->dev)); + + sc->rx_coherent_interrupts++; + + /* restart DMA engine */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG); + + tmp &= ~(RT2860_REG_TX_WB_DDONE | + RT2860_REG_RX_DMA_ENABLE | + RT2860_REG_TX_DMA_ENABLE); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG, tmp); + + /* init Tx rings (4 EDCAs + HCCA + MGMT) */ + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + rt2860_reset_tx_ring(sc, &sc->tx_ring[i]); + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + { + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_BASE_PTR(i), + sc->tx_ring[i].desc_phys_addr); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_MAX_CNT(i), + RT2860_SOFTC_TX_RING_DESC_COUNT); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_CTX_IDX(i), 0); + } + + /* init Rx ring */ + + rt2860_reset_rx_ring(sc, &sc->rx_ring); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_BASE_PTR, + sc->rx_ring.desc_phys_addr); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_MAX_CNT, + RT2860_SOFTC_RX_RING_DATA_COUNT); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_CALC_IDX, + RT2860_SOFTC_RX_RING_DATA_COUNT - 1); + + rt2860_txrx_enable(sc); +} + +/* + * rt2860_txrx_coherent_intr + */ +static void rt2860_txrx_coherent_intr(struct rt2860_softc *sc) +{ + uint32_t tmp; + int i; + + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: Tx/Rx coherent interrupt\n", + device_get_nameunit(sc->dev)); + + sc->txrx_coherent_interrupts++; + + /* restart DMA engine */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG); + + tmp &= ~(RT2860_REG_TX_WB_DDONE | + RT2860_REG_RX_DMA_ENABLE | + RT2860_REG_TX_DMA_ENABLE); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG, tmp); + + /* init Tx rings (4 EDCAs + HCCA + MGMT) */ + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + rt2860_reset_tx_ring(sc, &sc->tx_ring[i]); + + for (i = 0; i < RT2860_SOFTC_TX_RING_COUNT; i++) + { + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_BASE_PTR(i), + sc->tx_ring[i].desc_phys_addr); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_MAX_CNT(i), + RT2860_SOFTC_TX_RING_DESC_COUNT); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_TX_CTX_IDX(i), 0); + } + + /* init Rx ring */ + + rt2860_reset_rx_ring(sc, &sc->rx_ring); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_BASE_PTR, + sc->rx_ring.desc_phys_addr); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_MAX_CNT, + RT2860_SOFTC_RX_RING_DATA_COUNT); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_CALC_IDX, + RT2860_SOFTC_RX_RING_DATA_COUNT - 1); + + rt2860_txrx_enable(sc); +} + +/* + * rt2860_fifo_sta_full_intr + */ +static void rt2860_fifo_sta_full_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: FIFO statistic full interrupt\n", + device_get_nameunit(sc->dev)); + + sc->fifo_sta_full_interrupts++; + + RT2860_SOFTC_LOCK(sc); + + if (!(sc->intr_disable_mask & RT2860_REG_INT_FIFO_STA_FULL)) + { + rt2860_intr_disable(sc, RT2860_REG_INT_FIFO_STA_FULL); + + taskqueue_enqueue(sc->taskqueue, &sc->fifo_sta_full_task); + } + + sc->intr_pending_mask |= RT2860_REG_INT_FIFO_STA_FULL; + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_rx_intr + */ +static void rt2860_rx_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: Rx interrupt\n", + device_get_nameunit(sc->dev)); + + sc->rx_interrupts++; + + RT2860_SOFTC_LOCK(sc); + + if (!(sc->intr_disable_mask & RT2860_REG_INT_RX_DONE)) + { + rt2860_intr_disable(sc, RT2860_REG_INT_RX_DONE); + + taskqueue_enqueue(sc->taskqueue, &sc->rx_done_task); + } + + sc->intr_pending_mask |= RT2860_REG_INT_RX_DONE; + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_rx_delay_intr + */ +static void rt2860_rx_delay_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: Rx delay interrupt\n", + device_get_nameunit(sc->dev)); + + sc->rx_delay_interrupts++; +} + +/* + * rt2860_tx_intr + */ +static void rt2860_tx_intr(struct rt2860_softc *sc, int qid) +{ + KASSERT(qid >= 0 && qid < RT2860_SOFTC_TX_RING_COUNT, + ("%s: Tx interrupt: invalid qid=%d\n", + device_get_nameunit(sc->dev), qid)); + + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: Tx interrupt: qid=%d\n", + device_get_nameunit(sc->dev), qid); + + sc->tx_interrupts[qid]++; + + RT2860_SOFTC_LOCK(sc); + + if (!(sc->intr_disable_mask & (RT2860_REG_INT_TX_AC0_DONE << qid))) + { + rt2860_intr_disable(sc, (RT2860_REG_INT_TX_AC0_DONE << qid)); + + taskqueue_enqueue(sc->taskqueue, &sc->tx_done_task); + } + + sc->intr_pending_mask |= (RT2860_REG_INT_TX_AC0_DONE << qid); + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_tx_delay_intr + */ +static void rt2860_tx_delay_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: Tx delay interrupt\n", + device_get_nameunit(sc->dev)); + + sc->tx_delay_interrupts++; +} + +/* + * rt2860_pre_tbtt_intr + */ +static void rt2860_pre_tbtt_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: Pre-TBTT interrupt\n", + device_get_nameunit(sc->dev)); + + sc->pre_tbtt_interrupts++; +} + +/* + * rt2860_tbtt_intr + */ +static void rt2860_tbtt_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: TBTT interrupt\n", + device_get_nameunit(sc->dev)); + + sc->tbtt_interrupts++; +} + +/* + * rt2860_mcu_cmd_intr + */ +static void rt2860_mcu_cmd_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: MCU command interrupt\n", + device_get_nameunit(sc->dev)); + + sc->mcu_cmd_interrupts++; +} + +/* + * rt2860_auto_wakeup_intr + */ +static void rt2860_auto_wakeup_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: auto wakeup interrupt\n", + device_get_nameunit(sc->dev)); + + sc->auto_wakeup_interrupts++; +} + +/* + * rt2860_gp_timer_intr + */ +static void rt2860_gp_timer_intr(struct rt2860_softc *sc) +{ + RT2860_DPRINTF(sc, RT2860_DEBUG_INTR, + "%s: GP timer interrupt\n", + device_get_nameunit(sc->dev)); + + sc->gp_timer_interrupts++; +} + +/* + * rt2860_rx_done_task + */ +static void rt2860_rx_done_task(void *context, int pending) +{ + struct rt2860_softc *sc; + int again; + + sc = context; + + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: Rx done task\n", + device_get_nameunit(sc->dev)); + + if (!(sc->sc_flags & RT2860_RUNNING)) + return; + + sc->intr_pending_mask &= ~RT2860_REG_INT_RX_DONE; + + again = rt2860_rx_eof(sc, sc->rx_process_limit); + + RT2860_SOFTC_LOCK(sc); + + if ((sc->intr_pending_mask & RT2860_REG_INT_RX_DONE) || again) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: Rx done task: scheduling again\n", + device_get_nameunit(sc->dev)); + + taskqueue_enqueue(sc->taskqueue, &sc->rx_done_task); + } + else + { + rt2860_intr_enable(sc, RT2860_REG_INT_RX_DONE); + } + + RT2860_SOFTC_UNLOCK(sc); +} + +/* + * rt2860_tx_done_task + */ +static void rt2860_tx_done_task(void *context, int pending) +{ + struct rt2860_softc *sc; + uint32_t intr_mask; + int i; + + sc = context; + + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: Tx done task\n", + device_get_nameunit(sc->dev)); + + if (!(sc->sc_flags & RT2860_RUNNING)) + return; + + for (i = RT2860_SOFTC_TX_RING_COUNT - 1; i >= 0; i--) + { + if (sc->intr_pending_mask & (RT2860_REG_INT_TX_AC0_DONE << i)) + { + sc->intr_pending_mask &= ~(RT2860_REG_INT_TX_AC0_DONE << i); + + rt2860_tx_eof(sc, &sc->tx_ring[i]); + } + } + + sc->tx_timer = 0; + + intr_mask = (RT2860_REG_INT_TX_MGMT_DONE | + RT2860_REG_INT_TX_HCCA_DONE | + RT2860_REG_INT_TX_AC3_DONE | + RT2860_REG_INT_TX_AC2_DONE | + RT2860_REG_INT_TX_AC1_DONE | + RT2860_REG_INT_TX_AC0_DONE); + + RT2860_SOFTC_LOCK(sc); + + rt2860_intr_enable(sc, ~sc->intr_pending_mask & + (sc->intr_disable_mask & intr_mask)); + + if (sc->intr_pending_mask & intr_mask) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: Tx done task: scheduling again\n", + device_get_nameunit(sc->dev)); + + taskqueue_enqueue(sc->taskqueue, &sc->tx_done_task); + } + + RT2860_SOFTC_UNLOCK(sc); + +// if (!IFQ_IS_EMPTY(&ifp->if_snd)) + rt2860_start(sc); +} + +/* + * rt2860_fifo_sta_full_task + */ +static void rt2860_fifo_sta_full_task(void *context, int pending) +{ + struct rt2860_softc *sc; + + sc = context; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATS, + "%s: FIFO statistic full task\n", + device_get_nameunit(sc->dev)); + + if (!(sc->sc_flags & RT2860_RUNNING)) + return; + + sc->intr_pending_mask &= ~RT2860_REG_INT_FIFO_STA_FULL; + + rt2860_drain_fifo_stats(sc); + + RT2860_SOFTC_LOCK(sc); + + if (sc->intr_pending_mask & RT2860_REG_INT_FIFO_STA_FULL) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_STATS, + "%s: FIFO statistic full task: scheduling again\n", + device_get_nameunit(sc->dev)); + + taskqueue_enqueue(sc->taskqueue, &sc->fifo_sta_full_task); + } + else + { + rt2860_intr_enable(sc, RT2860_REG_INT_FIFO_STA_FULL); + } + + RT2860_SOFTC_UNLOCK(sc); + +// if (!IFQ_IS_EMPTY(&ifp->if_snd)) + rt2860_start(sc); +} + +/* + * rt2860_periodic_task + */ +static void rt2860_periodic_task(void *context, int pending) +{ + struct rt2860_softc *sc; + struct ieee80211com *ic; + struct ieee80211vap *vap; + + sc = context; + ic = &sc->sc_ic; + vap = TAILQ_FIRST(&ic->ic_vaps); + + RT2860_DPRINTF(sc, RT2860_DEBUG_PERIODIC, + "%s: periodic task: round=%lu\n", + device_get_nameunit(sc->dev), sc->periodic_round); + + if (!(sc->sc_flags & RT2860_RUNNING)) + return; + + RT2860_SOFTC_LOCK(sc); + + sc->periodic_round++; + + rt2860_update_stats(sc); + + if ((sc->periodic_round % 10) == 0) + { + rt2860_bbp_tuning(sc); + + rt2860_update_raw_counters(sc); + + rt2860_watchdog(sc); + + if (vap != NULL && vap->iv_opmode != IEEE80211_M_MONITOR && vap->iv_state == IEEE80211_S_RUN) + { + if (vap->iv_opmode == IEEE80211_M_STA) + rt2860_amrr_update_iter_func(vap, vap->iv_bss); + else + ieee80211_iterate_nodes(&ic->ic_sta, rt2860_amrr_update_iter_func, vap); + } + } + + RT2860_SOFTC_UNLOCK(sc); + + callout_reset(&sc->periodic_ch, hz / 10, rt2860_periodic, sc); +} + +/* + * rt2860_rx_eof + */ +static int rt2860_rx_eof(struct rt2860_softc *sc, int limit) +{ + struct ieee80211com *ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct rt2860_softc_node *rni; + struct rt2860_softc_rx_radiotap_header *tap; + struct rt2860_softc_rx_ring *ring; + struct rt2860_rxdesc *desc; + struct rt2860_softc_rx_data *data; + struct rt2860_rxwi *rxwi; + struct mbuf *m, *mnew; + bus_dma_segment_t segs[1]; + bus_dmamap_t dma_map; + uint32_t index, desc_flags; + uint8_t rssi, ant, phymode, bw, shortgi, stbc, mcs, tid, frag; +#ifdef RT2860_HW_CRYPTO + uint8_t cipher_err, keyidx; +#endif + uint16_t seq; + int8_t rssi_dbm; + int error, nsegs, len, ampdu, amsdu, rssi_dbm_rel, nframes, i; + + ic = &sc->sc_ic; + ring = &sc->rx_ring; + + nframes = 0; + + while (limit != 0) + { + index = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_RX_DRX_IDX); + if (ring->cur == index) + break; + + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); +#ifdef XXX_TESTED_AND_WORKED + if (!(desc->sdl0 & htole16(RT2860_RXDESC_SDL0_DDONE))) + break; +#endif + + nframes++; + + mnew = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (mnew == NULL) + { + sc->rx_mbuf_alloc_errors++; + counter_u64_add(ic->ic_ierrors, 1); + goto skip; + } + + mnew->m_len = mnew->m_pkthdr.len = MJUMPAGESIZE; + + error = bus_dmamap_load_mbuf_sg(ring->data_dma_tag, ring->spare_dma_map, + mnew, segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: could not load Rx mbuf DMA map: error=%d, nsegs=%d\n", + device_get_nameunit(sc->dev), error, nsegs); + + m_freem(mnew); + + sc->rx_mbuf_dmamap_errors++; + counter_u64_add(ic->ic_ierrors, 1); + + goto skip; + } + + KASSERT(nsegs == 1, ("%s: too many DMA segments", + device_get_nameunit(sc->dev))); + + bus_dmamap_sync(ring->data_dma_tag, data->dma_map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(ring->data_dma_tag, data->dma_map); + + dma_map = data->dma_map; + data->dma_map = ring->spare_dma_map; + ring->spare_dma_map = dma_map; + + bus_dmamap_sync(ring->data_dma_tag, data->dma_map, + BUS_DMASYNC_PREREAD); + + m = data->m; + + data->m = mnew; + desc->sdp0 = htole32(segs[0].ds_addr); + + desc_flags = le32toh(desc->flags); + + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: Rx frame: rxdesc flags=0x%08x\n", + device_get_nameunit(sc->dev), desc_flags); + + /* get Rx wireless info */ + + rxwi = mtod(m, struct rt2860_rxwi *); + len = (le16toh(rxwi->tid_size) >> RT2860_RXWI_SIZE_SHIFT) & + RT2860_RXWI_SIZE_MASK; + + /* check for L2 padding between IEEE 802.11 frame header and body */ + + if (desc_flags & RT2860_RXDESC_FLAGS_L2PAD) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: L2 padding: len=%d\n", + device_get_nameunit(sc->dev), len); + + len += 2; + } + +// m->m_pkthdr.rcvif = ifp; + m->m_data = (caddr_t) (rxwi + 1); + m->m_pkthdr.len = m->m_len = len; + + /* check for crc errors */ + + if (desc_flags & RT2860_RXDESC_FLAGS_CRC_ERR) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: rxdesc: crc error\n", + device_get_nameunit(sc->dev)); + + counter_u64_add(ic->ic_ierrors, 1); + + if (ic->ic_promisc == 0) + { + m_freem(m); + goto skip; + } + } + + wh = (struct ieee80211_frame *) (rxwi + 1); + + /* check for cipher errors */ +#ifdef RT2860_HW_CRYPTO + if (desc_flags & RT2860_RXDESC_FLAGS_DECRYPTED) + { + cipher_err = ((desc_flags >> RT2860_RXDESC_FLAGS_CIPHER_ERR_SHIFT) & + RT2860_RXDESC_FLAGS_CIPHER_ERR_MASK); + if (cipher_err == RT2860_RXDESC_FLAGS_CIPHER_ERR_NONE) + { + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + + m->m_flags |= M_WEP; + + sc->rx_cipher_no_errors++; + } + else + { + RT2860_DPRINTF(sc, RT2860_DEBUG_RX_CRYPT, + "%s: rxdesc: cipher error=0x%02x keyidx=%d\n", + device_get_nameunit(sc->dev), cipher_err, + (rxwi->udf_bssidx_keyidx >> RT2860_RXWI_KEYIDX_SHIFT) & + RT2860_RXWI_KEYIDX_MASK); + + if (cipher_err == RT2860_RXDESC_FLAGS_CIPHER_ERR_ICV) + sc->rx_cipher_icv_errors++; + else if (cipher_err == RT2860_RXDESC_FLAGS_CIPHER_ERR_MIC) + sc->rx_cipher_mic_errors++; + else if (cipher_err == RT2860_RXDESC_FLAGS_CIPHER_ERR_INVALID_KEY) + sc->rx_cipher_invalid_key_errors++; + + if ((cipher_err == RT2860_RXDESC_FLAGS_CIPHER_ERR_MIC) && + (desc_flags & RT2860_RXDESC_FLAGS_MYBSS)) + { + ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh); + if (ni != NULL) + { + keyidx = (rxwi->udf_bssidx_keyidx >> RT2860_RXWI_KEYIDX_SHIFT) & + RT2860_RXWI_KEYIDX_MASK; + + ieee80211_notify_michael_failure(ni->ni_vap, wh, keyidx); + + ieee80211_free_node(ni); + } + } + + counter_u64_add(ic->ic_ierrors, 1); + + if (ic->ic_promisc == 0) + { + m_free(m); + goto skip; + } + } + } + else + { + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) + { + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: rxdesc: not decrypted but protected flag set\n", + device_get_nameunit(sc->dev)); + + counter_u64_add(ic->ic_ierrors, 1); + + if (ic->ic_promisc == 0) + { + m_free(m); + goto skip; + } + } + } +#endif + /* check for A-MPDU */ + + if (desc_flags & RT2860_RXDESC_FLAGS_BA) + { + m->m_flags |= M_AMPDU; + + sc->rx_ampdu++; + + if (wh->i_fc[1] & IEEE80211_FC1_RETRY) + sc->rx_ampdu_retries++; + + ampdu = 1; + } + else + { + ampdu = 0; + } + + /* check for A-MSDU */ + + if (desc_flags & RT2860_RXDESC_FLAGS_AMSDU) + { + sc->rx_amsdu++; + + amsdu = 1; + } + else + { + amsdu = 0; + } + + ant = rt2860_maxrssi_rxpath(sc, rxwi); + rssi = rxwi->rssi[ant]; + rssi_dbm = rt2860_rssi2dbm(sc, rssi, ant); + phymode = ((rxwi->phymode_stbc_shortgi >> RT2860_RXWI_PHYMODE_SHIFT) & + RT2860_RXWI_PHYMODE_MASK); + bw = ((rxwi->bw_mcs >> RT2860_RXWI_BW_SHIFT) & RT2860_RXWI_BW_MASK); + shortgi = ((rxwi->phymode_stbc_shortgi >> RT2860_RXWI_SHORTGI_SHIFT) & + RT2860_RXWI_SHORTGI_MASK); + stbc = ((rxwi->phymode_stbc_shortgi >> RT2860_RXWI_STBC_SHIFT) & + RT2860_RXWI_STBC_MASK); + mcs = ((rxwi->bw_mcs >> RT2860_RXWI_MCS_SHIFT) & RT2860_RXWI_MCS_MASK); + tid = ((rxwi->tid_size >> RT2860_RXWI_TID_SHIFT) & RT2860_RXWI_TID_MASK); + seq = ((rxwi->seq_frag >> RT2860_RXWI_SEQ_SHIFT) & RT2860_RXWI_SEQ_MASK); + frag = ((rxwi->seq_frag >> RT2860_RXWI_FRAG_SHIFT) & RT2860_RXWI_FRAG_MASK); + + if (ieee80211_radiotap_active(ic)) + { + tap = &sc->rxtap; + + tap->flags = (desc_flags & RT2860_RXDESC_FLAGS_L2PAD) ? IEEE80211_RADIOTAP_F_DATAPAD : 0; + tap->dbm_antsignal = rssi_dbm; + tap->dbm_antnoise = RT2860_NOISE_FLOOR; + tap->antenna = ant; + tap->antsignal = rssi; + tap->chan_flags = htole32(ic->ic_curchan->ic_flags); + tap->chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->chan_ieee = ic->ic_curchan->ic_ieee; + tap->chan_maxpow = 0; + + if (phymode == RT2860_TXWI_PHYMODE_HT_MIXED || phymode == RT2860_TXWI_PHYMODE_HT_GF) + tap->rate = mcs | IEEE80211_RATE_MCS; + else + tap->rate = rt2860_rxrate(rxwi); + + if (desc_flags & RT2860_RXDESC_FLAGS_CRC_ERR) + tap->flags |= IEEE80211_RADIOTAP_F_BADFCS; + + if (desc_flags & RT2860_RXDESC_FLAGS_FRAG) + tap->flags |= IEEE80211_RADIOTAP_F_FRAG; + + if (rxwi->bw_mcs & RT2860_RXWI_MCS_SHOTPRE) + tap->flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + if ((desc_flags & RT2860_RXDESC_FLAGS_DECRYPTED) || + (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) + tap->flags |= IEEE80211_RADIOTAP_F_WEP; + + if (shortgi) + tap->flags |= IEEE80211_RADIOTAP_F_SHORTGI; + + /* XXX use temporarily radiotap CFP flag as A-MPDU flag */ + + if (ampdu) + tap->flags |= IEEE80211_RADIOTAP_F_CFP; + } + + /* + * net80211 assumes that RSSI data are in the range [-127..127] and + * in .5 dBm units relative to the current noise floor + */ + + rssi_dbm_rel = (rssi_dbm - RT2860_NOISE_FLOOR) * 2; + if (rssi_dbm_rel > 127) + rssi_dbm_rel = 127; + + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: received frame: len=%d, phymode=%d, bw=%d, shortgi=%d, stbc=0x%02x, mcs=%d, " + "ant=%d, rssi=%d/%d/%d, snr=%d/%d, wcid=0x%02x, ampdu=%d, amsdu=%d, tid=%d, seq=%d, frag=%d, " + "retry=%d, rssi_dbm=%d, rssi_dbm_rel=%d\n", + device_get_nameunit(sc->dev), + len, phymode, bw, shortgi, stbc, mcs, + ant, rxwi->rssi[0], rxwi->rssi[1], rxwi->rssi[2], + rxwi->snr[0], rxwi->snr[1], + rxwi->wcid, ampdu, amsdu, tid, seq, frag, (wh->i_fc[1] & IEEE80211_FC1_RETRY) ? 1 : 0, + rssi_dbm, rssi_dbm_rel); + + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *) wh); + if (ni != NULL) + { + rni = (struct rt2860_softc_node *) ni; + + for (i = 0; i < RT2860_SOFTC_RSSI_COUNT; i++) + { + rni->last_rssi[i] = rxwi->rssi[i]; + rni->last_rssi_dbm[i] = rt2860_rssi2dbm(sc, rxwi->rssi[i], i); + } + + ieee80211_input(ni, m, rssi_dbm_rel, RT2860_NOISE_FLOOR); + ieee80211_free_node(ni); + } + else + { + ieee80211_input_all(ic, m, rssi_dbm_rel, RT2860_NOISE_FLOOR); + } + +skip: + + desc->sdl0 &= ~htole16(RT2860_RXDESC_SDL0_DDONE); + + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + ring->cur = (ring->cur + 1) % RT2860_SOFTC_RX_RING_DATA_COUNT; + + limit--; + } + + if (ring->cur == 0) + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_CALC_IDX, + RT2860_SOFTC_RX_RING_DATA_COUNT - 1); + else + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_RX_CALC_IDX, + ring->cur - 1); + + RT2860_DPRINTF(sc, RT2860_DEBUG_RX, + "%s: Rx eof: nframes=%d\n", + device_get_nameunit(sc->dev), nframes); + + sc->rx_packets += nframes; + + return (limit == 0); +} + +/* + * rt2860_tx_eof + */ +static void rt2860_tx_eof(struct rt2860_softc *sc, + struct rt2860_softc_tx_ring *ring) +{ + struct rt2860_txdesc *desc; + struct rt2860_softc_tx_data *data; + uint32_t index; + int ndescs, nframes; + + ndescs = 0; + nframes = 0; + + for (;;) + { + index = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_TX_DTX_IDX(ring->qid)); + if (ring->desc_next == index) + break; + + ndescs++; + + rt2860_drain_fifo_stats(sc); + + desc = &ring->desc[ring->desc_next]; + + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + if (desc->sdl0 & htole16(RT2860_TXDESC_SDL0_LASTSEG) || + desc->sdl1 & htole16(RT2860_TXDESC_SDL1_LASTSEG)) + { + nframes++; + + data = &ring->data[ring->data_next]; + + if (data->m->m_flags & M_TXCB) + ieee80211_process_callback(data->ni, data->m, 0); + + bus_dmamap_sync(ring->data_dma_tag, data->dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dma_tag, data->dma_map); + +// m_freem(data->m); + +// ieee80211_free_node(data->ni); + ieee80211_tx_complete(data->ni, data->m, 0); + + data->m = NULL; + data->ni = NULL; + +// ifp->if_opackets++; + + RT2860_SOFTC_TX_RING_LOCK(ring); + + ring->data_queued--; + ring->data_next = (ring->data_next + 1) % RT2860_SOFTC_TX_RING_DATA_COUNT; + + RT2860_SOFTC_TX_RING_UNLOCK(ring); + } + + desc->sdl0 &= ~htole16(RT2860_TXDESC_SDL0_DDONE); + + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + RT2860_SOFTC_TX_RING_LOCK(ring); + + ring->desc_queued--; + ring->desc_next = (ring->desc_next + 1) % RT2860_SOFTC_TX_RING_DESC_COUNT; + + RT2860_SOFTC_TX_RING_UNLOCK(ring); + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_TX, + "%s: Tx eof: qid=%d, ndescs=%d, nframes=%d\n", + device_get_nameunit(sc->dev), ring->qid, ndescs, nframes); +} + +/* + * rt2860_update_stats + */ +static void rt2860_update_stats(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + uint32_t stacnt[3]; + int beacons, noretryok, retryok, failed, underflows, zerolen; + + ic = &sc->sc_ic; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATS, + "%s: update statistic\n", + device_get_nameunit(sc->dev)); + + rt2860_drain_fifo_stats(sc); + + /* read and clear Tx statistic registers */ + + rt2860_io_mac_read_multi(sc, RT2860_REG_TX_STA_CNT0, + stacnt, sizeof(stacnt)); + + stacnt[0] = le32toh(stacnt[0]); + stacnt[1] = le32toh(stacnt[1]); + stacnt[2] = le32toh(stacnt[2]); + + beacons = stacnt[0] >> 16; + noretryok = stacnt[1] & 0xffff; + retryok = stacnt[1] >> 16; + failed = stacnt[0] & 0xffff; + underflows = stacnt[2] >> 16; + zerolen = stacnt[2] & 0xffff; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATS, + "%s: update statistic: beacons=%d, noretryok=%d, retryok=%d, failed=%d, underflows=%d, zerolen=%d\n", + device_get_nameunit(sc->dev), + beacons, noretryok, retryok, failed, underflows, zerolen); + + counter_u64_add(sc->sc_ic.ic_oerrors, failed); + + sc->tx_beacons += beacons; + sc->tx_noretryok += noretryok; + sc->tx_retryok += retryok; + sc->tx_failed += failed; + sc->tx_underflows += underflows; + sc->tx_zerolen += zerolen; +} + +/* + * rt2860_bbp_tuning + */ +static void rt2860_bbp_tuning(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211vap *vap; + struct ieee80211_node *ni; + int chan, group; + int8_t rssi, old, new; + + /* RT2860C does not support BBP tuning */ + +// if (sc->mac_rev == 0x28600100) + if (sc->mac_rev == 0x28600102) + return; + + ic = &sc->sc_ic; + vap = TAILQ_FIRST(&ic->ic_vaps); + + if ((ic->ic_flags & IEEE80211_F_SCAN) || vap == NULL || + vap->iv_opmode != IEEE80211_M_STA || vap->iv_state != IEEE80211_S_RUN) + return; + + ni = vap->iv_bss; + + chan = ieee80211_chan2ieee(ic, ni->ni_chan); + + if (chan <= 14) + group = 0; + else if (chan <= 64) + group = 1; + else if (chan <= 128) + group = 2; + else + group = 3; + + rssi = ieee80211_getrssi(vap); + + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + { + new = 0x2e + sc->lna_gain[group]; + } + else + { + if (!IEEE80211_IS_CHAN_HT40(ni->ni_chan)) + new = 0x32 + sc->lna_gain[group] * 5 / 3; + else + new = 0x3a + sc->lna_gain[group] * 5 / 3; + } + + /* Tune if absolute average RSSI is greater than -80 */ + + if (rssi > 30) + new += 0x10; + + old = rt2860_io_bbp_read(sc, 66); + + if (old != new) + rt2860_io_bbp_write(sc, 66, new); +} + +/* + * rt2860_watchdog + */ +static void rt2860_watchdog(struct rt2860_softc *sc) +{ + uint32_t tmp; + int ntries; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_PBF_TXRXQ_PCNT); + + RT2860_DPRINTF(sc, RT2860_DEBUG_WATCHDOG, + "%s: watchdog: TXRXQ_PCNT=0x%08x\n", + device_get_nameunit(sc->dev), tmp); + + if (((tmp >> RT2860_REG_TX0Q_PCNT_SHIFT) & RT2860_REG_TX0Q_PCNT_MASK) != 0) + { + sc->tx_queue_not_empty[0]++; + + rt2860_io_mac_write(sc, RT2860_REG_PBF_CFG, 0xf40012); + + for (ntries = 0; ntries < 10; ntries++) + { + tmp = rt2860_io_mac_read(sc, RT2860_REG_PBF_TXRXQ_PCNT); + if (((tmp >> RT2860_REG_TX0Q_PCNT_SHIFT) & RT2860_REG_TX0Q_PCNT_MASK) == 0) + break; + + DELAY(1); + } + + rt2860_io_mac_write(sc, RT2860_REG_PBF_CFG, 0xf40006); + } + + if (((tmp >> RT2860_REG_TX1Q_PCNT_SHIFT) & RT2860_REG_TX1Q_PCNT_MASK) != 0) + { + sc->tx_queue_not_empty[1]++; + + rt2860_io_mac_write(sc, RT2860_REG_PBF_CFG, 0xf4000a); + + for (ntries = 0; ntries < 10; ntries++) + { + tmp = rt2860_io_mac_read(sc, RT2860_REG_PBF_TXRXQ_PCNT); + if (((tmp >> RT2860_REG_TX1Q_PCNT_SHIFT) & RT2860_REG_TX1Q_PCNT_MASK) == 0) + break; + + DELAY(1); + } + + rt2860_io_mac_write(sc, RT2860_REG_PBF_CFG, 0xf40006); + } +} + +/* + * rt2860_drain_fifo_stats + */ +static void rt2860_drain_fifo_stats(struct rt2860_softc *sc) +{ + uint32_t stats; + uint8_t wcid, mcs, pid; + int ok, agg, retrycnt; + + /* drain Tx status FIFO (maxsize = 16) */ + + while ((stats = rt2860_io_mac_read(sc, RT2860_REG_TX_STA_FIFO)) & + RT2860_REG_TX_STA_FIFO_VALID) + { + wcid = (stats >> RT2860_REG_TX_STA_FIFO_WCID_SHIFT) & + RT2860_REG_TX_STA_FIFO_WCID_MASK; + + /* if no ACK was requested, no feedback is available */ + + if (!(stats & RT2860_REG_TX_STA_FIFO_ACK_REQ) || wcid == RT2860_WCID_RESERVED) + continue; + + /* update AMRR statistic */ + + ok = (stats & RT2860_REG_TX_STA_FIFO_TX_OK) ? 1 : 0; + agg = (stats & RT2860_REG_TX_STA_FIFO_AGG) ? 1 : 0; + mcs = (stats >> RT2860_REG_TX_STA_FIFO_MCS_SHIFT) & + RT2860_REG_TX_STA_FIFO_MCS_MASK; + pid = (stats >> RT2860_REG_TX_STA_FIFO_PID_SHIFT) & + RT2860_REG_TX_STA_FIFO_PID_MASK; + retrycnt = (mcs < 0xf) ? (pid - mcs - 1) : 0; + + RT2860_DPRINTF(sc, RT2860_DEBUG_STATS, + "%s: FIFO statistic: wcid=0x%02x, ok=%d, agg=%d, mcs=0x%02x, pid=0x%02x, retrycnt=%d\n", + device_get_nameunit(sc->dev), + wcid, ok, agg, mcs, pid, retrycnt); + + rt2860_amrr_tx_complete(&sc->amrr_node[wcid], ok, retrycnt); + + if (!ok) + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + } +} + +/* + * rt2860_update_raw_counters + */ +static void rt2860_update_raw_counters(struct rt2860_softc *sc) +{ + uint32_t tmp; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT); + + sc->tx_nonagg += tmp & 0xffff; + sc->tx_agg += tmp >> 16; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT0); + + sc->tx_ampdu += (tmp & 0xffff) / 1 + (tmp >> 16) / 2; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT1); + + sc->tx_ampdu += (tmp & 0xffff) / 3 + (tmp >> 16) / 4; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT2); + + sc->tx_ampdu += (tmp & 0xffff) / 5 + (tmp >> 16) / 6; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT3); + + sc->tx_ampdu += (tmp & 0xffff) / 7 + (tmp >> 16) / 8; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT4); + + sc->tx_ampdu += (tmp & 0xffff) / 9 + (tmp >> 16) / 10; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT5); + + sc->tx_ampdu += (tmp & 0xffff) / 11 + (tmp >> 16) / 12; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT6); + + sc->tx_ampdu += (tmp & 0xffff) / 13 + (tmp >> 16) / 14; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_AGG_CNT7); + + sc->tx_ampdu += (tmp & 0xffff) / 15 + (tmp >> 16) / 16; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_RX_STA_CNT0); + + sc->rx_crc_errors += tmp & 0xffff; + sc->rx_phy_errors += tmp >> 16; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_RX_STA_CNT1); + + sc->rx_false_ccas += tmp & 0xffff; + sc->rx_plcp_errors += tmp >> 16; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_RX_STA_CNT2); + + sc->rx_dup_packets += tmp & 0xffff; + sc->rx_fifo_overflows += tmp >> 16; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TXRX_MPDU_DEN_CNT); + + sc->tx_mpdu_zero_density += tmp & 0xffff; + sc->rx_mpdu_zero_density += tmp >> 16; +} + +/* + * rt2860_intr_enable + */ +static void rt2860_intr_enable(struct rt2860_softc *sc, uint32_t intr_mask) +{ + uint32_t tmp; + + sc->intr_disable_mask &= ~intr_mask; + + tmp = sc->intr_enable_mask & ~sc->intr_disable_mask; + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_INT_MASK, tmp); +} + +/* + * rt2860_intr_disable + */ +static void rt2860_intr_disable(struct rt2860_softc *sc, uint32_t intr_mask) +{ + uint32_t tmp; + + sc->intr_disable_mask |= intr_mask; + + tmp = sc->intr_enable_mask & ~sc->intr_disable_mask; + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_INT_MASK, tmp); +} + +/* + * rt2860_txrx_enable + */ +static int rt2860_txrx_enable(struct rt2860_softc *sc) +{ + struct ieee80211com *ic; + uint32_t tmp; + int ntries; + + ic = &sc->sc_ic; + + /* enable Tx/Rx DMA engine */ + + rt2860_io_mac_write(sc, RT2860_REG_SYS_CTRL, RT2860_REG_TX_ENABLE); + + for (ntries = 0; ntries < 200; ntries++) + { + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG); + if (!(tmp & (RT2860_REG_TX_DMA_BUSY | RT2860_REG_RX_DMA_BUSY))) + break; + + DELAY(1000); + } + + if (ntries == 200) + { + printf("%s: timeout waiting for DMA engine\n", + device_get_nameunit(sc->dev)); + return -1; + } + + DELAY(50); + + tmp |= RT2860_REG_TX_WB_DDONE | + RT2860_REG_RX_DMA_ENABLE | + RT2860_REG_TX_DMA_ENABLE | + (RT2860_REG_WPDMA_BT_SIZE64 << RT2860_REG_WPDMA_BT_SIZE_SHIFT); + + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_WPDMA_GLO_CFG, tmp); + + /* set Rx filter */ + + tmp = RT2860_REG_RX_FILTER_DROP_CRC_ERR | + RT2860_REG_RX_FILTER_DROP_PHY_ERR; + + if (ic->ic_opmode != IEEE80211_M_MONITOR) + { + tmp |= RT2860_REG_RX_FILTER_DROP_DUPL | + RT2860_REG_RX_FILTER_DROP_CTS | + RT2860_REG_RX_FILTER_DROP_BA | + RT2860_REG_RX_FILTER_DROP_ACK | + RT2860_REG_RX_FILTER_DROP_VER_ERR | + RT2860_REG_RX_FILTER_DROP_CTRL_RSV | + RT2860_REG_RX_FILTER_DROP_CFACK | + RT2860_REG_RX_FILTER_DROP_CFEND; + + if (ic->ic_opmode == IEEE80211_M_STA) + tmp |= RT2860_REG_RX_FILTER_DROP_RTS | + RT2860_REG_RX_FILTER_DROP_PSPOLL; + + if (ic->ic_promisc == 0) + tmp |= RT2860_REG_RX_FILTER_DROP_UC_NOME; + } + + rt2860_io_mac_write(sc, RT2860_REG_RX_FILTER_CFG, tmp); + + rt2860_io_mac_write(sc, RT2860_REG_SYS_CTRL, + RT2860_REG_RX_ENABLE | RT2860_REG_TX_ENABLE); + + return 0; +} + +/* + * rt2860_alloc_rx_ring + */ +static int rt2860_alloc_rx_ring(struct rt2860_softc *sc, + struct rt2860_softc_rx_ring *ring) +{ + struct rt2860_rxdesc *desc; + struct rt2860_softc_rx_data *data; + bus_dma_segment_t segs[1]; + int i, nsegs, error; + + error = bus_dma_tag_create(bus_get_dma_tag(sc->dev), PAGE_SIZE, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + RT2860_SOFTC_RX_RING_DATA_COUNT * sizeof(struct rt2860_rxdesc), 1, + RT2860_SOFTC_RX_RING_DATA_COUNT * sizeof(struct rt2860_rxdesc), + 0, NULL, NULL, &ring->desc_dma_tag); + if (error != 0) + { + printf("%s: could not create Rx desc DMA tag\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + error = bus_dmamem_alloc(ring->desc_dma_tag, (void **) &ring->desc, + BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_dma_map); + if (error != 0) + { + printf("%s: could not allocate Rx desc DMA memory\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + error = bus_dmamap_load(ring->desc_dma_tag, ring->desc_dma_map, + ring->desc, + RT2860_SOFTC_RX_RING_DATA_COUNT * sizeof(struct rt2860_rxdesc), + rt2860_dma_map_addr, &ring->desc_phys_addr, 0); + if (error != 0) + { + printf("%s: could not load Rx desc DMA map\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + error = bus_dma_tag_create(bus_get_dma_tag(sc->dev), PAGE_SIZE, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, + &ring->data_dma_tag); + if (error != 0) + { + printf("%s: could not create Rx data DMA tag\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + for (i = 0; i < RT2860_SOFTC_RX_RING_DATA_COUNT; i++) + { + desc = &ring->desc[i]; + data = &ring->data[i]; + + error = bus_dmamap_create(ring->data_dma_tag, 0, &data->dma_map); + if (error != 0) + { + printf("%s: could not create Rx data DMA map\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (data->m == NULL) + { + printf("%s: could not allocate Rx mbuf\n", + device_get_nameunit(sc->dev)); + error = ENOMEM; + goto fail; + } + + data->m->m_len = data->m->m_pkthdr.len = MJUMPAGESIZE; + + error = bus_dmamap_load_mbuf_sg(ring->data_dma_tag, data->dma_map, + data->m, segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) + { + printf("%s: could not load Rx mbuf DMA map\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + KASSERT(nsegs == 1, ("%s: too many DMA segments", + device_get_nameunit(sc->dev))); + + desc->sdp0 = htole32(segs[0].ds_addr); + desc->sdl0 = htole16(MJUMPAGESIZE); + } + + error = bus_dmamap_create(ring->data_dma_tag, 0, &ring->spare_dma_map); + if (error != 0) + { + printf("%s: could not create Rx spare DMA map\n", + device_get_nameunit(sc->dev)); + goto fail; + } + + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return 0; + +fail: + + rt2860_free_rx_ring(sc, ring); + + return error; +} + +/* + * rt2860_reset_rx_ring + */ +static void rt2860_reset_rx_ring(struct rt2860_softc *sc, + struct rt2860_softc_rx_ring *ring) +{ + struct rt2860_rxdesc *desc; + int i; + + for (i = 0; i < RT2860_SOFTC_RX_RING_DATA_COUNT; i++) { + desc = &ring->desc[i]; + + desc->sdl0 &= ~htole16(RT2860_RXDESC_SDL0_DDONE); + } + + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + ring->cur = 0; +} + +/* + * rt2860_free_rx_ring + */ +static void rt2860_free_rx_ring(struct rt2860_softc *sc, + struct rt2860_softc_rx_ring *ring) +{ + struct rt2860_softc_rx_data *data; + int i; + + if (ring->desc != NULL) { + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->desc_dma_tag, ring->desc_dma_map); + bus_dmamem_free(ring->desc_dma_tag, ring->desc, + ring->desc_dma_map); + } + + if (ring->desc_dma_tag != NULL) + bus_dma_tag_destroy(ring->desc_dma_tag); + + for (i = 0; i < RT2860_SOFTC_RX_RING_DATA_COUNT; i++) { + data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dma_tag, data->dma_map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(ring->data_dma_tag, data->dma_map); + m_freem(data->m); + } + + if (data->dma_map != NULL) + bus_dmamap_destroy(ring->data_dma_tag, data->dma_map); + } + + if (ring->spare_dma_map != NULL) + bus_dmamap_destroy(ring->data_dma_tag, ring->spare_dma_map); + + if (ring->data_dma_tag != NULL) + bus_dma_tag_destroy(ring->data_dma_tag); +} + +/* + * rt2860_alloc_tx_ring + */ +static int rt2860_alloc_tx_ring(struct rt2860_softc *sc, + struct rt2860_softc_tx_ring *ring, int qid) +{ + struct rt2860_softc_tx_data *data; + int error, i, size; + + size = RT2860_SOFTC_TX_RING_DESC_COUNT * sizeof(struct rt2860_txdesc); + mtx_init(&ring->lock, device_get_nameunit(sc->dev), NULL, MTX_DEF); + + error = bus_dma_tag_create(bus_get_dma_tag(sc->dev), PAGE_SIZE, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + size, 1, size, 0, NULL, NULL, &ring->desc_dma_tag); + if (error != 0) { + device_printf(sc->dev, "could not create Tx desc DMA tag\n"); + goto fail; + } + + error = bus_dmamem_alloc(ring->desc_dma_tag, (void **) &ring->desc, + BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_dma_map); + if (error != 0) { + device_printf(sc->dev, + "could not allocate Tx desc DMA memory\n"); + goto fail; + } + + error = bus_dmamap_load(ring->desc_dma_tag, ring->desc_dma_map, + ring->desc, size, rt2860_dma_map_addr, &ring->desc_phys_addr, 0); + if (error != 0) { + device_printf(sc->dev, "could not load Tx desc DMA map\n"); + goto fail; + } + + ring->desc_queued = 0; + ring->desc_cur = 0; + ring->desc_next = 0; + + size = RT2860_SOFTC_TX_RING_DATA_COUNT * RT2860_TX_DATA_SEG0_SIZE; + error = bus_dma_tag_create(bus_get_dma_tag(sc->dev), PAGE_SIZE, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + size, 1, size, 0, NULL, NULL, &ring->seg0_dma_tag); + if (error != 0) { + device_printf(sc->dev, "could not create Tx seg0 DMA tag\n"); + goto fail; + } + + error = bus_dmamem_alloc(ring->seg0_dma_tag, (void **) &ring->seg0, + BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->seg0_dma_map); + if (error != 0) { + device_printf(sc->dev, "could not allocate Tx seg0 DMA memory\n"); + goto fail; + } + + error = bus_dmamap_load(ring->seg0_dma_tag, ring->seg0_dma_map, + ring->seg0, size, rt2860_dma_map_addr, &ring->seg0_phys_addr, 0); + if (error != 0) { + device_printf(sc->dev, "could not load Tx seg0 DMA map\n"); + goto fail; + } + + + error = bus_dma_tag_create(bus_get_dma_tag(sc->dev), PAGE_SIZE, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MJUMPAGESIZE, RT2860_SOFTC_MAX_SCATTER, MJUMPAGESIZE, 0, + NULL, NULL, &ring->data_dma_tag); + if (error != 0) { + device_printf(sc->dev, "could not create Tx data DMA tag\n"); + goto fail; + } + + for (i = 0; i < RT2860_SOFTC_TX_RING_DATA_COUNT; i++){ + data = &ring->data[i]; + + error = bus_dmamap_create(ring->data_dma_tag, 0, + &data->dma_map); + if (error != 0) { + device_printf(sc->dev, "could not create Tx data DMA map\n"); + goto fail; + } + } + + ring->data_queued = 0; + ring->data_cur = 0; + ring->data_next = 0; + + ring->qid = qid; + + return 0; + +fail: + + rt2860_free_tx_ring(sc, ring); + + return error; +} + +/* + * rt2860_reset_tx_ring + */ +static void rt2860_reset_tx_ring(struct rt2860_softc *sc, + struct rt2860_softc_tx_ring *ring) +{ + struct rt2860_softc_tx_data *data; + struct rt2860_txdesc *desc; + int i; + + for (i = 0; i < RT2860_SOFTC_TX_RING_DESC_COUNT; i++) + { + desc = &ring->desc[i]; + + desc->sdl0 = 0; + desc->sdl1 = 0; + } + + ring->desc_queued = 0; + ring->desc_cur = 0; + ring->desc_next = 0; + + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_PREWRITE); + + bus_dmamap_sync(ring->seg0_dma_tag, ring->seg0_dma_map, + BUS_DMASYNC_PREWRITE); + + for (i = 0; i < RT2860_SOFTC_TX_RING_DATA_COUNT; i++) + { + data = &ring->data[i]; + + if (data->m != NULL) + { + bus_dmamap_sync(ring->data_dma_tag, data->dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dma_tag, data->dma_map); + m_freem(data->m); + data->m = NULL; + } + + if (data->ni != NULL) + { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } + + ring->data_queued = 0; + ring->data_cur = 0; + ring->data_next = 0; +} + +/* + * rt2860_free_tx_ring + */ +static void rt2860_free_tx_ring(struct rt2860_softc *sc, + struct rt2860_softc_tx_ring *ring) +{ + struct rt2860_softc_tx_data *data; + int i; + + if (ring->desc != NULL) + { + bus_dmamap_sync(ring->desc_dma_tag, ring->desc_dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->desc_dma_tag, ring->desc_dma_map); + bus_dmamem_free(ring->desc_dma_tag, ring->desc, + ring->desc_dma_map); + } + + if (ring->desc_dma_tag != NULL) + bus_dma_tag_destroy(ring->desc_dma_tag); + + if (ring->seg0 != NULL) + { + bus_dmamap_sync(ring->seg0_dma_tag, ring->seg0_dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->seg0_dma_tag, ring->seg0_dma_map); + bus_dmamem_free(ring->seg0_dma_tag, ring->seg0, + ring->seg0_dma_map); + } + + if (ring->seg0_dma_tag != NULL) + bus_dma_tag_destroy(ring->seg0_dma_tag); + + for (i = 0; i < RT2860_SOFTC_TX_RING_DATA_COUNT; i++) + { + data = &ring->data[i]; + + if (data->m != NULL) + { + bus_dmamap_sync(ring->data_dma_tag, data->dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dma_tag, data->dma_map); + m_freem(data->m); + } + + if (data->ni != NULL) + ieee80211_free_node(data->ni); + + if (data->dma_map != NULL) + bus_dmamap_destroy(ring->data_dma_tag, data->dma_map); + } + + if (ring->data_dma_tag != NULL) + bus_dma_tag_destroy(ring->data_dma_tag); + + mtx_destroy(&ring->lock); +} + +/* + * rt2860_dma_map_addr + */ +static void rt2860_dma_map_addr(void *arg, bus_dma_segment_t *segs, + int nseg, int error) +{ + if (error != 0) + return; + + KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); + + *(bus_addr_t *) arg = segs[0].ds_addr; +} + +/* + * rt2860_sysctl_attach + */ +static void rt2860_sysctl_attach(struct rt2860_softc *sc) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + struct sysctl_oid *stats; + + ctx = device_get_sysctl_ctx(sc->dev); + tree = device_get_sysctl_tree(sc->dev); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "tx_stbc", CTLFLAG_RW, &sc->tx_stbc, 0, + "Tx STBC"); + + /* statistic counters */ + + stats = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "stats", CTLFLAG_RD, 0, "statistic"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "interrupts", CTLFLAG_RD, &sc->interrupts, 0, + "all interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_coherent_interrupts", CTLFLAG_RD, &sc->tx_coherent_interrupts, 0, + "Tx coherent interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_coherent_interrupts", CTLFLAG_RD, &sc->rx_coherent_interrupts, 0, + "Rx coherent interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "txrx_coherent_interrupts", CTLFLAG_RD, &sc->txrx_coherent_interrupts, 0, + "Tx/Rx coherent interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "fifo_sta_full_interrupts", CTLFLAG_RD, &sc->fifo_sta_full_interrupts, 0, + "FIFO statistic full interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_interrupts", CTLFLAG_RD, &sc->rx_interrupts, 0, + "Rx interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_delay_interrupts", CTLFLAG_RD, &sc->rx_delay_interrupts, 0, + "Rx delay interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_mgmt_interrupts", CTLFLAG_RD, &sc->tx_interrupts[5], 0, + "Tx MGMT interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_hcca_interrupts", CTLFLAG_RD, &sc->tx_interrupts[4], 0, + "Tx HCCA interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac3_interrupts", CTLFLAG_RD, &sc->tx_interrupts[3], 0, + "Tx AC3 interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac2_interrupts", CTLFLAG_RD, &sc->tx_interrupts[2], 0, + "Tx AC2 interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac1_interrupts", CTLFLAG_RD, &sc->tx_interrupts[1], 0, + "Tx AC1 interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac0_interrupts", CTLFLAG_RD, &sc->tx_interrupts[0], 0, + "Tx AC0 interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_delay_interrupts", CTLFLAG_RD, &sc->tx_delay_interrupts, 0, + "Tx delay interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "pre_tbtt_interrupts", CTLFLAG_RD, &sc->pre_tbtt_interrupts, 0, + "Pre-TBTT interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tbtt_interrupts", CTLFLAG_RD, &sc->tbtt_interrupts, 0, + "TBTT interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "mcu_cmd_interrupts", CTLFLAG_RD, &sc->mcu_cmd_interrupts, 0, + "MCU command interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "auto_wakeup_interrupts", CTLFLAG_RD, &sc->auto_wakeup_interrupts, 0, + "auto wakeup interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "gp_timer_interrupts", CTLFLAG_RD, &sc->gp_timer_interrupts, 0, + "GP timer interrupts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_mgmt_desc_queued", CTLFLAG_RD, &sc->tx_ring[5].desc_queued, 0, + "Tx MGMT descriptors queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_mgmt_data_queued", CTLFLAG_RD, &sc->tx_ring[5].data_queued, 0, + "Tx MGMT data queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_hcca_desc_queued", CTLFLAG_RD, &sc->tx_ring[4].desc_queued, 0, + "Tx HCCA descriptors queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_hcca_data_queued", CTLFLAG_RD, &sc->tx_ring[4].data_queued, 0, + "Tx HCCA data queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac3_desc_queued", CTLFLAG_RD, &sc->tx_ring[3].desc_queued, 0, + "Tx AC3 descriptors queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac3_data_queued", CTLFLAG_RD, &sc->tx_ring[3].data_queued, 0, + "Tx AC3 data queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac2_desc_queued", CTLFLAG_RD, &sc->tx_ring[2].desc_queued, 0, + "Tx AC2 descriptors queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac2_data_queued", CTLFLAG_RD, &sc->tx_ring[2].data_queued, 0, + "Tx AC2 data queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac1_desc_queued", CTLFLAG_RD, &sc->tx_ring[1].desc_queued, 0, + "Tx AC1 descriptors queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac1_data_queued", CTLFLAG_RD, &sc->tx_ring[1].data_queued, 0, + "Tx AC1 data queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac0_desc_queued", CTLFLAG_RD, &sc->tx_ring[0].desc_queued, 0, + "Tx AC0 descriptors queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac0_data_queued", CTLFLAG_RD, &sc->tx_ring[0].data_queued, 0, + "Tx AC0 data queued"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_mgmt_data_queue_full", CTLFLAG_RD, &sc->tx_data_queue_full[5], 0, + "Tx MGMT data queue full"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_hcca_data_queue_full", CTLFLAG_RD, &sc->tx_data_queue_full[4], 0, + "Tx HCCA data queue full"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac3_data_queue_full", CTLFLAG_RD, &sc->tx_data_queue_full[3], 0, + "Tx AC3 data queue full"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac2_data_queue_full", CTLFLAG_RD, &sc->tx_data_queue_full[2], 0, + "Tx AC2 data queue full"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac1_data_queue_full", CTLFLAG_RD, &sc->tx_data_queue_full[1], 0, + "Tx AC1 data queue full"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ac0_data_queue_full", CTLFLAG_RD, &sc->tx_data_queue_full[0], 0, + "Tx AC0 data queue full"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_watchdog_timeouts", CTLFLAG_RD, &sc->tx_watchdog_timeouts, 0, + "Tx watchdog timeouts"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_defrag_packets", CTLFLAG_RD, &sc->tx_defrag_packets, 0, + "Tx defragmented packets"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "no_tx_desc_avail", CTLFLAG_RD, &sc->no_tx_desc_avail, 0, + "no Tx descriptors available"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_mbuf_alloc_errors", CTLFLAG_RD, &sc->rx_mbuf_alloc_errors, 0, + "Rx mbuf allocation errors"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_mbuf_dmamap_errors", CTLFLAG_RD, &sc->rx_mbuf_dmamap_errors, 0, + "Rx mbuf DMA mapping errors"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_queue_0_not_empty", CTLFLAG_RD, &sc->tx_queue_not_empty[0], 0, + "Tx queue 0 not empty"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_queue_1_not_empty", CTLFLAG_RD, &sc->tx_queue_not_empty[1], 0, + "Tx queue 1 not empty"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_beacons", CTLFLAG_RD, &sc->tx_beacons, 0, + "Tx beacons"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_noretryok", CTLFLAG_RD, &sc->tx_noretryok, 0, + "Tx successfull without retries"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_retryok", CTLFLAG_RD, &sc->tx_retryok, 0, + "Tx successfull with retries"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_failed", CTLFLAG_RD, &sc->tx_failed, 0, + "Tx failed"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_underflows", CTLFLAG_RD, &sc->tx_underflows, 0, + "Tx underflows"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_zerolen", CTLFLAG_RD, &sc->tx_zerolen, 0, + "Tx zero length"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_nonagg", CTLFLAG_RD, &sc->tx_nonagg, 0, + "Tx non-aggregated"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_agg", CTLFLAG_RD, &sc->tx_agg, 0, + "Tx aggregated"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ampdu", CTLFLAG_RD, &sc->tx_ampdu, 0, + "Tx A-MPDU"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_mpdu_zero_density", CTLFLAG_RD, &sc->tx_mpdu_zero_density, 0, + "Tx MPDU with zero density"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "tx_ampdu_sessions", CTLFLAG_RD, &sc->tx_ampdu_sessions, 0, + "Tx A-MPDU sessions"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_packets", CTLFLAG_RD, &sc->rx_packets, 0, + "Rx packets"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_ampdu", CTLFLAG_RD, &sc->rx_ampdu, 0, + "Rx A-MPDU"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_ampdu_retries", CTLFLAG_RD, &sc->rx_ampdu_retries, 0, + "Rx A-MPDU retries"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_mpdu_zero_density", CTLFLAG_RD, &sc->rx_mpdu_zero_density, 0, + "Rx MPDU with zero density"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_ampdu_sessions", CTLFLAG_RD, &sc->rx_ampdu_sessions, 0, + "Rx A-MPDU sessions"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_amsdu", CTLFLAG_RD, &sc->rx_amsdu, 0, + "Rx A-MSDU"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_crc_errors", CTLFLAG_RD, &sc->rx_crc_errors, 0, + "Rx CRC errors"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_phy_errors", CTLFLAG_RD, &sc->rx_phy_errors, 0, + "Rx PHY errors"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_false_ccas", CTLFLAG_RD, &sc->rx_false_ccas, 0, + "Rx false CCAs"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_plcp_errors", CTLFLAG_RD, &sc->rx_plcp_errors, 0, + "Rx PLCP errors"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_dup_packets", CTLFLAG_RD, &sc->rx_dup_packets, 0, + "Rx duplicate packets"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_fifo_overflows", CTLFLAG_RD, &sc->rx_fifo_overflows, 0, + "Rx FIFO overflows"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_cipher_no_errors", CTLFLAG_RD, &sc->rx_cipher_no_errors, 0, + "Rx cipher no errors"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_cipher_icv_errors", CTLFLAG_RD, &sc->rx_cipher_icv_errors, 0, + "Rx cipher ICV errors"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_cipher_mic_errors", CTLFLAG_RD, &sc->rx_cipher_mic_errors, 0, + "Rx cipher MIC errors"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(stats), OID_AUTO, + "rx_cipher_invalid_key_errors", CTLFLAG_RD, &sc->rx_cipher_invalid_key_errors, 0, + "Rx cipher invalid key errors"); +} + + +static void +rt2860_parent(struct ieee80211com *ic) +{ + struct rt2860_softc *sc = ic->ic_softc; + int startall = 0; + + RT2860_SOFTC_LOCK(sc); + if (ic->ic_nrunning> 0) { + if (!(sc->sc_flags & RT2860_RUNNING)) { + rt2860_init_locked(sc); + startall = 1; + } else + rt2860_update_promisc(ic); + } else if (sc->sc_flags & RT2860_RUNNING) + rt2860_stop_locked(sc); + RT2860_SOFTC_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static int +rt2860_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct rt2860_softc *sc = ic->ic_softc; + int error; + + RT2860_SOFTC_LOCK(sc); + if ((sc->sc_flags & RT2860_RUNNING) == 0) { + RT2860_SOFTC_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RT2860_SOFTC_UNLOCK(sc); + return (error); + } + rt2860_start(sc); + RT2860_SOFTC_UNLOCK(sc); + + return (0); +} + Index: sys/dev/rt2860/rt2860_amrr.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_amrr.h @@ -0,0 +1,80 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_AMRR_H_ +#define _RT2860_AMRR_H_ + +#define RT2860_AMRR_MIN_SUCCESS_THRESHOLD 1 +#define RT2860_AMRR_MAX_SUCCESS_THRESHOLD 15 + +struct rt2860_amrr +{ + int ntxpath; + + unsigned int min_success_threshold; + unsigned int max_success_threshold; + + int interval; +}; + +struct rt2860_amrr_node +{ + struct rt2860_amrr *amrr; + + int rate_index; + + int ticks; + + unsigned int txcnt; + unsigned int success; + unsigned int success_threshold; + unsigned int recovery; + unsigned int retrycnt; +}; + +void rt2860_amrr_init(struct rt2860_amrr *amrr, struct ieee80211vap *vap, + int ntxpath, int min_success_threshold, int max_success_threshold, int msecs); + +void rt2860_amrr_cleanup(struct rt2860_amrr *amrr); + +void rt2860_amrr_node_init(struct rt2860_amrr *amrr, + struct rt2860_amrr_node *amrr_node, struct ieee80211_node *ni); + +int rt2860_amrr_choose(struct ieee80211_node *ni, + struct rt2860_amrr_node *amrr_node); + +static __inline void rt2860_amrr_tx_complete(struct rt2860_amrr_node *amrr_node, + int ok, int retries) +{ + amrr_node->txcnt++; + + if (ok) + amrr_node->success++; + + amrr_node->retrycnt += retries; +} + +static __inline void rt2860_amrr_tx_update(struct rt2860_amrr_node *amrr_node, + int txcnt, int success, int retrycnt) +{ + amrr_node->txcnt = txcnt; + amrr_node->success = success; + amrr_node->retrycnt = retrycnt; +} + +#endif /* #ifndef _RT2860_AMRR_H_ */ Index: sys/dev/rt2860/rt2860_amrr.c =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_amrr.c @@ -0,0 +1,224 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#include + +/* + * Defines and macros + */ + +#define RT2860_AMRR_IS_SUCCESS(amrr_node) ((amrr_node)->retrycnt < (amrr_node)->txcnt / 10) + +#define RT2860_AMRR_IS_FAILURE(amrr_node) ((amrr_node)->retrycnt > (amrr_node)->txcnt / 3) + +#define RT2860_AMRR_IS_ENOUGH(amrr_node) ((amrr_node)->txcnt > 10) + +/* + * Static function prototypes + */ + +static int rt2860_amrr_update(struct rt2860_amrr *amrr, + struct rt2860_amrr_node *amrr_node, struct ieee80211_node *ni); + +/* + * rt2860_amrr_init + */ +void rt2860_amrr_init(struct rt2860_amrr *amrr, struct ieee80211vap *vap, + int ntxpath, int min_success_threshold, int max_success_threshold, int msecs) +{ + int t; + + amrr->ntxpath = ntxpath; + + amrr->min_success_threshold = min_success_threshold; + amrr->max_success_threshold = max_success_threshold; + + if (msecs < 100) + msecs = 100; + + t = msecs_to_ticks(msecs); + + amrr->interval = (t < 1) ? 1 : t; +} + +/* + * rt2860_amrr_cleanup + */ +void rt2860_amrr_cleanup(struct rt2860_amrr *amrr) +{ +} + +/* + * rt2860_amrr_node_init + */ +void rt2860_amrr_node_init(struct rt2860_amrr *amrr, + struct rt2860_amrr_node *amrr_node, struct ieee80211_node *ni) +{ + const struct ieee80211_rateset *rs; + + amrr_node->amrr = amrr; + amrr_node->success = 0; + amrr_node->recovery = 0; + amrr_node->txcnt = 0; + amrr_node->retrycnt = 0; + amrr_node->success_threshold = amrr->min_success_threshold; + + if (ni->ni_flags & IEEE80211_NODE_HT) + { + rs = (const struct ieee80211_rateset *) &ni->ni_htrates; + + for (amrr_node->rate_index = rs->rs_nrates - 1; + amrr_node->rate_index > 0 && (rs->rs_rates[amrr_node->rate_index] & IEEE80211_RATE_VAL) > 4; + amrr_node->rate_index--) ; + + ni->ni_txrate = rs->rs_rates[amrr_node->rate_index] | IEEE80211_RATE_MCS; + } + else + { + rs = &ni->ni_rates; + + for (amrr_node->rate_index = rs->rs_nrates - 1; + amrr_node->rate_index > 0 && (rs->rs_rates[amrr_node->rate_index] & IEEE80211_RATE_VAL) > 72; + amrr_node->rate_index--) ; + + ni->ni_txrate = rs->rs_rates[amrr_node->rate_index] & IEEE80211_RATE_VAL; + } + + amrr_node->ticks = ticks; +} + +/* + * rt2860_amrr_choose + */ +int rt2860_amrr_choose(struct ieee80211_node *ni, + struct rt2860_amrr_node *amrr_node) +{ + struct rt2860_amrr *amrr; + int rate_index; + + amrr = amrr_node->amrr; + + if (RT2860_AMRR_IS_ENOUGH(amrr_node) && + (ticks - amrr_node->ticks) > amrr->interval) + { + rate_index = rt2860_amrr_update(amrr, amrr_node, ni); + if (rate_index != amrr_node->rate_index) + { + if (ni->ni_flags & IEEE80211_NODE_HT) + ni->ni_txrate = ni->ni_htrates.rs_rates[rate_index] | IEEE80211_RATE_MCS; + else + ni->ni_txrate = ni->ni_rates.rs_rates[rate_index] & IEEE80211_RATE_VAL; + + amrr_node->rate_index = rate_index; + } + + amrr_node->ticks = ticks; + } + else + { + rate_index = amrr_node->rate_index; + } + + return rate_index; +} + +/* + * rt2860_amrr_update + */ +static int rt2860_amrr_update(struct rt2860_amrr *amrr, + struct rt2860_amrr_node *amrr_node, struct ieee80211_node *ni) +{ + const struct ieee80211_rateset *rs; + int rate_index; + + KASSERT(RT2860_AMRR_IS_ENOUGH(amrr_node), + ("not enough Tx count: txcnt=%d", + amrr_node->txcnt)); + + if (ni->ni_flags & IEEE80211_NODE_HT) + rs = (const struct ieee80211_rateset *) &ni->ni_htrates; + else + rs = &ni->ni_rates; + + rate_index = amrr_node->rate_index; + + if (RT2860_AMRR_IS_SUCCESS(amrr_node)) + { + amrr_node->success++; + if ((amrr_node->success >= amrr_node->success_threshold) && + (rate_index + 1 < rs->rs_nrates) && + (!(ni->ni_flags & IEEE80211_NODE_HT) || (rs->rs_rates[rate_index + 1] & IEEE80211_RATE_VAL) < (amrr->ntxpath * 8))) + { + amrr_node->recovery = 1; + amrr_node->success = 0; + + rate_index++; + } + else + { + amrr_node->recovery = 0; + } + } + else if (RT2860_AMRR_IS_FAILURE(amrr_node)) + { + amrr_node->success = 0; + + if (rate_index > 0) + { + if (amrr_node->recovery) + { + amrr_node->success_threshold *= 2; + if (amrr_node->success_threshold > amrr->max_success_threshold) + amrr_node->success_threshold = amrr->max_success_threshold; + } + else + { + amrr_node->success_threshold = amrr->min_success_threshold; + } + + rate_index--; + } + + amrr_node->recovery = 0; + } + + amrr_node->txcnt = 0; + amrr_node->retrycnt = 0; + + return rate_index; +} Index: sys/dev/rt2860/rt2860_debug.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_debug.h @@ -0,0 +1,55 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_DEBUG_H_ +#define _RT2860_DEBUG_H_ + +#ifdef RT2860_DEBUG + +enum +{ + RT2860_DEBUG_EEPROM = 0x00000001, + RT2860_DEBUG_RX = 0x00000002, + RT2860_DEBUG_TX = 0x00000004, + RT2860_DEBUG_INTR = 0x00000008, + RT2860_DEBUG_STATE = 0x00000010, + RT2860_DEBUG_CHAN = 0x00000020, + RT2860_DEBUG_NODE = 0x00000040, + RT2860_DEBUG_KEY = 0x00000080, + RT2860_DEBUG_PROT = 0x00000100, + RT2860_DEBUG_WME = 0x00000200, + RT2860_DEBUG_BEACON = 0x00000400, + RT2860_DEBUG_BA = 0x00000800, + RT2860_DEBUG_STATS = 0x00001000, + RT2860_DEBUG_RATE = 0x00002000, + RT2860_DEBUG_PERIODIC = 0x00004000, + RT2860_DEBUG_WATCHDOG = 0x00008000, + RT2860_DEBUG_RX_CRYPT = 0x00010000, + RT2860_DEBUG_TX_CRYPT = 0x00020000, + RT2860_DEBUG_ANY = 0xffffffff +}; + +#define RT2860_DPRINTF(sc, m, fmt, ...) do { if ((sc)->debug & (m)) printf(fmt, __VA_ARGS__); } while (0) + +#else + +#define RT2860_DPRINTF(sc, m, fmt, ...) + +#endif /* #ifdef RT2860_DEBUG */ + +#endif /* #ifndef _RT2860_DEBUG_H_ */ Index: sys/dev/rt2860/rt2860_eeprom.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_eeprom.h @@ -0,0 +1,91 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_EEPROM_H_ +#define _RT2860_EEPROM_H_ + +#define RT2860_EEPROM_VERSION 0x0002 +#define RT2860_EEPROM_ADDRESS01 0x0004 +#define RT2860_EEPROM_ADDRESS23 0x0006 +#define RT2860_EEPROM_ADDRESS45 0x0008 +#define RT2860_EEPROM_POWERSAVE_LEVEL 0x0022 +#define RT2860_EEPROM_ANTENNA 0x0034 +#define RT2860_EEPROM_NIC_CONFIG 0x0036 +#define RT2860_EEPROM_COUNTRY 0x0038 +#define RT2860_EEPROM_RF_FREQ_OFF 0x003a +#define RT2860_EEPROM_LED1_OFF 0x003c +#define RT2860_EEPROM_LED2_OFF 0x003e +#define RT2860_EEPROM_LED3_OFF 0x0040 +#define RT2860_EEPROM_LNA_GAIN 0x0044 +#define RT2860_EEPROM_RSSI_OFF_2GHZ_BASE 0x0046 +#define RT2860_EEPROM_RSSI2_OFF_2GHZ_BASE 0x0048 +#define RT2860_EEPROM_RSSI_OFF_5GHZ_BASE 0x004a +#define RT2860_EEPROM_RSSI2_OFF_5GHZ_BASE 0x004c +#define RT2860_EEPROM_TXPOW_RATE_DELTA 0x0050 +#define RT2860_EEPROM_TXPOW1_2GHZ_BASE 0x0052 +#define RT2860_EEPROM_TXPOW2_2GHZ_BASE 0x0060 +#define RT2860_EEPROM_TSSI_2GHZ_BASE 0x006e +#define RT2860_EEPROM_TXPOW1_5GHZ_BASE 0x0078 +#define RT2860_EEPROM_TXPOW2_5GHZ_BASE 0x00a6 +#define RT2860_EEPROM_TSSI_5GHZ_BASE 0x00d4 +#define RT2860_EEPROM_TXPOW_RATE_BASE 0x00de +#define RT2860_EEPROM_BBP_BASE 0x00f0 +#define RT3071_EEPROM_RF_BASE 0x0082 + +#define RT2860_EEPROM_RF_2820 1 /* 2.4GHz 2T3R */ +#define RT2860_EEPROM_RF_2850 2 /* 2.4/5GHz 2T3R */ +#define RT2860_EEPROM_RF_2720 3 /* 2.4GHz 1T2R */ +#define RT2860_EEPROM_RF_2750 4 /* 2.4G/5GHz 1T2R */ +#define RT2860_EEPROM_RF_3020 5 /* 2.4G 1T1R */ +#define RT2860_EEPROM_RF_2020 6 /* 2.4G B/G */ +#define RT2860_EEPROM_RF_3021 7 /* 2.4G 1T2R */ +#define RT2860_EEPROM_RF_3022 8 /* 2.4G 2T2R */ +#define RT2860_EEPROM_RF_3052 9 /* 2.4G/5G 2T2R */ +#define RT2860_EEPROM_RF_2853 10 /* 2.4G.5G 3T3R */ +#define RT2860_EEPROM_RF_3320 11 /* 2.4G 1T1R with PA (RT3350/RT3370/RT3390) */ +#define RT2860_EEPROM_RF_3322 12 /* 2.4G 2T2R with PA (RT3352/RT3371/RT3372/RT3391/RT3392) */ +#define RT2860_EEPROM_RF_3053 13 /* 2.4G/5G 3T3R (RT3883/RT3563/RT3573/RT3593/RT3662) */ +#define RT2860_EEPROM_RF_3853 13 /* 2.4G/5G 3T3R (RT3883/RT3563/RT3573/RT3593/RT3662) */ + +/* + * RT2860_EEPROM_NIC_CONFIG flags + */ +#define RT2860_EEPROM_EXT_LNA_5GHZ (1 << 3) +#define RT2860_EEPROM_EXT_LNA_2GHZ (1 << 2) +#define RT2860_EEPROM_TX_AGC_CNTL (1 << 1) +#define RT2860_EEPROM_HW_RADIO_CNTL (1 << 0) + +#define RT2860_EEPROM_LED_POLARITY (1 << 7) +#define RT2860_EEPROM_LED_MODE_MASK 0x7f + +#define RT2860_EEPROM_LED_CNTL_DEFAULT 0x01 +#define RT2860_EEPROM_LED1_OFF_DEFAULT 0x5555 +#define RT2860_EEPROM_LED2_OFF_DEFAULT 0x2221 +#define RT2860_EEPROM_LED3_OFF_DEFAULT 0xa9f8 + +#define RT2860_EEPROM_RSSI_OFF_MIN -10 +#define RT2860_EEPROM_RSSI_OFF_MAX 10 + +#define RT2860_EEPROM_TXPOW_2GHZ_MIN 0 +#define RT2860_EEPROM_TXPOW_2GHZ_MAX 31 +#define RT2860_EEPROM_TXPOW_2GHZ_DEFAULT 5 +#define RT2860_EEPROM_TXPOW_5GHZ_MIN -7 +#define RT2860_EEPROM_TXPOW_5GHZ_MAX 15 +#define RT2860_EEPROM_TXPOW_5GHZ_DEFAULT 5 + +#endif /* #ifndef _RT2860_EEPROM_H_ */ Index: sys/dev/rt2860/rt2860_fdt.c =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_fdt.c @@ -0,0 +1,114 @@ + +/*- + * Copyright (c) 2018 Hiroki Mori + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static const struct ofw_compat_data rt_compat_data[] = { + { "ralink,rt3050-wmac", RT_CHIPID_RT3050 }, + { "ralink,rt3052-wmac", RT_CHIPID_RT3052 }, + { NULL, 0 } +}; + +/* + * Static function prototypes + */ + +static int rt2860_fdt_probe(device_t dev); + +static int rt2860_fdt_attach(device_t dev); + +int rt2860_attach(device_t dev, int id); + +int rt2860_detach(device_t dev); + +int rt2860_shutdown(device_t dev); + +int rt2860_suspend(device_t dev); + +int rt2860_resume(device_t dev); + +/* + * rt2860_fdt_probe + */ +static int rt2860_fdt_probe(device_t dev) +{ + const struct ofw_compat_data * cd; + + + cd = ofw_bus_search_compatible(dev, rt_compat_data); + if (cd->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Ralink RT2860 802.11n MAC/BPP"); + return 0; +} + +/* + * rt2860_fdt_attach + */ +static int rt2860_fdt_attach(device_t dev) +{ + struct rt2860_softc *sc; + const struct ofw_compat_data * cd; + + sc = device_get_softc(dev); + sc->mem_rid = 0; + + cd = ofw_bus_search_compatible(dev, rt_compat_data); + + return (rt2860_attach(dev, cd->ocd_data)); +} + +static device_method_t rt2860_fdt_dev_methods[] = +{ + DEVMETHOD(device_probe, rt2860_fdt_probe), + DEVMETHOD(device_attach, rt2860_fdt_attach), + DEVMETHOD(device_detach, rt2860_detach), + DEVMETHOD(device_shutdown, rt2860_shutdown), + DEVMETHOD(device_suspend, rt2860_suspend), + DEVMETHOD(device_resume, rt2860_resume), + { 0, 0 } +}; + +static driver_t rt2860_fdt_driver = +{ + "rt2860", + rt2860_fdt_dev_methods, + sizeof(struct rt2860_softc) +}; + +static devclass_t rt2860_fdt_dev_class; + +DRIVER_MODULE(rt2860, simplebus, rt2860_fdt_driver, rt2860_fdt_dev_class, 0, 0); +MODULE_DEPEND(rt2860, wlan, 1, 1, 1); + Index: sys/dev/rt2860/rt2860_io.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_io.h @@ -0,0 +1,73 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_IO_H_ +#define _RT2860_IO_H_ + +#include + +#define RT2860_IO_MCU_CMD_SLEEP 0x30 +#define RT2860_IO_MCU_CMD_WAKEUP 0x31 +#define RT2860_IO_MCU_CMD_RADIOOFF 0x35 +#define RT2860_IO_MCU_CMD_LEDS 0x50 +#define RT2860_IO_MCU_CMD_LED_BRIGHTNESS 0x51 +#define RT2860_IO_MCU_CMD_LED1 0x52 +#define RT2860_IO_MCU_CMD_LED2 0x53 +#define RT2860_IO_MCU_CMD_LED3 0x54 +#define RT2860_IO_MCU_CMD_BOOT 0x72 +#define RT2860_IO_MCU_CMD_BBP 0x80 +#define RT2860_IO_MCU_CMD_POWERSAVE_LEVEL 0x83 + +void rt2860_io_rf_load_defaults(struct rt2860_softc *sc); +uint32_t rt2860_io_mac_read(struct rt2860_softc *sc, uint16_t reg); + +void rt2860_io_mac_read_multi(struct rt2860_softc *sc, + uint16_t reg, void *buf, size_t len); + +void rt2860_io_mac_write(struct rt2860_softc *sc, + uint16_t reg, uint32_t val); + +void rt2860_io_mac_write_multi(struct rt2860_softc *sc, + uint16_t reg, const void *buf, size_t len); + +void rt2860_io_mac_set_region_4(struct rt2860_softc *sc, + uint16_t reg, uint32_t val, size_t len); + +uint16_t rt2860_io_eeprom_read(struct rt2860_softc *sc, uint16_t addr); + +void rt2860_io_eeprom_read_multi(struct rt2860_softc *sc, + uint16_t addr, void *buf, size_t len); + +uint8_t rt2860_io_bbp_read(struct rt2860_softc *sc, uint8_t reg); + +void rt2860_io_bbp_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val); + +void rt2860_io_rf_write(struct rt2860_softc *sc, uint8_t reg, uint32_t val); + +int32_t rt2860_io_rf_read(struct rt2860_softc *sc, uint8_t reg); + +void rt2860_io_mcu_cmd(struct rt2860_softc *sc, uint8_t cmd, + uint8_t token, uint16_t arg); + +int rt2860_io_mcu_cmd_check(struct rt2860_softc *sc, uint8_t cid); + +int rt2860_io_mcu_load_ucode(struct rt2860_softc *sc, + const uint8_t *ucode, size_t len); + + +#endif /* #ifndef _RT2860_IO_H_ */ Index: sys/dev/rt2860/rt2860_io.c =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_io.c @@ -0,0 +1,864 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#if defined(__mips__) +#include + +#define EEPROM_IN_FLASH +#endif + +/* + * Defines and macros + */ + +/* + * RT2860_IO_EEPROM_RAISE_CLK + */ +#define RT2860_IO_EEPROM_RAISE_CLK(sc, val) \ +do \ +{ \ + (val) |= RT2860_REG_EESK; \ + \ + rt2860_io_mac_write((sc), RT2860_REG_EEPROM_CSR, (val)); \ + \ + DELAY(1); \ +} while (0) + +/* + * RT2860_IO_EEPROM_LOWER_CLK + */ +#define RT2860_IO_EEPROM_LOWER_CLK(sc, val) \ +do \ +{ \ + (val) &= ~RT2860_REG_EESK; \ + \ + rt2860_io_mac_write((sc), RT2860_REG_EEPROM_CSR, (val)); \ + \ + DELAY(1); \ +} while (0) + +#define RT2860_IO_BYTE_CRC16(byte, crc) \ + ((uint16_t) (((crc) << 8) ^ rt2860_io_ccitt16[(((crc) >> 8) ^ (byte)) & 255])) + +/* + * Static function prototypes + */ + +static void rt2860_io_eeprom_shiftout_bits(struct rt2860_softc *sc, + uint16_t val, uint16_t count); + +static uint16_t rt2860_io_eeprom_shiftin_bits(struct rt2860_softc *sc); + +static uint8_t rt2860_io_byte_rev(uint8_t byte); + +#if defined(__mips__) && !defined(EEPROM_IN_FLASH) +/* Default EEPROM value for RT3050 */ +static const uint8_t rt3050_eeprom[] = { + 0x50, 0x30, 0x01, 0x01, 0x00, 0x0c, 0x43, 0x30, 0x52, 0x88, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0c, + 0x43, 0x30, 0x52, 0x77, 0x00, 0x0c, 0x43, 0x30, 0x52, 0x66, 0x11, 0x05, 0x20, 0x00, + 0xff, 0xff, 0x2f, 0x01, 0x55, 0x77, 0xa8, 0xaa, 0x8c, 0x88, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x10, 0x10, + 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x66, 0x66, + 0xcc, 0xaa, 0x88, 0x66, 0xcc, 0xaa, 0x88, 0x66, 0xcc, 0xaa, 0x88, 0x66, 0xcc, 0xaa, + 0x88, 0x66, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; +#endif + +uint8_t rt3052_rf_default[] = { + 0x50, /* 0 */ + 0x01, + 0xF7, + 0x75, + 0x40, + 0x03, + 0x42, + 0x50, + 0x39, + 0x0F, + 0x60, /* 10 */ + 0x21, + 0x75, + 0x75, + 0x90, + 0x58, + 0xB3, + 0x92, + 0x2C, + 0x02, + 0xBA, /* 20 */ + 0xDB, + 0x00, + 0x31, + 0x08, + 0x01, + 0x25, /* Core Power: 0x25=1.25V */ + 0x23, /* RF: 1.35V */ + 0x13, /* ADC: must consist with R27 */ + 0x83, + 0x00, /* 30 */ + 0x00, +}; +/* #endif */ + + +/* + * Static variables + */ + +static const uint16_t rt2860_io_ccitt16[] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +/* + * rt2860_io_mac_read + */ +uint32_t rt2860_io_mac_read(struct rt2860_softc *sc, uint16_t reg) +{ + + return bus_space_read_4(sc->bst, sc->bsh, reg); +} + +/* + * rt2860_io_mac_read_multi + */ +void rt2860_io_mac_read_multi(struct rt2860_softc *sc, + uint16_t reg, void *buf, size_t len) +{ + + bus_space_read_region_1(sc->bst, sc->bsh, reg, buf, len); +} + +/* + * rt2860_io_mac_write + */ +void rt2860_io_mac_write(struct rt2860_softc *sc, + uint16_t reg, uint32_t val) +{ + + bus_space_write_4(sc->bst, sc->bsh, reg, val); +} + +/* + * rt2860_io_mac_write_multi + */ +void rt2860_io_mac_write_multi(struct rt2860_softc *sc, + uint16_t reg, const void *buf, size_t len) +{ + int i; + const uint8_t *p; + + p = buf; + for (i = 0; i < len; i ++) + bus_space_write_1(sc->bst, sc->bsh, reg + i, *(p+i)); + +#ifdef notyet + bus_space_write_region_1(sc->bst, sc->bsh, reg, buf, len); +#endif +} + +/* + * rt2860_io_mac_set_region_4 + */ +void rt2860_io_mac_set_region_4(struct rt2860_softc *sc, + uint16_t reg, uint32_t val, size_t len) +{ + int i; + + for (i = 0; i < len; i += sizeof(uint32_t)) + rt2860_io_mac_write(sc, reg + i, val); +} + +/* Read 16-bit from eFUSE ROM (>=RT3071 only.) */ +static uint16_t +rt3090_efuse_read_2(struct rt2860_softc *sc, uint16_t addr) +{ + uint32_t tmp; + uint16_t reg; + int ntries; + + addr *= 2; + /*- + * Read one 16-byte block into registers EFUSE_DATA[0-3]: + * DATA0: F E D C + * DATA1: B A 9 8 + * DATA2: 7 6 5 4 + * DATA3: 3 2 1 0 + */ + tmp = rt2860_io_mac_read(sc, RT3070_EFUSE_CTRL); + tmp &= ~(RT3070_EFSROM_MODE_MASK | RT3070_EFSROM_AIN_MASK); + tmp |= (addr & ~0xf) << RT3070_EFSROM_AIN_SHIFT | RT3070_EFSROM_KICK; + rt2860_io_mac_write(sc, RT3070_EFUSE_CTRL, tmp); + for (ntries = 0; ntries < 500; ntries++) { + tmp = rt2860_io_mac_read(sc, RT3070_EFUSE_CTRL); + if (!(tmp & RT3070_EFSROM_KICK)) + break; + DELAY(2); + } + if (ntries == 500) + return 0xffff; + + if ((tmp & RT3070_EFUSE_AOUT_MASK) == RT3070_EFUSE_AOUT_MASK) + return 0xffff; /* address not found */ + + /* determine to which 32-bit register our 16-bit word belongs */ + reg = RT3070_EFUSE_DATA3 - (addr & 0xc); + tmp = rt2860_io_mac_read(sc, reg); + + return (addr & 2) ? tmp >> 16 : tmp & 0xffff; +} + + +/* + * rt2860_io_eeprom_read + */ +uint16_t rt2860_io_eeprom_read(struct rt2860_softc *sc, uint16_t addr) +{ + uint32_t tmp; + uint16_t val; + + addr = (addr >> 1); + +#if defined(__mips__) + if (sc->mac_rev == 0x28720200) { +#ifdef EEPROM_IN_FLASH + /* xxx get from dts value */ + uint8_t *eeprom = (uint8_t *)MIPS_PHYS_TO_KSEG1(0x1f040000); + return (*(eeprom + addr*2) + + *(eeprom + addr*2+1) * 0x100); +#else + return (rt3050_eeprom[addr*2] + + rt3050_eeprom[addr*2+1] * 0x100); +#endif + } else if ((sc->mac_rev & 0xffff0000) >= 0x30710000) { + tmp = rt2860_io_mac_read(sc, RT3070_EFUSE_CTRL); + if (tmp & RT3070_SEL_EFUSE) + return (rt3090_efuse_read_2(sc, addr)); + } +#endif + + tmp = rt2860_io_mac_read(sc, RT2860_REG_EEPROM_CSR); + + tmp &= ~(RT2860_REG_EEDI | RT2860_REG_EEDO | RT2860_REG_EESK); + tmp |= RT2860_REG_EECS; + + rt2860_io_mac_write(sc, RT2860_REG_EEPROM_CSR, tmp); + + if (((sc->mac_rev & 0xffff0000) != 0x30710000) && + ((sc->mac_rev & 0xffff0000) != 0x30900000) && + ((sc->mac_rev & 0xffff0000) != 0x35720000) && + ((sc->mac_rev & 0xffff0000) != 0x33900000)) + { + RT2860_IO_EEPROM_RAISE_CLK(sc, tmp); + RT2860_IO_EEPROM_LOWER_CLK(sc, tmp); + } + + rt2860_io_eeprom_shiftout_bits(sc, RT2860_REG_EEOP_READ, 3); + rt2860_io_eeprom_shiftout_bits(sc, addr, sc->eeprom_addr_num); + + val = rt2860_io_eeprom_shiftin_bits(sc); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_EEPROM_CSR); + + tmp &= ~(RT2860_REG_EECS | RT2860_REG_EEDI); + + rt2860_io_mac_write(sc, RT2860_REG_EEPROM_CSR, tmp); + + RT2860_IO_EEPROM_RAISE_CLK(sc, tmp); + RT2860_IO_EEPROM_LOWER_CLK(sc, tmp); + + return val; +} + +/* + * rt2860_io_eeprom_read_multi + */ +void rt2860_io_eeprom_read_multi(struct rt2860_softc *sc, + uint16_t addr, void *buf, size_t len) +{ + uint16_t *ptr; + int i; + + len += len % sizeof(uint16_t); + ptr = buf; + + i = 0; + + do + { + *ptr++ = rt2860_io_eeprom_read(sc, addr + i); + + i += sizeof(uint16_t); + len -= sizeof(uint16_t); + } while (len > 0); +} + +/* + * rt2860_io_bbp_read + */ +uint8_t rt2860_io_bbp_read(struct rt2860_softc *sc, uint8_t reg) +{ + int ntries; + uint32_t tmp; + + if (sc->mac_rev == 0x28720200) + { + for (ntries = 0; ntries < 100; ntries ++) { + if ( !(rt2860_io_mac_read(sc, RT2860_REG_BBP_CSR_CFG) & + RT2860_REG_BBP_CSR_BUSY) ) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s:%s: BBP busy after 100 probes\n", + device_get_nameunit(sc->dev), __func__); + return (0); + } + rt2860_io_mac_write(sc, RT2860_REG_BBP_CSR_CFG, + RT2860_REG_BBP_CSR_READ | + RT2860_REG_BBP_CSR_KICK | RT2860_REG_BBP_RW_MODE_PARALLEL | + (reg & RT2860_REG_BBP_REG_MASK) << RT2860_REG_BBP_REG_SHIFT); + for (ntries = 0; ntries < 100; ntries ++) { + if ( !(rt2860_io_mac_read(sc, RT2860_REG_BBP_CSR_CFG) & + RT2860_REG_BBP_CSR_BUSY) ) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s:%s: BBP busy after 100 probes\n", + device_get_nameunit(sc->dev), __func__); + return (0); + } + else { + return + ((rt2860_io_mac_read(sc, RT2860_REG_BBP_CSR_CFG) >> + RT2860_REG_BBP_VAL_SHIFT) & + RT2860_REG_BBP_VAL_MASK); + } + return (0); + } + + for (ntries = 0; ntries < 100; ntries++) + { + if (!(rt2860_io_mac_read(sc, RT2860_REG_H2M_MAILBOX_BBP_AGENT) & + RT2860_REG_BBP_CSR_BUSY)) + break; + + DELAY(1); + } + + if (ntries == 100) + { + printf("%s: could not read from BBP through MCU: reg=0x%02x\n", + device_get_nameunit(sc->dev), reg); + return 0; + } + + tmp = RT2860_REG_BBP_RW_MODE_PARALLEL | + RT2860_REG_BBP_CSR_BUSY | + RT2860_REG_BBP_CSR_READ | + ((reg & RT2860_REG_BBP_REG_MASK) << RT2860_REG_BBP_REG_SHIFT); + + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX_BBP_AGENT, tmp); + + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_BBP, + RT2860_REG_H2M_TOKEN_NO_INTR, 0); + + DELAY(1000); + + for (ntries = 0; ntries < 100; ntries++) + { + tmp = rt2860_io_mac_read(sc, RT2860_REG_H2M_MAILBOX_BBP_AGENT); + if (!(tmp & RT2860_REG_BBP_CSR_BUSY)) + return ((tmp >> RT2860_REG_BBP_VAL_SHIFT) & + RT2860_REG_BBP_VAL_MASK); + + DELAY(1); + } + + printf("%s: could not read from BBP through MCU: reg=0x%02x\n", + device_get_nameunit(sc->dev), reg); + + return 0; +} + +/* + * rt2860_io_bbp_write + */ +void rt2860_io_bbp_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val) +{ + int ntries; + uint32_t tmp; + + if (sc->mac_rev == 0x28720200) + { + for (ntries = 0; ntries < 100; ntries ++) { + if ( !(rt2860_io_mac_read(sc, RT2860_REG_BBP_CSR_CFG) & + RT2860_REG_BBP_CSR_BUSY) ) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s:%s: BBP busy after 100 probes\n", + device_get_nameunit(sc->dev), __func__); + return; + } + rt2860_io_mac_write(sc, RT2860_REG_BBP_CSR_CFG, + RT2860_REG_BBP_CSR_KICK | RT2860_REG_BBP_RW_MODE_PARALLEL | + (reg & RT2860_REG_BBP_REG_MASK) << RT2860_REG_BBP_REG_SHIFT | + (val & RT2860_REG_BBP_VAL_MASK) << RT2860_REG_BBP_VAL_SHIFT ); + rt2860_io_bbp_read(sc, reg); + return; + } + + for (ntries = 0; ntries < 100; ntries++) + { + if (!(rt2860_io_mac_read(sc, RT2860_REG_H2M_MAILBOX_BBP_AGENT) & + RT2860_REG_BBP_CSR_BUSY)) + break; + + DELAY(1); + } + + if (ntries == 100) + { + printf("%s: could not write to BBP through MCU: reg=0x%02x\n", + device_get_nameunit(sc->dev), reg); + return; + } + + tmp = RT2860_REG_BBP_RW_MODE_PARALLEL | + RT2860_REG_BBP_CSR_BUSY | + ((reg & RT2860_REG_BBP_REG_MASK) << RT2860_REG_BBP_REG_SHIFT) | + ((val & RT2860_REG_BBP_VAL_MASK) << RT2860_REG_BBP_VAL_SHIFT); + + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX_BBP_AGENT, tmp); + + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_BBP, + RT2860_REG_H2M_TOKEN_NO_INTR, 0); + + DELAY(1000); +} + +/* + * rt2860_io_rf_write + */ +void rt2860_io_rf_write(struct rt2860_softc *sc, uint8_t reg, uint32_t val) +{ + int ntries; + if (sc->mac_rev == 0x28720200) + { + for (ntries = 0; ntries < 100; ntries ++) { + if ( !(rt2860_io_mac_read(sc, RT2872_REG_RF_CSR_CFG) & + RT2872_REG_RF_CSR_BUSY) ) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s:%s: RF busy after 100 probes\n", + device_get_nameunit(sc->dev), __func__); + return; + } + rt2860_io_mac_write(sc, RT2872_REG_RF_CSR_CFG, + RT2872_REG_RF_CSR_KICK | RT2872_REG_RF_CSR_WRITE | + (reg & RT2872_REG_RF_ID_MASK) << RT2872_REG_RF_ID_SHIFT | + (val & RT2872_REG_RF_VAL_MASK) << RT2872_REG_RF_VAL_SHIFT ); + rt2860_io_rf_read(sc, reg); + return; + } + + + for (ntries = 0; ntries < 100; ntries++) + if (!(rt2860_io_mac_read(sc, RT2860_REG_RF_CSR_CFG0) & + RT2860_REG_RF_BUSY)) + break; + + if (ntries == 100) + { + printf("%s: could not write to RF: reg=0x%02x\n", + device_get_nameunit(sc->dev), reg); + return; + } + + rt2860_io_mac_write(sc, RT2860_REG_RF_CSR_CFG0, val); +} + +/* + * rt2860_io_rf_read + */ +int32_t rt2860_io_rf_read(struct rt2860_softc *sc, uint8_t reg) +{ + int ntries; + if (sc->mac_rev == 0x28720200) + { + for (ntries = 0; ntries < 100; ntries ++) { + if ( !(rt2860_io_mac_read(sc, RT2872_REG_RF_CSR_CFG) & + RT2872_REG_RF_CSR_BUSY) ) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s:%s: RF busy after 100 probes\n", + device_get_nameunit(sc->dev), __func__); + return (-1); + } + rt2860_io_mac_write(sc, RT2872_REG_RF_CSR_CFG, + RT2872_REG_RF_CSR_KICK | + (reg & RT2872_REG_RF_ID_MASK) << RT2872_REG_RF_ID_SHIFT ); + + for (ntries = 0; ntries < 100; ntries ++) { + if ( !(rt2860_io_mac_read(sc, RT2872_REG_RF_CSR_CFG) & + RT2872_REG_RF_CSR_BUSY) ) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s:%s: RF busy after 100 probes\n", + device_get_nameunit(sc->dev), __func__); + } + + return (rt2860_io_mac_read(sc, RT2872_REG_RF_CSR_CFG) & RT2872_REG_RF_VAL_MASK); + } + return (-1); +} + +/* + * rt2860_io_rf_load_defaults + */ +void rt2860_io_rf_load_defaults(struct rt2860_softc *sc) +{ + int i; + + if (sc->mac_rev == 0x28720200) { + for (i = 0; i < sizeof(rt3052_rf_default); i ++) + rt2860_io_rf_write(sc, i, rt3052_rf_default[i]); + } +} + +/* + * rt2860_io_mcu_cmd + */ +void rt2860_io_mcu_cmd(struct rt2860_softc *sc, uint8_t cmd, + uint8_t token, uint16_t arg) +{ + uint32_t tmp; + int ntries; + + if (sc->mac_rev == 0x28720200) + return; + + for (ntries = 0; ntries < 100; ntries++) + { + if (!(rt2860_io_mac_read(sc, RT2860_REG_H2M_MAILBOX) & + RT2860_REG_H2M_BUSY)) + break; + + DELAY(2); + } + + if (ntries == 100) + { + printf("%s: could not read H2M: cmd=0x%02x\n", + device_get_nameunit(sc->dev), cmd); + return; + } + + tmp = RT2860_REG_H2M_BUSY | (token << 16) | arg; + + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX, tmp); + rt2860_io_mac_write(sc, RT2860_REG_H2M_HOST_CMD, cmd); +} + +/* + * rt2860_io_mcu_cmd_check + */ +int rt2860_io_mcu_cmd_check(struct rt2860_softc *sc, uint8_t cid) +{ + uint32_t tmp, mask, status; + int result, ntries; + + result = -1; + + for (ntries = 0; ntries < 200; ntries++) + { + tmp = rt2860_io_mac_read(sc, RT2860_REG_H2M_MAILBOX_CID); + + if (((cid >> RT2860_REG_H2M_CID0_SHIFT) & RT2860_REG_H2M_CID_MASK) == cid) + { + mask = (RT2860_REG_H2M_CID_MASK << RT2860_REG_H2M_CID0_SHIFT); + break; + } + else if (((tmp >> RT2860_REG_H2M_CID1_SHIFT) & RT2860_REG_H2M_CID_MASK) == cid) + { + mask = (RT2860_REG_H2M_CID_MASK << RT2860_REG_H2M_CID1_SHIFT); + break; + } + else if (((tmp >> RT2860_REG_H2M_CID2_SHIFT) & RT2860_REG_H2M_CID_MASK) == cid) + { + mask = (RT2860_REG_H2M_CID_MASK << RT2860_REG_H2M_CID2_SHIFT); + break; + } + else if (((tmp >> RT2860_REG_H2M_CID3_SHIFT) & RT2860_REG_H2M_CID_MASK) == cid) + { + mask = (RT2860_REG_H2M_CID_MASK << RT2860_REG_H2M_CID3_SHIFT); + break; + } + + DELAY(100); + } + + status = rt2860_io_mac_read(sc, RT2860_REG_H2M_MAILBOX_STATUS); + + if (ntries < 200) + { + status &= mask; + + if ((status == 0x1) || + (status == 0x100) || + (status == 0x10000) || + (status == 0x1000000)) + result = 0; + } + + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX_STATUS, 0xffffffff); + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX_CID, 0xffffffff); + + return result; +} + +/* + * rt2860_io_mcu_load_ucode + */ +int rt2860_io_mcu_load_ucode(struct rt2860_softc *sc, + const uint8_t *ucode, size_t len) +{ + int i, ntries; + uint16_t crc; + + for (i = 0, crc = 0xffff; i < len - 2; i++) + crc = RT2860_IO_BYTE_CRC16(rt2860_io_byte_rev(ucode[i]), crc); + + if (ucode[len - 2] != rt2860_io_byte_rev(crc >> 8) || + ucode[len - 1] != rt2860_io_byte_rev(crc)) + { + printf("%s: wrong microcode crc\n", + device_get_nameunit(sc->dev)); + return EINVAL; + } + + rt2860_io_mac_write(sc, RT2860_REG_PBF_SYS_CTRL, RT2860_REG_HST_PM_SEL); + + for(i = 0; i < len; i += 4) + { + rt2860_io_mac_write(sc, RT2860_REG_MCU_UCODE_BASE + i, + (ucode[i+3] << 24) | (ucode[i+2] << 16) | + (ucode[i+1] << 8) | ucode[i]); + } + + if (sc->mac_rev != 0x28720200) + rt2860_io_mac_write_multi(sc, RT2860_REG_MCU_UCODE_BASE, + ucode, len); + + rt2860_io_mac_write(sc, RT2860_REG_PBF_SYS_CTRL, 0); + + if (sc->mac_rev != 0x28720200) + rt2860_io_mac_write(sc, RT2860_REG_PBF_SYS_CTRL, + RT2860_REG_MCU_RESET); + + DELAY(10000); + + /* initialize BBP R/W access agent */ + + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX_BBP_AGENT, 0); + rt2860_io_mac_write(sc, RT2860_REG_H2M_MAILBOX, 0); + + if (sc->mac_rev != 0x28720200) { + + for (ntries = 0; ntries < 1000; ntries++) + { + if (rt2860_io_mac_read(sc, RT2860_REG_PBF_SYS_CTRL) & + RT2860_REG_MCU_READY) + break; + + DELAY(1000); + } + + if (ntries == 1000) + { + printf("%s: timeout waiting for MCU to initialize\n", + device_get_nameunit(sc->dev)); + return ETIMEDOUT; + } + } + + return 0; +} + +/* + * rt2860_io_eeprom_shiftout_bits + */ +static void rt2860_io_eeprom_shiftout_bits(struct rt2860_softc *sc, + uint16_t val, uint16_t count) +{ + uint32_t mask, tmp; + + mask = (1 << (count - 1)); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_EEPROM_CSR); + + tmp &= ~(RT2860_REG_EEDO | RT2860_REG_EEDI); + + do + { + tmp &= ~RT2860_REG_EEDI; + + if(val & mask) + tmp |= RT2860_REG_EEDI; + + rt2860_io_mac_write(sc, RT2860_REG_EEPROM_CSR, tmp); + + RT2860_IO_EEPROM_RAISE_CLK(sc, tmp); + RT2860_IO_EEPROM_LOWER_CLK(sc, tmp); + + mask = (mask >> 1); + } while (mask); + + tmp &= ~RT2860_REG_EEDI; + + rt2860_io_mac_write(sc, RT2860_REG_EEPROM_CSR, tmp); +} + +/* + * rt2860_io_eeprom_shiftin_bits + */ +static uint16_t rt2860_io_eeprom_shiftin_bits(struct rt2860_softc *sc) +{ + uint32_t tmp; + uint16_t val; + int i; + + val = 0; + + tmp = rt2860_io_mac_read(sc, RT2860_REG_EEPROM_CSR); + + tmp &= ~(RT2860_REG_EEDO | RT2860_REG_EEDI); + + for(i = 0; i < 16; i++) + { + val = (val << 1); + + RT2860_IO_EEPROM_RAISE_CLK(sc, tmp); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_EEPROM_CSR); + + RT2860_IO_EEPROM_LOWER_CLK(sc, tmp); + + tmp &= ~RT2860_REG_EEDI; + if(tmp & RT2860_REG_EEDO) + val |= 1; + } + + return val; +} + +/* + * rt2860_io_byte_rev + */ +static uint8_t rt2860_io_byte_rev(uint8_t byte) +{ + int i; + uint8_t tmp; + + for(i = 0, tmp = 0; ; i++) + { + if(byte & 0x80) + tmp |= 0x80; + + if(i == 7) + break; + + byte <<= 1; + tmp >>= 1; + } + + return tmp; +} Index: sys/dev/rt2860/rt2860_led.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_led.h @@ -0,0 +1,33 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_LED_H_ +#define _RT2860_LED_H_ + +#include + +#define RT2860_LED_CMD_RADIO_OFF 0 +#define RT2860_LED_CMD_RADIO_ON (1 << 5) +#define RT2860_LED_CMD_LINK_2GHZ (1 << 6) +#define RT2860_LED_CMD_LINK_5GHZ (1 << 7) + +void rt2860_led_brightness(struct rt2860_softc *sc, uint8_t brightness); + +void rt2860_led_cmd(struct rt2860_softc *sc, uint8_t cmd); + +#endif /* #ifndef _RT2860_LED_H_ */ Index: sys/dev/rt2860/rt2860_led.c =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_led.c @@ -0,0 +1,51 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +/* + * rt2860_led_brightness + */ +void rt2860_led_brightness(struct rt2860_softc *sc, uint8_t brightness) +{ + uint8_t polarity; + uint16_t tmp; + + polarity = (sc->led_cntl & RT2860_EEPROM_LED_POLARITY) ? 1 : 0; + + tmp = (polarity << 8) | brightness; + + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_LED_BRIGHTNESS, + RT2860_REG_H2M_TOKEN_NO_INTR, tmp); +} + +/* + * rt2860_led_cmd + */ +void rt2860_led_cmd(struct rt2860_softc *sc, uint8_t cmd) +{ + uint16_t tmp; + + tmp = (cmd << 8) | (sc->led_cntl & RT2860_EEPROM_LED_MODE_MASK); + + rt2860_io_mcu_cmd(sc, RT2860_IO_MCU_CMD_LEDS, + RT2860_REG_H2M_TOKEN_NO_INTR, tmp); +} Index: sys/dev/rt2860/rt2860_pci.c =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_pci.c @@ -0,0 +1,163 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Defines and macros + */ + +#define PCI_VENDOR_RALINK 0x1814 +#define PCI_PRODUCT_RALINK_RT2860_PCI 0x0601 +#define PCI_PRODUCT_RALINK_RT2860_PCIe 0x0681 +#define PCI_PRODUCT_RALINK_RT2760_PCI 0x0701 +#define PCI_PRODUCT_RALINK_RT2790_PCIe 0x0781 +#define PCI_PRODUCT_RALINK_RT3090_PCIe 0x3090 + +/* + * Data structures and types + */ + +struct rt2860_pci_ident +{ + uint16_t vendor; + uint16_t device; + const char *name; +}; + +/* + * Static function prototypes + */ + +static int rt2860_pci_probe(device_t dev); + +static int rt2860_pci_attach(device_t dev); + +/* + * Bus independent methods + */ + +int rt2860_attach(device_t dev, int id); + +int rt2860_detach(device_t dev); + +int rt2860_shutdown(device_t dev); + +int rt2860_suspend(device_t dev); + +int rt2860_resume(device_t dev); + + +/* + * Static variables + */ + +static const struct rt2860_pci_ident rt2860_pci_ids[] = +{ + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2860_PCI, "Ralink RT2860 PCI" }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2860_PCIe, "Ralink RT2860 PCIe" }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2760_PCI, "Ralink RT2760 PCI" }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2790_PCIe, "Ralink RT2790 PCIe" }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2790_PCIe, "Ralink RT2790 PCIe" }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT3090_PCIe, "Ralink RT3090 PCIe" }, + { 0, 0, NULL } +}; + + +/* + * rt2860_pci_probe + */ +static int rt2860_pci_probe(device_t dev) +{ + const struct rt2860_pci_ident *ident; + + for (ident = rt2860_pci_ids; ident->name != NULL; ident++) + { + if (pci_get_vendor(dev) == ident->vendor && + pci_get_device(dev) == ident->device) + { + device_set_desc(dev, ident->name); + return 0; + } + } + + return ENXIO; +} + +/* + * rt2860_pci_attach + */ +static int rt2860_pci_attach(device_t dev) +{ + struct rt2860_softc *sc; + + sc = device_get_softc(dev); + + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) + { + printf("%s: chip is in D%d power mode, setting to D0\n", + device_get_nameunit(dev), pci_get_powerstate(dev)); + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + } + + /* enable bus-mastering */ + pci_enable_busmaster(dev); + sc->mem_rid = PCIR_BAR(0); + + return (rt2860_attach(dev, pci_get_device(dev))); +} + +static device_method_t rt2860_pci_dev_methods[] = +{ + /* PCI only */ + DEVMETHOD(device_probe, rt2860_pci_probe), + DEVMETHOD(device_attach, rt2860_pci_attach), + + /* Any bus */ + DEVMETHOD(device_detach, rt2860_detach), + DEVMETHOD(device_shutdown, rt2860_shutdown), + DEVMETHOD(device_suspend, rt2860_suspend), + DEVMETHOD(device_resume, rt2860_resume), + { 0, 0 } +}; + +static driver_t rt2860_pci_driver = +{ + "rt2860", + rt2860_pci_dev_methods, + sizeof(struct rt2860_softc) +}; + +static devclass_t rt2860_pci_dev_class; + +DRIVER_MODULE(rt2860, pci, rt2860_pci_driver, rt2860_pci_dev_class, 0, 0); +MODULE_DEPEND(rt2860, pci, 1, 1, 1); +MODULE_DEPEND(rt2860, wlan, 1, 1, 1); + Index: sys/dev/rt2860/rt2860_read_eeprom.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_read_eeprom.h @@ -0,0 +1,29 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_READ_EEPROM_H_ +#define _RT2860_READ_EEPROM_H_ + +#include + +void rt2860_read_eeprom(struct rt2860_softc *sc); + +uint32_t rt2860_read_eeprom_txpow_rate_add_delta(uint32_t txpow_rate, + int8_t delta); + +#endif /* #ifndef _RT2860_READ_EEPROM_H_ */ Index: sys/dev/rt2860/rt2860_read_eeprom.c =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_read_eeprom.c @@ -0,0 +1,510 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +/* + * rt2860_read_eeprom + */ +void rt2860_read_eeprom(struct rt2860_softc *sc) +{ + uint32_t tmp; + uint16_t val; + int i; + + /* read EEPROM address number */ + + tmp = rt2860_io_mac_read(sc, RT2860_REG_EEPROM_CSR); + + if((tmp & 0x30) == 0) + sc->eeprom_addr_num = 6; + else if((tmp & 0x30) == 0x10) + sc->eeprom_addr_num = 8; + else + sc->eeprom_addr_num = 8; + + /* read EEPROM version */ + + sc->eeprom_rev = rt2860_io_eeprom_read(sc, RT2860_EEPROM_VERSION); + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM rev=0x%04x\n", + device_get_nameunit(sc->dev), sc->eeprom_rev); + + /* read MAC address */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_ADDRESS01); + + sc->mac_addr[0] = (val & 0xff); + sc->mac_addr[1] = (val >> 8); + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_ADDRESS23); + + sc->mac_addr[2] = (val & 0xff); + sc->mac_addr[3] = (val >> 8); + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_ADDRESS45); + + sc->mac_addr[4] = (val & 0xff); + sc->mac_addr[5] = (val >> 8); + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM mac address=%s\n", + device_get_nameunit(sc->dev), ether_sprintf(sc->mac_addr)); + + /* read RF information */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_ANTENNA); + if (val == 0xffff) + { + printf("%s: invalid EEPROM antenna info\n", + device_get_nameunit(sc->dev)); + + sc->rf_rev = RT2860_EEPROM_RF_2820; + sc->ntxpath = 1; + sc->nrxpath = 2; + } + else + { + sc->rf_rev = (val >> 8) & 0xf; + sc->ntxpath = (val >> 4) & 0xf; + sc->nrxpath = (val & 0xf); + } + + if ((sc->mac_rev != 0x28830300) && (sc->nrxpath > 2)) + { + /* only 2 Rx streams for RT2860 series */ + + sc->nrxpath = 2; + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM RF rev=0x%04x, paths=%dT%dR\n", + device_get_nameunit(sc->dev), sc->rf_rev, sc->ntxpath, sc->nrxpath); + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_NIC_CONFIG); + if ((val & 0xff00) != 0xff00) + sc->patch_dac = (val >> 15) & 1; + + sc->hw_radio_cntl = ((val & RT2860_EEPROM_HW_RADIO_CNTL) ? 1 : 0); + sc->tx_agc_cntl = ((val & RT2860_EEPROM_TX_AGC_CNTL) ? 1 : 0); + sc->ext_lna_2ghz = ((val & RT2860_EEPROM_EXT_LNA_2GHZ) ? 1 : 0); + sc->ext_lna_5ghz = ((val & RT2860_EEPROM_EXT_LNA_5GHZ) ? 1 : 0); + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM NIC config: HW radio cntl=%d, Tx AGC cntl=%d, ext LNA gains=%d/%d\n", + device_get_nameunit(sc->dev), + sc->hw_radio_cntl, sc->tx_agc_cntl, sc->ext_lna_2ghz, sc->ext_lna_5ghz); + + /* read country code */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_COUNTRY); + + sc->country_2ghz = (val >> 8) & 0xff; + sc->country_5ghz = (val & 0xff); + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM country code=%d/%d\n", + device_get_nameunit(sc->dev), sc->country_2ghz, sc->country_5ghz); + + /* read RF frequency offset */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_RF_FREQ_OFF); + + if ((val & 0xff) != 0xff) + { + sc->rf_freq_off = (val & 0xff); + } + else + { + printf("%s: invalid EEPROM RF freq offset\n", + device_get_nameunit(sc->dev)); + + sc->rf_freq_off = 0; + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM freq offset=0x%02x\n", + device_get_nameunit(sc->dev), sc->rf_freq_off); + + /* read LEDs operating mode */ + + if (((val >> 8) & 0xff) != 0xff) + { + sc->led_cntl = ((val >> 8) & 0xff); + sc->led_off[0] = rt2860_io_eeprom_read(sc, RT2860_EEPROM_LED1_OFF); + sc->led_off[1] = rt2860_io_eeprom_read(sc, RT2860_EEPROM_LED2_OFF); + sc->led_off[2] = rt2860_io_eeprom_read(sc, RT2860_EEPROM_LED3_OFF); + } + else + { + printf("%s: invalid EEPROM LED settings\n", + device_get_nameunit(sc->dev)); + + sc->led_cntl = RT2860_EEPROM_LED_CNTL_DEFAULT; + sc->led_off[0] = RT2860_EEPROM_LED1_OFF_DEFAULT; + sc->led_off[1] = RT2860_EEPROM_LED2_OFF_DEFAULT; + sc->led_off[2] = RT2860_EEPROM_LED3_OFF_DEFAULT; + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM LED cntl=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", + device_get_nameunit(sc->dev), sc->led_cntl, + sc->led_off[0], sc->led_off[1], sc->led_off[2]); + + /* read RSSI offsets and LNA gains */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_LNA_GAIN); + if ((sc->mac_rev & 0xffff0000) >= 0x30710000) + sc->lna_gain[0] = RT3090_DEF_LNA; + else /* channel group 0 */ + sc->lna_gain[0] = val & 0xff; + + sc->lna_gain[1] = (val >> 8) & 0xff; + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_RSSI_OFF_2GHZ_BASE); + + sc->rssi_off_2ghz[0] = (val & 0xff); + sc->rssi_off_2ghz[1] = (val >> 8) & 0xff; + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_RSSI_OFF_2GHZ_BASE + 2); + + if ((sc->mac_rev & 0xffff0000) >= 0x30710000) { + /* + * On RT3090 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; + } else + sc->rssi_off_2ghz[2] = val & 0xff; /* Ant C */ + sc->lna_gain[2] = (val >> 8) & 0xff; + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_RSSI_OFF_5GHZ_BASE); + + sc->rssi_off_5ghz[0] = (val & 0xff); + sc->rssi_off_5ghz[1] = (val >> 8) & 0xff; + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_RSSI_OFF_5GHZ_BASE + 2); + + sc->rssi_off_5ghz[2] = (val & 0xff); + sc->lna_gain[3] = (val >> 8) & 0xff; + + for (i = 2; i < RT2860_SOFTC_LNA_GAIN_COUNT; i++) + { + if (sc->lna_gain[i] == 0x00 || sc->lna_gain[i] == (int8_t) 0xff) + { + printf("%s: invalid EEPROM LNA gain #%d: 0x%02x\n", + device_get_nameunit(sc->dev), i, sc->lna_gain[i]); + + sc->lna_gain[i] = sc->lna_gain[1]; + } + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM LNA gains=0x%02x/0x%02x/0x%02x/0x%02x\n", + device_get_nameunit(sc->dev), + sc->lna_gain[0], sc->lna_gain[1], sc->lna_gain[2], sc->lna_gain[3]); + + for (i = 0; i < RT2860_SOFTC_RSSI_OFF_COUNT; i++) + { + if (sc->rssi_off_2ghz[i] < RT2860_EEPROM_RSSI_OFF_MIN || + sc->rssi_off_2ghz[i] > RT2860_EEPROM_RSSI_OFF_MAX) + { + printf("%s: invalid EEPROM RSSI offset #%d (2GHz): 0x%02x\n", + device_get_nameunit(sc->dev), i, sc->rssi_off_2ghz[i]); + + sc->rssi_off_2ghz[i] = 0; + } + + if (sc->rssi_off_5ghz[i] < RT2860_EEPROM_RSSI_OFF_MIN || + sc->rssi_off_5ghz[i] > RT2860_EEPROM_RSSI_OFF_MAX) + { + printf("%s: invalid EEPROM RSSI offset #%d (5GHz): 0x%02x\n", + device_get_nameunit(sc->dev), i, sc->rssi_off_5ghz[i]); + + sc->rssi_off_5ghz[i] = 0; + } + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM RSSI offsets 2GHz=%d/%d/%d\n", + device_get_nameunit(sc->dev), + sc->rssi_off_2ghz[0], sc->rssi_off_2ghz[1], sc->rssi_off_2ghz[2]); + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM RSSI offsets 5GHz=%d/%d/%d\n", + device_get_nameunit(sc->dev), + sc->rssi_off_5ghz[0], sc->rssi_off_5ghz[1], sc->rssi_off_5ghz[2]); + + /* read Tx power settings for 2GHz channels */ + + for (i = 0; i < 14; i += 2) + { + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TXPOW1_2GHZ_BASE + i / 2); + + sc->txpow1[i + 0] = (int8_t) (val & 0xff); + sc->txpow1[i + 1] = (int8_t) (val >> 8); + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TXPOW2_2GHZ_BASE + i / 2); + + sc->txpow2[i + 0] = (int8_t) (val & 0xff); + sc->txpow2[i + 1] = (int8_t) (val >> 8); + } + + /* read Tx power settings for 5GHz channels */ + + for (; i < RT2860_SOFTC_TXPOW_COUNT; i += 2) + { + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TXPOW1_5GHZ_BASE + i / 2); + + sc->txpow1[i + 0] = (int8_t) (val & 0xff); + sc->txpow1[i + 1] = (int8_t) (val >> 8); + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TXPOW2_5GHZ_BASE + i / 2); + + sc->txpow2[i + 0] = (int8_t) (val & 0xff); + sc->txpow2[i + 1] = (int8_t) (val >> 8); + } + + /* fix broken Tx power settings */ + + for (i = 0; i < 14; i++) + { + if (sc->txpow1[i] < RT2860_EEPROM_TXPOW_2GHZ_MIN || + sc->txpow1[i] > RT2860_EEPROM_TXPOW_2GHZ_MAX) + { + printf("%s: invalid EEPROM Tx power1 #%d (2GHz): 0x%02x\n", + device_get_nameunit(sc->dev), i, sc->txpow1[i]); + + sc->txpow1[i] = RT2860_EEPROM_TXPOW_2GHZ_DEFAULT; + } + + if (sc->txpow2[i] < RT2860_EEPROM_TXPOW_2GHZ_MIN || + sc->txpow2[i] > RT2860_EEPROM_TXPOW_2GHZ_MAX) + { + printf("%s: invalid EEPROM Tx power2 #%d (2GHz): 0x%02x\n", + device_get_nameunit(sc->dev), i, sc->txpow2[i]); + + sc->txpow2[i] = RT2860_EEPROM_TXPOW_2GHZ_DEFAULT; + } + } + + for (; i < RT2860_SOFTC_TXPOW_COUNT; i++) + { + if (sc->txpow1[i] < RT2860_EEPROM_TXPOW_5GHZ_MIN || + sc->txpow1[i] > RT2860_EEPROM_TXPOW_5GHZ_MAX) + { + printf("%s: invalid EEPROM Tx power1 #%d (5GHz): 0x%02x\n", + device_get_nameunit(sc->dev), i, sc->txpow1[i]); + + sc->txpow1[i] = RT2860_EEPROM_TXPOW_5GHZ_DEFAULT; + } + + if (sc->txpow2[i] < RT2860_EEPROM_TXPOW_5GHZ_MIN || + sc->txpow2[i] > RT2860_EEPROM_TXPOW_5GHZ_MAX) + { + printf("%s: invalid EEPROM Tx power2 #%d (5GHz): 0x%02x\n", + device_get_nameunit(sc->dev), i, sc->txpow2[i]); + + sc->txpow2[i] = RT2860_EEPROM_TXPOW_5GHZ_DEFAULT; + } + } + + /* read Tx power per rate deltas */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TXPOW_RATE_DELTA); + + sc->txpow_rate_delta_2ghz = 0; + sc->txpow_rate_delta_5ghz = 0; + + if ((val & 0xff) != 0xff) + { + if (val & 0x80) + sc->txpow_rate_delta_2ghz = (val & 0xf); + + if (!(val & 0x40)) + sc->txpow_rate_delta_2ghz = -sc->txpow_rate_delta_2ghz; + } + + val >>= 8; + + if ((val & 0xff) != 0xff) + { + if (val & 0x80) + sc->txpow_rate_delta_5ghz = (val & 0xf); + + if (!(val & 0x40)) + sc->txpow_rate_delta_5ghz = -sc->txpow_rate_delta_5ghz; + } + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM Tx power per rate deltas=%d(2MHz), %d(5MHz)\n", + device_get_nameunit(sc->dev), + sc->txpow_rate_delta_2ghz, sc->txpow_rate_delta_5ghz); + + /* read Tx power per rate */ + + for (i = 0; i < RT2860_SOFTC_TXPOW_RATE_COUNT; i++) + { + rt2860_io_eeprom_read_multi(sc, RT2860_EEPROM_TXPOW_RATE_BASE + i * sizeof(uint32_t), + &tmp, sizeof(uint32_t)); + + sc->txpow_rate_20mhz[i] = tmp; + sc->txpow_rate_40mhz_2ghz[i] = + rt2860_read_eeprom_txpow_rate_add_delta(tmp, sc->txpow_rate_delta_2ghz); + sc->txpow_rate_40mhz_5ghz[i] = + rt2860_read_eeprom_txpow_rate_add_delta(tmp, sc->txpow_rate_delta_5ghz); + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM Tx power per rate #%d=0x%08x(20MHz), 0x%08x(40MHz/2GHz), 0x%08x(40MHz/5GHz)\n", + device_get_nameunit(sc->dev), i, + sc->txpow_rate_20mhz[i], sc->txpow_rate_40mhz_2ghz[i], sc->txpow_rate_40mhz_5ghz[i]); + } + + if (sc->tx_agc_cntl) + sc->tx_agc_cntl_2ghz = sc->tx_agc_cntl_5ghz = 1; + + /* read factory-calibrated samples for temperature compensation */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_2GHZ_BASE); + + sc->tssi_2ghz[0] = (val & 0xff); /* [-4] */ + sc->tssi_2ghz[1] = (val >> 8); /* [-3] */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_2GHZ_BASE + 2); + + sc->tssi_2ghz[2] = (val & 0xff); /* [-2] */ + sc->tssi_2ghz[3] = (val >> 8); /* [-1] */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_2GHZ_BASE + 2 * 2); + + sc->tssi_2ghz[4] = (val & 0xff); /* [0] */ + sc->tssi_2ghz[5] = (val >> 8); /* [+1] */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_2GHZ_BASE + 3 * 2); + + sc->tssi_2ghz[6] = (val & 0xff); /* [+2] */ + sc->tssi_2ghz[7] = (val >> 8); /* [+3] */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_2GHZ_BASE + 4 * 2); + + sc->tssi_2ghz[8] = (val & 0xff); /* [+4] */ + sc->tssi_step_2ghz = (val >> 8); + + if (sc->tssi_2ghz[4] == 0xff) + sc->tx_agc_cntl_2ghz = 0; + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM TSSI 2GHz: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, " + "0x%02x, 0x%02x, step=%d\n", + device_get_nameunit(sc->dev), + sc->tssi_2ghz[0], sc->tssi_2ghz[1], sc->tssi_2ghz[2], + sc->tssi_2ghz[3], sc->tssi_2ghz[4], sc->tssi_2ghz[5], + sc->tssi_2ghz[6], sc->tssi_2ghz[7], sc->tssi_2ghz[8], + sc->tssi_step_2ghz); + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_5GHZ_BASE); + + sc->tssi_5ghz[0] = (val & 0xff); /* [-4] */ + sc->tssi_5ghz[1] = (val >> 8); /* [-3] */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_5GHZ_BASE + 2); + + sc->tssi_5ghz[2] = (val & 0xff); /* [-2] */ + sc->tssi_5ghz[3] = (val >> 8); /* [-1] */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_5GHZ_BASE + 2 * 2); + + sc->tssi_5ghz[4] = (val & 0xff); /* [0] */ + sc->tssi_5ghz[5] = (val >> 8); /* [+1] */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_5GHZ_BASE + 3 * 2); + + sc->tssi_5ghz[6] = (val & 0xff); /* [+2] */ + sc->tssi_5ghz[7] = (val >> 8); /* [+3] */ + + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_TSSI_5GHZ_BASE + 4 * 2); + + sc->tssi_5ghz[8] = (val & 0xff); /* [+4] */ + sc->tssi_step_5ghz = (val >> 8); + + if (sc->tssi_5ghz[4] == 0xff) + sc->tx_agc_cntl_5ghz = 0; + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM TSSI 5GHz: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, " + "0x%02x, 0x%02x, step=%d\n", + device_get_nameunit(sc->dev), + sc->tssi_5ghz[0], sc->tssi_5ghz[1], sc->tssi_5ghz[2], + sc->tssi_5ghz[3], sc->tssi_5ghz[4], sc->tssi_5ghz[5], + sc->tssi_5ghz[6], sc->tssi_5ghz[7], sc->tssi_5ghz[8], + sc->tssi_step_5ghz); + + /* read default BBP settings */ + + rt2860_io_eeprom_read_multi(sc, RT2860_EEPROM_BBP_BASE, + sc->bbp_eeprom, RT2860_SOFTC_BBP_EEPROM_COUNT * 2); + + if ((sc->mac_rev & 0xffff0000) >= 0x30710000) { + /* read vendor RF settings */ + rt2860_io_eeprom_read_multi(sc, RT3071_EEPROM_RF_BASE, sc->rf, 10 * 2); + } + + /* read powersave level */ + val = rt2860_io_eeprom_read(sc, RT2860_EEPROM_POWERSAVE_LEVEL); + + sc->powersave_level = val & 0xff; + + if ((sc->powersave_level & 0xff) == 0xff) + printf("%s: invalid EEPROM powersave level\n", + device_get_nameunit(sc->dev)); + + RT2860_DPRINTF(sc, RT2860_DEBUG_EEPROM, + "%s: EEPROM powersave level=0x%02x\n", + device_get_nameunit(sc->dev), sc->powersave_level); +} + +/* + * rt2860_read_eeprom_txpow_rate_add_delta + */ +uint32_t rt2860_read_eeprom_txpow_rate_add_delta(uint32_t txpow_rate, + int8_t delta) +{ + int8_t b4; + int i; + + for (i = 0; i < 8; i++) + { + b4 = txpow_rate & 0xf; + b4 += delta; + + if (b4 < 0) + b4 = 0; + else if (b4 > 0xf) + b4 = 0xf; + + txpow_rate = (txpow_rate >> 4) | (b4 << 28); + } + + return txpow_rate; +} Index: sys/dev/rt2860/rt2860_reg.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_reg.h @@ -0,0 +1,560 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_REG_H_ +#define _RT2860_REG_H_ + +#define RT2860_REG_PCI_CFG 0x0000 +#define RT2860_REG_EEPROM_CSR 0x0004 +#define RT2860_REG_PCI_MCU_CSR 0x0008 +#define RT2860_REG_PCI_SYS_CSR 0x000c +#define RT2860_REG_PCIE_JTAG 0x0010 + +#define RT2860_REG_SCHDMA_INT_STATUS 0x0200 +#define RT2860_REG_SCHDMA_INT_MASK 0x0204 +#define RT2860_REG_SCHDMA_WPDMA_GLO_CFG 0x0208 +#define RT2860_REG_SCHDMA_WPDMA_RST_IDX 0x020c +#define RT2860_REG_SCHDMA_DELAY_INT_CFG 0x0210 +#define RT2860_REG_SCHDMA_WMM_AIFSN_CFG 0x0214 +#define RT2860_REG_SCHDMA_WMM_CWMIN_CFG 0x0218 +#define RT2860_REG_SCHDMA_WMM_CWMAX_CFG 0x021c +#define RT2860_REG_SCHDMA_WMM_TXOP0_CFG 0x0220 +#define RT2860_REG_SCHDMA_WMM_TXOP1_CFG 0x0224 +#define RT2860_REG_SCHDMA_GPIO_CTRL_CFG 0x0228 +#define RT2860_REG_SCHDMA_RX_BASE_PTR 0x0290 +#define RT2860_REG_SCHDMA_RX_MAX_CNT 0x0294 +#define RT2860_REG_SCHDMA_RX_CALC_IDX 0x0298 +#define RT2860_REG_SCHDMA_RX_DRX_IDX 0x029c +#define RT2860_REG_SCHDMA_TX_BASE_PTR(qid) (0x0230 + (qid) * 16) +#define RT2860_REG_SCHDMA_TX_MAX_CNT(qid) (0x0234 + (qid) * 16) +#define RT2860_REG_SCHDMA_TX_CTX_IDX(qid) (0x0238 + (qid) * 16) +#define RT2860_REG_SCHDMA_TX_DTX_IDX(qid) (0x023c + (qid) * 16) +#define RT2860_REG_SCHDMA_US_CYC_CNT 0x02a4 + +#define RT2860_REG_PBF_SYS_CTRL 0x0400 +#define RT2860_REG_PBF_HOST_CMD 0x0404 +#define RT2860_REG_PBF_CFG 0x0408 +#define RT2860_REG_PBF_MAX_PCNT 0x040c +#define RT2860_REG_PBF_BUF_CTRL 0x0410 +#define RT2860_REG_PBF_MCU_INT_STA 0x0414 +#define RT2860_REG_PBF_MCU_INT_ENA 0x0418 +#define RT2860_REG_PBF_TX0Q_IO 0x041c +#define RT2860_REG_PBF_TX1Q_IO 0x0420 +#define RT2860_REG_PBF_TX2Q_IO 0x0424 +#define RT2860_REG_PBF_RX0Q_IO 0x0428 +#define RT2860_REG_PBF_BCN_OFFSET0 0x042c +#define RT2860_REG_PBF_BCN_OFFSET1 0x0430 +#define RT2860_REG_PBF_TXRXQ_STA 0x0434 +#define RT2860_REG_PBF_TXRXQ_PCNT 0x0438 +#define RT2860_REG_PBF_DBG 0x043c +#define RT2860_REG_PBF_CAP_CTRL 0x0440 + +#define RT2872_REG_RF_CSR_CFG 0x500 +#define RT2872_REG_RF_SETTING 0x504 +#define RT2872_REG_RF_TEST_CONTROL 0x508 + +#define RT2860_REG_MAC_CSR0 0x1000 +#define RT2860_REG_SYS_CTRL 0x1004 +#define RT2860_REG_ADDR_DW0 0x1008 +#define RT2860_REG_ADDR_DW1 0x100c +#define RT2860_REG_BSSID_DW0 0x1010 +#define RT2860_REG_BSSID_DW1 0x1014 +#define RT2860_REG_MAX_LEN_CFG 0x1018 +#define RT2860_REG_BBP_CSR_CFG 0x101c +#define RT2860_REG_RF_CSR_CFG0 0x1020 +#define RT2860_REG_LED_CFG 0x102c +#define RT2860_REG_AMPDU_MAX_LEN_20M1S 0x1030 +#define RT2860_REG_AMPDU_MAX_LEN_20M2S 0x1034 +#define RT2860_REG_AMPDU_MAX_LEN_40M1S 0x1038 +#define RT2860_REG_AMPDU_MAX_LEN_40M2S 0x103c +#define RT2860_REG_AMPDU_BA_WINSIZE 0x1040 + +#define RT2860_REG_XIFS_TIME_CFG 0x1100 +#define RT2860_REG_BKOFF_SLOT_CFG 0x1104 +#define RT2860_REG_NAV_TIME_CFG 0x1108 +#define RT2860_REG_CH_TIME_CFG 0x110c +#define RT2860_REG_PBF_LIFE_TIMER 0x1110 +#define RT2860_REG_BCN_TIME_CFG 0x1114 +#define RT2860_REG_TBTT_SYNC_CFG 0x1118 +#define RT2860_REG_TSF_TIMER_DW0 0x111c +#define RT2860_REG_TSF_TIMER_DW1 0x1120 +#define RT2860_REG_TBTT_TIMER 0x1124 +#define RT2860_REG_INT_TIMER 0x1128 +#define RT2860_REG_INT_TIMER_EN 0x112c +#define RT2860_REG_CH_IDLE_STA 0x1130 + +#define RT2860_REG_STATUS_CFG 0x1200 +#define RT2860_REG_PWR_PIN_CFG 0x1204 +#define RT2860_REG_AUTO_WAKEUP_CFG 0x1208 + +#define RT2860_REG_TX_EDCA_AC_CFG(aci) (0x1300 + (aci) * 4) +#define RT2860_REG_TX_EDCA_TID_AC_MAP 0x1310 +#define RT2860_REG_TX_PWR_CFG(ridx) (0x1314 + (ridx) * 4) +#define RT2860_REG_TX_PIN_CFG 0x1328 +#define RT2860_REG_TX_BAND_CFG 0x132c +#define RT2860_REG_TX_SW_CFG0 0x1330 +#define RT2860_REG_TX_SW_CFG1 0x1334 +#define RT2860_REG_TX_SW_CFG2 0x1338 +#define RT2860_REG_TX_TXOP_THRES_CFG 0x133c +#define RT2860_REG_TX_TXOP_CTRL_CFG 0x1340 +#define RT2860_REG_TX_RTS_CFG 0x1344 +#define RT2860_REG_TX_TIMEOUT_CFG 0x1348 +#define RT2860_REG_TX_RTY_CFG 0x134c +#define RT2860_REG_TX_LINK_CFG 0x1350 +#define RT2860_REG_TX_HT_FBK_CFG0 0x1354 +#define RT2860_REG_TX_HT_FBK_CFG1 0x1358 +#define RT2860_REG_TX_LG_FBK_CFG0 0x135c +#define RT2860_REG_TX_LG_FBK_CFG1 0x1360 +#define RT2860_REG_TX_CCK_PROT_CFG 0x1364 +#define RT2860_REG_TX_OFDM_PROT_CFG 0x1368 +#define RT2860_REG_TX_MM20_PROT_CFG 0x136c +#define RT2860_REG_TX_MM40_PROT_CFG 0x1370 +#define RT2860_REG_TX_GF20_PROT_CFG 0x1374 +#define RT2860_REG_TX_GF40_PROT_CFG 0x1378 +#define RT2860_REG_TX_EXP_CTS_TIME 0x137c +#define RT2860_REG_TX_EXP_ACK_TIME 0x1380 + +#define RT2860_REG_RX_FILTER_CFG 0x1400 +#define RT2860_REG_AUTO_RSP_CFG 0x1404 +#define RT2860_REG_LEGACY_BASIC_RATE 0x1408 +#define RT2860_REG_HT_BASIC_RATE 0x140c +#define RT2860_REG_HT_CTRL_CFG 0x1410 +#define RT2860_REG_SIFS_COST_CFG 0x1414 +#define RT2860_REG_RX_PARSER_CFG 0x1418 + +#define RT2860_REG_TX_SEC_CNT0 0x1500 +#define RT2860_REG_RX_SEC_CNT0 0x1504 +#define RT2860_REG_CCMP_FC_MUTE 0x1508 + +#define RT2860_REG_HCCAPSMP_TXOP_HLDR_ADDR0 0x1600 +#define RT2860_REG_HCCAPSMP_TXOP_HLDR_ADDR1 0x1604 +#define RT2860_REG_HCCAPSMP_TXOP_HLDR_ET 0x1608 +#define RT2860_REG_HCCAPSMP_QOS_CFPOLL_RA_DW0 0x160c +#define RT2860_REG_HCCAPSMP_QOS_CFPOLL_A1_DW1 0x1610 +#define RT2860_REG_HCCAPSMP_QOS_CFPOLL_QC 0x1614 + +#define RT2860_REG_RX_STA_CNT0 0x1700 +#define RT2860_REG_RX_STA_CNT1 0x1704 +#define RT2860_REG_RX_STA_CNT2 0x1708 +#define RT2860_REG_TX_STA_CNT0 0x170c +#define RT2860_REG_TX_STA_CNT1 0x1710 +#define RT2860_REG_TX_STA_CNT2 0x1714 +#define RT2860_REG_TX_STA_FIFO 0x1718 +#define RT2860_REG_TX_AGG_CNT 0x171c +#define RT2860_REG_TX_AGG_CNT0 0x1720 +#define RT2860_REG_TX_AGG_CNT1 0x1724 +#define RT2860_REG_TX_AGG_CNT2 0x1728 +#define RT2860_REG_TX_AGG_CNT3 0x172c +#define RT2860_REG_TX_AGG_CNT4 0x1730 +#define RT2860_REG_TX_AGG_CNT5 0x1734 +#define RT2860_REG_TX_AGG_CNT6 0x1738 +#define RT2860_REG_TX_AGG_CNT7 0x173c +#define RT2860_REG_TXRX_MPDU_DEN_CNT 0x1740 + +#define RT2860_REG_WCID(wcid) (0x1800 + (wcid) * 8) +#define RT2860_REG_PKEY(wcid) (0x4000 + (wcid) * 32) +#define RT2860_REG_IVEIV(wcid) (0x6000 + (wcid) * 8) +#define RT2860_REG_WCID_ATTR(wcid) (0x6800 + (wcid) * 4) +#define RT2860_REG_SKEY(vap, kidx) (0x6c00 + ((vap) * 4 + (kidx)) * 32) +#define RT2860_REG_SKEY_MODE(vap) (0x7000 + ((vap) / 2) * 4) +#define RT2860_REG_SKEY_MODE_0_7 0x7000 + +#define RT2860_REG_MCU_UCODE_BASE 0x2000 + +#define RT2860_REG_H2M_HOST_CMD 0x0404 +#define RT2860_REG_H2M_MAILBOX 0x7010 +#define RT2860_REG_H2M_MAILBOX_CID 0x7014 +#define RT2860_REG_H2M_MAILBOX_STATUS 0x701c +#define RT2860_REG_H2M_MAILBOX_BBP_AGENT 0x7028 + +#define RT2860_REG_BEACON_BASE(vap) (0x7800 + (vap) * 512) + +/* RT3070 registers */ +#define RT3070_RF_CSR_CFG 0x0500 +#define RT3070_EFUSE_CTRL 0x0580 +#define RT3070_EFUSE_DATA0 0x0590 +#define RT3070_EFUSE_DATA1 0x0594 +#define RT3070_EFUSE_DATA2 0x0598 +#define RT3070_EFUSE_DATA3 0x059c +#define RT3090_OSC_CTRL 0x05a4 +#define RT3070_LDO_CFG0 0x05d4 +#define RT3070_GPIO_SWITCH 0x05dc + +#define RT3090_AUX_CTRL 0x010c +#define RT3070_OPT_14 0x0114 + +/* possible flags for register RF_CSR_CFG */ +#define RT3070_RF_KICK (1 << 17) +#define RT3070_RF_WRITE (1 << 16) + +/* possible flags for register EFUSE_CTRL */ +#define RT3070_SEL_EFUSE (1 << 31) +#define RT3070_EFSROM_KICK (1 << 30) +#define RT3070_EFSROM_AIN_MASK 0x03ff0000 +#define RT3070_EFSROM_AIN_SHIFT 16 +#define RT3070_EFSROM_MODE_MASK 0x000000c0 +#define RT3070_EFUSE_AOUT_MASK 0x0000003f + +/* possible flags for RT3020 RF register 1 */ +#define RT3070_RF_BLOCK (1 << 0) +#define RT3070_RX0_PD (1 << 2) +#define RT3070_TX0_PD (1 << 3) +#define RT3070_RX1_PD (1 << 4) +#define RT3070_TX1_PD (1 << 5) +#define RT3070_RX2_PD (1 << 6) +#define RT3070_TX2_PD (1 << 7) + +/* possible flags for RT3020 RF register 1 */ +#define RT3070_RF_BLOCK (1 << 0) +#define RT3070_RX0_PD (1 << 2) +#define RT3070_TX0_PD (1 << 3) +#define RT3070_RX1_PD (1 << 4) +#define RT3070_TX1_PD (1 << 5) +#define RT3070_RX2_PD (1 << 6) +#define RT3070_TX2_PD (1 << 7) + +/* possible flags for RT3020 RF register 7 */ +#define RT3070_TUNE (1 << 0) + +/* possible flags for RT3020 RF register 15 */ +#define RT3070_TX_LO2 (1 << 3) + +/* possible flags for RT3020 RF register 17 */ +#define RT3070_TX_LO1 (1 << 3) + +/* possible flags for RT3020 RF register 20 */ +#define RT3070_RX_LO1 (1 << 3) + +/* possible flags for RT3020 RF register 21 */ +#define RT3070_RX_LO2 (1 << 3) +#define RT3070_RX_CTB (1 << 7) + +/* possible flags for RT3020 RF register 22 */ +#define RT3070_BB_LOOPBACK (1 << 0) + +/* possible flags for RT3053 RF register 1 */ +#define RT3593_VCO (1 << 0) + +/* possible flags for RT3053 RF register 2 */ +#define RT3593_RESCAL (1 << 7) + +/* possible flags for RT3053 RF register 3 */ +#define RT3593_VCOCAL (1 << 7) + +/* possible flags for RT3053 RF register 6 */ +#define RT3593_VCO_IC (1 << 6) + +/* possible flags for RT3053 RF register 20 */ +#define RT3593_LDO_PLL_VC_MASK 0x0e +#define RT3593_LDO_RF_VC_MASK 0xe0 + +/* possible flags for RT3053 RF register 22 */ +#define RT3593_CP_IC_MASK 0xe0 +#define RT3593_CP_IC_SHIFT 5 + +/* possible flags for RT3053 RF register 46 */ +#define RT3593_RX_CTB (1 << 5) + +#define RT3090_DEF_LNA 10 + + +#define RT2860_REG_RF_R1 0 +#define RT2860_REG_RF_R2 1 +#define RT2860_REG_RF_R3 2 +#define RT2860_REG_RF_R4 3 + +/* + * RT2860_REG_EEPROM_CSR flags + */ +#define RT2860_REG_EERL (1 << 7) +#define RT2860_REG_EEDO (1 << 3) +#define RT2860_REG_EEDI (1 << 2) +#define RT2860_REG_EECS (1 << 1) +#define RT2860_REG_EESK (1 << 0) +#define RT2860_REG_EEOP_READ 0x6 + +/* + * RT2860_REG_SCHDMA_INT_STATUS + * RT2860_REG_SCHDMA_INT_MASK flags + */ +#define RT2860_REG_INT_TX_COHERENT (1 << 17) +#define RT2860_REG_INT_RX_COHERENT (1 << 16) +#define RT2860_REG_INT_GP_TIMER (1 << 15) +#define RT2860_REG_INT_AUTO_WAKEUP (1 << 14) +#define RT2860_REG_INT_FIFO_STA_FULL (1 << 13) +#define RT2860_REG_INT_PRE_TBTT (1 << 12) +#define RT2860_REG_INT_TBTT (1 << 11) +#define RT2860_REG_INT_TXRX_COHERENT (1 << 10) +#define RT2860_REG_INT_MCU_CMD (1 << 9) +#define RT2860_REG_INT_TX_MGMT_DONE (1 << 8) +#define RT2860_REG_INT_TX_HCCA_DONE (1 << 7) +#define RT2860_REG_INT_TX_AC3_DONE (1 << 6) +#define RT2860_REG_INT_TX_AC2_DONE (1 << 5) +#define RT2860_REG_INT_TX_AC1_DONE (1 << 4) +#define RT2860_REG_INT_TX_AC0_DONE (1 << 3) +#define RT2860_REG_INT_RX_DONE (1 << 2) +#define RT2860_REG_INT_TX_DELAY_DONE (1 << 1) +#define RT2860_REG_INT_RX_DELAY_DONE (1 << 0) + +/* + * RT2860_REG_SCHDMA_WPDMA_GLO_CFG flags + */ +#define RT2860_REG_TX_WB_DDONE (1 << 6) +#define RT2860_REG_RX_DMA_BUSY (1 << 3) +#define RT2860_REG_RX_DMA_ENABLE (1 << 2) +#define RT2860_REG_TX_DMA_BUSY (1 << 1) +#define RT2860_REG_TX_DMA_ENABLE (1 << 0) +#define RT2860_REG_WPDMA_BT_SIZE_SHIFT 4 +#define RT2860_REG_WPDMA_BT_SIZE16 0 +#define RT2860_REG_WPDMA_BT_SIZE32 1 +#define RT2860_REG_WPDMA_BT_SIZE64 2 +#define RT2860_REG_WPDMA_BT_SIZE128 3 + +/* + * RT2860_REG_SCHDMA_WPDMA_RST_IDX flags + */ +#define RT2860_REG_RST_IDX_RX (1 << 16) +#define RT2860_REG_RST_IDX_TX_MGMT (1 << 5) +#define RT2860_REG_RST_IDX_TX_HCCA (1 << 4) +#define RT2860_REG_RST_IDX_TX_AC3 (1 << 3) +#define RT2860_REG_RST_IDX_TX_AC2 (1 << 2) +#define RT2860_REG_RST_IDX_TX_AC1 (1 << 1) +#define RT2860_REG_RST_IDX_TX_AC0 (1 << 0) + +/* + * RT2860_REG_SCHDMA_DELAY_INT_CFG flags + */ +#define RT2860_REG_INT_TX_DELAY_ENABLE (1 << 31) +#define RT2860_REG_INT_TX_MAX_PINT_SHIFT 24 +#define RT2860_REG_INT_TX_MAX_PINT_MASK 0x7 +#define RT2860_REG_INT_TX_MAX_PTIME_SHIFT 16 +#define RT2860_REG_INT_TX_MAX_PTIME_MASK 0x8 +#define RT2860_REG_INT_RX_DELAY_ENABLE (1 << 15) +#define RT2860_REG_INT_RX_MAX_PINT_SHIFT 8 +#define RT2860_REG_INT_RX_MAX_PINT_MASK 0x7 +#define RT2860_REG_INT_RX_MAX_PTIME_SHIFT 0 +#define RT2860_REG_INT_RX_MAX_PTIME_MASK 0x8 + +/* + * RT2860_REG_PBF_SYS_CTRL flags + */ +#define RT2860_REG_HST_PM_SEL (1 << 16) +#define RT2860_REG_MCU_READY (1 << 7) +#define RT2860_REG_MCU_RESET (1 << 0) + +/* + * RT2860_REG_PBF_TXRXQ_PCNT flags + */ +#define RT2860_REG_RXQ_PCNT_SHIFT 24 +#define RT2860_REG_RXQ_PCNT_MASK 0xff +#define RT2860_REG_TX2Q_PCNT_SHIFT 16 +#define RT2860_REG_TX2Q_PCNT_MASK 0xff +#define RT2860_REG_TX1Q_PCNT_SHIFT 8 +#define RT2860_REG_TX1Q_PCNT_MASK 0xff +#define RT2860_REG_TX0Q_PCNT_SHIFT 0 +#define RT2860_REG_TX0Q_PCNT_MASK 0xff + +/* + * RT2860_REG_SYS_CTRL flags + */ +#define RT2860_REG_RX_ENABLE (1 << 3) +#define RT2860_REG_TX_ENABLE (1 << 2) +#define RT2860_REG_BBP_HRST (1 << 1) +#define RT2860_REG_MAC_SRST (1 << 0) + +/* + * RT2872_REG_RF_CSR_CFG flags + */ +#define RT2872_REG_RF_CSR_BUSY (1 << 17) +#define RT2872_REG_RF_CSR_KICK (1 << 17) +#define RT2872_REG_RF_CSR_WRITE (1 << 16) +#define RT2872_REG_RF_ID_SHIFT 8 +#define RT2872_REG_RF_ID_MASK 0x1f +#define RT2872_REG_RF_VAL_SHIFT 0 +#define RT2872_REG_RF_VAL_MASK 0xff + +/* + * RT2860_REG_BBP_CSR_CFG flags + */ +#define RT2860_REG_BBP_RW_MODE_PARALLEL (1 << 19) +#define RT2860_REG_BBP_PAR_DUR (1 << 19) +#define RT2860_REG_BBP_CSR_BUSY (1 << 17) +#define RT2860_REG_BBP_CSR_KICK (1 << 17) +#define RT2860_REG_BBP_CSR_READ (1 << 16) +#define RT2860_REG_BBP_REG_SHIFT 8 +#define RT2860_REG_BBP_REG_MASK 0xff +#define RT2860_REG_BBP_VAL_SHIFT 0 +#define RT2860_REG_BBP_VAL_MASK 0xff + +/* + * RT2860_REG_RF_CSR_CFG0 flags + */ +#define RT2860_REG_RF_BUSY (1 << 31) + +/* + * RT2860_REG_BCN_TIME_CFG flags + */ +#define RT2860_REG_BCN_TX_ENABLE (1 << 20) +#define RT2860_REG_TBTT_TIMER_ENABLE (1 << 19) +#define RT2860_REG_TSF_TIMER_ENABLE (1 << 16) +#define RT2860_REG_TSF_SYNC_MODE_SHIFT 17 +#define RT2860_REG_TSF_SYNC_MODE_MASK 0x3 +#define RT2860_REG_TSF_SYNC_MODE_DISABLE 0 +#define RT2860_REG_TSF_SYNC_MODE_STA 1 +#define RT2860_REG_TSF_SYNC_MODE_IBSS 2 +#define RT2860_REG_TSF_SYNC_MODE_HOSTAP 3 + +/* + * RT2860_REG_STATUS_CFG flags + */ +#define RT2860_REG_STATUS_RX_BUSY (1 << 1) +#define RT2860_REG_STATUS_TX_BUSY (1 << 0) + +/* + * RT2860_REG_TX_PIN_CFG flags + */ +#define RT2860_REG_TRSW_ENABLE (1 << 18) +#define RT2860_REG_RFTR_ENABLE (1 << 16) +#define RT2860_REG_LNA_PE_G1_ENABLE (1 << 11) +#define RT2860_REG_LNA_PE_A1_ENABLE (1 << 10) +#define RT2860_REG_LNA_PE_G0_ENABLE (1 << 9) +#define RT2860_REG_LNA_PE_A0_ENABLE (1 << 8) +#define RT2860_REG_PA_PE_G1_ENABLE (1 << 3) +#define RT2860_REG_PA_PE_A1_ENABLE (1 << 2) +#define RT2860_REG_PA_PE_G0_ENABLE (1 << 1) +#define RT2860_REG_PA_PE_A0_ENABLE (1 << 0) + +/* + * RT2860_REG_TX_BAND_CFG flags + */ +#define RT2860_REG_TX_BAND_BG (1 << 2) +#define RT2860_REG_TX_BAND_A (1 << 1) +#define RT2860_REG_TX_BAND_HT40_ABOVE (1 << 0) +#define RT2860_REG_TX_BAND_HT40_BELOW (0 << 0) + +/* + * RT2860_REG_TX_RTS_CFG flags + */ +#define RT2860_REG_TX_RTS_THRESHOLD_SHIFT 8 +#define RT2860_REG_TX_RTS_THRESHOLD_MASK 0xffff + +/* + * RT2860_REG_TX_CCK_PROT_CFG + * RT2860_REG_TX_OFDM_PROT_CFG + * RT2860_REG_TX_MM20_PROT_CFG + * RT2860_REG_TX_MM40_PROT_CFG + * RT2860_REG_TX_GF20_PROT_CFG + * RT2860_REG_TX_GF40_PROT_CFG flags + */ +#define RT2860_REG_RTSTH_ENABLE (1 << 26) +#define RT2860_REG_TXOP_ALLOW_GF40 (1 << 25) +#define RT2860_REG_TXOP_ALLOW_GF20 (1 << 24) +#define RT2860_REG_TXOP_ALLOW_MM40 (1 << 23) +#define RT2860_REG_TXOP_ALLOW_MM20 (1 << 22) +#define RT2860_REG_TXOP_ALLOW_OFDM (1 << 21) +#define RT2860_REG_TXOP_ALLOW_CCK (1 << 20) +#define RT2860_REG_TXOP_ALLOW_ALL (0x3f << 20) +#define RT2860_REG_PROT_NAV_NONE (0 << 18) +#define RT2860_REG_PROT_NAV_SHORT (1 << 18) +#define RT2860_REG_PROT_NAV_LONG (2 << 18) +#define RT2860_REG_PROT_CTRL_NONE (0 << 16) +#define RT2860_REG_PROT_CTRL_RTS_CTS (1 << 16) +#define RT2860_REG_PROT_CTRL_CTS (2 << 16) +#define RT2860_REG_PROT_PHYMODE_SHIFT 14 +#define RT2860_REG_PROT_PHYMODE_MASK 0x3 +#define RT2860_REG_PROT_PHYMODE_CCK 0 +#define RT2860_REG_PROT_PHYMODE_OFDM 1 +#define RT2860_REG_PROT_MCS_SHIFT 0 +#define RT2860_REG_PROT_MCS_MASK 0x7f + +/* + * RT2860_REG_RX_FILTER_CFG flags + */ +#define RT2860_REG_RX_FILTER_DROP_CTRL_RSV (1 << 16) +#define RT2860_REG_RX_FILTER_DROP_BAR (1 << 15) +#define RT2860_REG_RX_FILTER_DROP_BA (1 << 14) +#define RT2860_REG_RX_FILTER_DROP_PSPOLL (1 << 13) +#define RT2860_REG_RX_FILTER_DROP_RTS (1 << 12) +#define RT2860_REG_RX_FILTER_DROP_CTS (1 << 11) +#define RT2860_REG_RX_FILTER_DROP_ACK (1 << 10) +#define RT2860_REG_RX_FILTER_DROP_CFEND (1 << 9) +#define RT2860_REG_RX_FILTER_DROP_CFACK (1 << 8) +#define RT2860_REG_RX_FILTER_DROP_DUPL (1 << 7) +#define RT2860_REG_RX_FILTER_DROP_BCAST (1 << 6) +#define RT2860_REG_RX_FILTER_DROP_MCAST (1 << 5) +#define RT2860_REG_RX_FILTER_DROP_VER_ERR (1 << 4) +#define RT2860_REG_RX_FILTER_DROP_NOT_MYBSS (1 << 3) +#define RT2860_REG_RX_FILTER_DROP_UC_NOME (1 << 2) +#define RT2860_REG_RX_FILTER_DROP_PHY_ERR (1 << 1) +#define RT2860_REG_RX_FILTER_DROP_CRC_ERR (1 << 0) + +/* + * RT2860_REG_AUTO_RSP_CFG flags + */ +#define RT2860_REG_CCK_SHORT_ENABLE (1 << 4) + +/* + * RT2860_REG_TX_STA_FIFO flags + */ +#define RT2860_REG_TX_STA_FIFO_MCS_SHIFT 16 +#define RT2860_REG_TX_STA_FIFO_MCS_MASK 0x7f +#define RT2860_REG_TX_STA_FIFO_WCID_SHIFT 8 +#define RT2860_REG_TX_STA_FIFO_WCID_MASK 0xff +#define RT2860_REG_TX_STA_FIFO_PID_SHIFT 1 +#define RT2860_REG_TX_STA_FIFO_PID_MASK 0xf +#define RT2860_REG_TX_STA_FIFO_ACK_REQ (1 << 7) +#define RT2860_REG_TX_STA_FIFO_AGG (1 << 6) +#define RT2860_REG_TX_STA_FIFO_TX_OK (1 << 5) +#define RT2860_REG_TX_STA_FIFO_VALID (1 << 0) + +/* + * RT2860_REG_WCID_ATTR flags + */ +#define RT2860_REG_VAP_SHIFT 4 +#define RT2860_REG_VAP_MASK 0x7 +#define RT2860_REG_CIPHER_MODE_SHIFT 1 +#define RT2860_REG_CIPHER_MODE_MASK 0x7 +#define RT2860_REG_CIPHER_MODE_NONE 0 +#define RT2860_REG_CIPHER_MODE_WEP40 1 +#define RT2860_REG_CIPHER_MODE_WEP104 2 +#define RT2860_REG_CIPHER_MODE_TKIP 3 +#define RT2860_REG_CIPHER_MODE_AES_CCMP 4 +#define RT2860_REG_CIPHER_MODE_CKIP40 5 +#define RT2860_REG_CIPHER_MODE_CKIP104 6 +#define RT2860_REG_CIPHER_MODE_CKIP128 7 +#define RT2860_REG_PKEY_ENABLE (1 << 0) + +/* + * RT2860_REG_H2M_MAILBOX flags + */ +#define RT2860_REG_H2M_BUSY (1 << 24) +#define RT2860_REG_H2M_TOKEN_POWERSAVE 1 +#define RT2860_REG_H2M_TOKEN_RADIOOFF 2 +#define RT2860_REG_H2M_TOKEN_WAKEUP 3 +#define RT2860_REG_H2M_TOKEN_NO_INTR 0xff + +/* + * RT2860_REG_H2M_MAILBOX_CID flags + */ +#define RT2860_REG_H2M_CID0_SHIFT 0 +#define RT2860_REG_H2M_CID1_SHIFT 8 +#define RT2860_REG_H2M_CID2_SHIFT 16 +#define RT2860_REG_H2M_CID3_SHIFT 24 +#define RT2860_REG_H2M_CID_MASK 0xff + +#endif /* #ifndef _RT2860_REG_H_ */ Index: sys/dev/rt2860/rt2860_rf.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_rf.h @@ -0,0 +1,49 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_RF_H_ +#define _RT2860_RF_H_ + +#include + +const char *rt2860_rf_name(int rf_rev); + +void rt2860_rf_select_chan_group(struct rt2860_softc *sc, + struct ieee80211_channel *c); + +void rt2860_rf_set_chan(struct rt2860_softc *sc, + struct ieee80211_channel *c); + +uint8_t rt3090_rf_read(struct rt2860_softc *sc, uint8_t reg); + +void rt3090_rf_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val); + +void rt3090_set_chan(struct rt2860_softc *sc, u_int chan); + +int rt3090_rf_init(struct rt2860_softc *sc); + +void rt3090_set_rx_antenna(struct rt2860_softc *, int); + +void rt3090_rf_wakeup(struct rt2860_softc *sc); + +int rt3090_filter_calib(struct rt2860_softc *sc, uint8_t init, uint8_t target, + uint8_t *val); + +void rt3090_rf_setup(struct rt2860_softc *sc); + +#endif /* #ifndef _RT2860_RF_H_ */ Index: sys/dev/rt2860/rt2860_rf.c =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_rf.c @@ -0,0 +1,1148 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +static void rt2872_rf_set_chan(struct rt2860_softc *sc, struct ieee80211_channel *c); + +extern uint8_t rt3052_rf_default[]; + +/* + * Static variables + */ + +static const struct rt2860_rf_prog +{ + uint8_t chan; + uint32_t r1, r2, r3, r4; +} rt2860_rf_2850[] = +{ + { 1, 0x98402ecc, 0x984c0786, 0x9816b455, 0x9800510b }, + { 2, 0x98402ecc, 0x984c0786, 0x98168a55, 0x9800519f }, + { 3, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800518b }, + { 4, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800519f }, + { 5, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800518b }, + { 6, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800519f }, + { 7, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800518b }, + { 8, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800519f }, + { 9, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800518b }, + { 10, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800519f }, + { 11, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800518b }, + { 12, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800519f }, + { 13, 0x98402ecc, 0x984c079e, 0x98168a55, 0x9800518b }, + { 14, 0x98402ecc, 0x984c07a2, 0x98168a55, 0x98005193 }, + { 36, 0x98402ecc, 0x984c099a, 0x98158a55, 0x980ed1a3 }, + { 38, 0x98402ecc, 0x984c099e, 0x98158a55, 0x980ed193 }, + { 40, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed183 }, + { 44, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed1a3 }, + { 46, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed18b }, + { 48, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed19b }, + { 52, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed193 }, + { 54, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed1a3 }, + { 56, 0x98402ec8, 0x984c068e, 0x98158a55, 0x980ed18b }, + { 60, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed183 }, + { 62, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed193 }, + { 64, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed1a3 }, + { 100, 0x98402ec8, 0x984c06b2, 0x98178a55, 0x980ed783 }, + { 102, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed793 }, + { 104, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed1a3 }, + { 108, 0x98402ecc, 0x985c0a32, 0x98578a55, 0x980ed193 }, + { 110, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed183 }, + { 112, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed19b }, + { 116, 0x98402ecc, 0x984c0a3a, 0x98178a55, 0x980ed1a3 }, + { 118, 0x98402ecc, 0x984c0a3e, 0x98178a55, 0x980ed193 }, + { 120, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed183 }, + { 124, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed193 }, + { 126, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed15b }, + { 128, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed1a3 }, + { 132, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed18b }, + { 134, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed193 }, + { 136, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed19b }, + { 140, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed183 }, + { 149, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed1a7 }, + { 151, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed187 }, + { 153, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed18f }, + { 157, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed19f }, + { 159, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed1a7 }, + { 161, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed187 }, + { 165, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed197 }, + { 184, 0x95002ccc, 0x9500491e, 0x9509be55, 0x950c0a0b }, + { 188, 0x95002ccc, 0x95004922, 0x9509be55, 0x950c0a13 }, + { 192, 0x95002ccc, 0x95004926, 0x9509be55, 0x950c0a1b }, + { 196, 0x95002ccc, 0x9500492a, 0x9509be55, 0x950c0a23 }, + { 208, 0x95002ccc, 0x9500493a, 0x9509be55, 0x950c0a13 }, + { 212, 0x95002ccc, 0x9500493e, 0x9509be55, 0x950c0a1b }, + { 216, 0x95002ccc, 0x95004982, 0x9509be55, 0x950c0a23 }, +}; + +static const struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; +} rt2860_rf2850[] = { + { 1, 0x100bb3, 0x1301e1, 0x05a014, 0x001402 }, + { 2, 0x100bb3, 0x1301e1, 0x05a014, 0x001407 }, + { 3, 0x100bb3, 0x1301e2, 0x05a014, 0x001402 }, + { 4, 0x100bb3, 0x1301e2, 0x05a014, 0x001407 }, + { 5, 0x100bb3, 0x1301e3, 0x05a014, 0x001402 }, + { 6, 0x100bb3, 0x1301e3, 0x05a014, 0x001407 }, + { 7, 0x100bb3, 0x1301e4, 0x05a014, 0x001402 }, + { 8, 0x100bb3, 0x1301e4, 0x05a014, 0x001407 }, + { 9, 0x100bb3, 0x1301e5, 0x05a014, 0x001402 }, + { 10, 0x100bb3, 0x1301e5, 0x05a014, 0x001407 }, + { 11, 0x100bb3, 0x1301e6, 0x05a014, 0x001402 }, + { 12, 0x100bb3, 0x1301e6, 0x05a014, 0x001407 }, + { 13, 0x100bb3, 0x1301e7, 0x05a014, 0x001402 }, + { 14, 0x100bb3, 0x1301e8, 0x05a014, 0x001404 }, + { 36, 0x100bb3, 0x130266, 0x056014, 0x001408 }, + { 38, 0x100bb3, 0x130267, 0x056014, 0x001404 }, + { 40, 0x100bb2, 0x1301a0, 0x056014, 0x001400 }, + { 44, 0x100bb2, 0x1301a0, 0x056014, 0x001408 }, + { 46, 0x100bb2, 0x1301a1, 0x056014, 0x001402 }, + { 48, 0x100bb2, 0x1301a1, 0x056014, 0x001406 }, + { 52, 0x100bb2, 0x1301a2, 0x056014, 0x001404 }, + { 54, 0x100bb2, 0x1301a2, 0x056014, 0x001408 }, + { 56, 0x100bb2, 0x1301a3, 0x056014, 0x001402 }, + { 60, 0x100bb2, 0x1301a4, 0x056014, 0x001400 }, + { 62, 0x100bb2, 0x1301a4, 0x056014, 0x001404 }, + { 64, 0x100bb2, 0x1301a4, 0x056014, 0x001408 }, + { 100, 0x100bb2, 0x1301ac, 0x05e014, 0x001400 }, + { 102, 0x100bb2, 0x1701ac, 0x15e014, 0x001404 }, + { 104, 0x100bb2, 0x1701ac, 0x15e014, 0x001408 }, + { 108, 0x100bb3, 0x17028c, 0x15e014, 0x001404 }, + { 110, 0x100bb3, 0x13028d, 0x05e014, 0x001400 }, + { 112, 0x100bb3, 0x13028d, 0x05e014, 0x001406 }, + { 116, 0x100bb3, 0x13028e, 0x05e014, 0x001408 }, + { 118, 0x100bb3, 0x13028f, 0x05e014, 0x001404 }, + { 120, 0x100bb1, 0x1300e0, 0x05e014, 0x001400 }, + { 124, 0x100bb1, 0x1300e0, 0x05e014, 0x001404 }, + { 126, 0x100bb1, 0x1300e0, 0x05e014, 0x001406 }, + { 128, 0x100bb1, 0x1300e0, 0x05e014, 0x001408 }, + { 132, 0x100bb1, 0x1300e1, 0x05e014, 0x001402 }, + { 134, 0x100bb1, 0x1300e1, 0x05e014, 0x001404 }, + { 136, 0x100bb1, 0x1300e1, 0x05e014, 0x001406 }, + { 140, 0x100bb1, 0x1300e2, 0x05e014, 0x001400 }, + { 149, 0x100bb1, 0x1300e2, 0x05e014, 0x001409 }, + { 151, 0x100bb1, 0x1300e3, 0x05e014, 0x001401 }, + { 153, 0x100bb1, 0x1300e3, 0x05e014, 0x001403 }, + { 157, 0x100bb1, 0x1300e3, 0x05e014, 0x001407 }, + { 159, 0x100bb1, 0x1300e3, 0x05e014, 0x001409 }, + { 161, 0x100bb1, 0x1300e4, 0x05e014, 0x001401 }, + { 165, 0x100bb1, 0x1300e4, 0x05e014, 0x001405 }, + { 167, 0x100bb1, 0x1300f4, 0x05e014, 0x001407 }, + { 169, 0x100bb1, 0x1300f4, 0x05e014, 0x001409 }, + { 171, 0x100bb1, 0x1300f5, 0x05e014, 0x001401 }, + { 173, 0x100bb1, 0x1300f5, 0x05e014, 0x001403 } +}; + +static const struct rt2860_rf_fi3020 +{ + uint8_t channel, n, r, k; +} rt2860_rf_fi3020[] = +{ + /* 802.11g */ + {1, 241, 2, 2}, + {2, 241, 2, 7}, + {3, 242, 2, 2}, + {4, 242, 2, 7}, + {5, 243, 2, 2}, + {6, 243, 2, 7}, + {7, 244, 2, 2}, + {8, 244, 2, 7}, + {9, 245, 2, 2}, + {10, 245, 2, 7}, + {11, 246, 2, 2}, + {12, 246, 2, 7}, + {13, 247, 2, 2}, + {14, 248, 2, 4}, + + /* 802.11 UNI / HyperLan 2 */ + {36, 0x56, 0, 4}, + {38, 0x56, 0, 6}, + {40, 0x56, 0, 8}, + {44, 0x57, 0, 0}, + {46, 0x57, 0, 2}, + {48, 0x57, 0, 4}, + {52, 0x57, 0, 8}, + {54, 0x57, 0, 10}, + {56, 0x58, 0, 0}, + {60, 0x58, 0, 4}, + {62, 0x58, 0, 6}, + {64, 0x58, 0, 8}, + + /* 802.11 HyperLan 2 */ + {100, 0x5b, 0, 8}, + {102, 0x5b, 0, 10}, + {104, 0x5c, 0, 0}, + {108, 0x5c, 0, 4}, + {110, 0x5c, 0, 6}, + {112, 0x5c, 0, 8}, + {116, 0x5d, 0, 0}, + {118, 0x5d, 0, 2}, + {120, 0x5d, 0, 4}, + {124, 0x5d, 0, 8}, + {126, 0x5d, 0, 10}, + {128, 0x5e, 0, 0}, + {132, 0x5e, 0, 4}, + {134, 0x5e, 0, 6}, + {136, 0x5e, 0, 8}, + {140, 0x5f, 0, 0}, + + /* 802.11 UNII */ + {149, 0x5f, 0, 9}, + {151, 0x5f, 0, 11}, + {153, 0x60, 0, 1}, + {157, 0x60, 0, 5}, + {159, 0x60, 0, 7}, + {161, 0x60, 0, 9}, + {165, 0x61, 0, 1}, + {167, 0x61, 0, 3}, + {169, 0x61, 0, 5}, + {171, 0x61, 0, 7}, + {173, 0x61, 0, 9}, +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rt3090_def_rf[] = { + { 4, 0x40 }, + { 5, 0x03 }, + { 6, 0x02 }, + { 7, 0x70 }, + { 9, 0x0f }, + { 10, 0x41 }, + { 11, 0x21 }, + { 12, 0x7b }, + { 14, 0x90 }, + { 15, 0x58 }, + { 16, 0xb3 }, + { 17, 0x92 }, + { 18, 0x2c }, + { 19, 0x02 }, + { 20, 0xba }, + { 21, 0xdb }, + { 24, 0x16 }, + { 25, 0x01 }, + { 29, 0x1f } +}; + +struct { + uint8_t n, r, k; +} rt3090_freqs[] = { + { 0xf1, 2, 2 }, + { 0xf1, 2, 7 }, + { 0xf2, 2, 2 }, + { 0xf2, 2, 7 }, + { 0xf3, 2, 2 }, + { 0xf3, 2, 7 }, + { 0xf4, 2, 2 }, + { 0xf4, 2, 7 }, + { 0xf5, 2, 2 }, + { 0xf5, 2, 7 }, + { 0xf6, 2, 2 }, + { 0xf6, 2, 7 }, + { 0xf7, 2, 2 }, + { 0xf8, 2, 4 }, + { 0x56, 0, 4 }, + { 0x56, 0, 6 }, + { 0x56, 0, 8 }, + { 0x57, 0, 0 }, + { 0x57, 0, 2 }, + { 0x57, 0, 4 }, + { 0x57, 0, 8 }, + { 0x57, 0, 10 }, + { 0x58, 0, 0 }, + { 0x58, 0, 4 }, + { 0x58, 0, 6 }, + { 0x58, 0, 8 }, + { 0x5b, 0, 8 }, + { 0x5b, 0, 10 }, + { 0x5c, 0, 0 }, + { 0x5c, 0, 4 }, + { 0x5c, 0, 6 }, + { 0x5c, 0, 8 }, + { 0x5d, 0, 0 }, + { 0x5d, 0, 2 }, + { 0x5d, 0, 4 }, + { 0x5d, 0, 8 }, + { 0x5d, 0, 10 }, + { 0x5e, 0, 0 }, + { 0x5e, 0, 4 }, + { 0x5e, 0, 6 }, + { 0x5e, 0, 8 }, + { 0x5f, 0, 0 }, + { 0x5f, 0, 9 }, + { 0x5f, 0, 11 }, + { 0x60, 0, 1 }, + { 0x60, 0, 5 }, + { 0x60, 0, 7 }, + { 0x60, 0, 9 }, + { 0x61, 0, 1 }, + { 0x61, 0, 3 }, + { 0x61, 0, 5 }, + { 0x61, 0, 7 }, + { 0x61, 0, 9 } +}; + + + +uint8_t +rt3090_rf_read(struct rt2860_softc *sc, uint8_t reg) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(rt2860_io_mac_read(sc, RT3070_RF_CSR_CFG) & RT3070_RF_KICK)) + break; + DELAY(1); + } + if (ntries == 100) { + device_printf(sc->dev, "could not read RF register\n"); + return 0xff; + } + tmp = RT3070_RF_KICK | reg << 8; + rt2860_io_mac_write(sc, RT3070_RF_CSR_CFG, tmp); + + for (ntries = 0; ntries < 100; ntries++) { + tmp = rt2860_io_mac_read(sc, RT3070_RF_CSR_CFG); + if (!(tmp & RT3070_RF_KICK)) + break; + DELAY(1); + } + if (ntries == 100) { + device_printf(sc->dev, "could not read RF register\n"); + return 0xff; + } + return tmp & 0xff; +} + +void +rt3090_rf_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 10; ntries++) { + if (!(rt2860_io_mac_read(sc, RT3070_RF_CSR_CFG) & RT3070_RF_KICK)) + break; + DELAY(10); + } + if (ntries == 10) { + device_printf(sc->dev, "could not write to RF\n"); + return; + } + + tmp = RT3070_RF_WRITE | RT3070_RF_KICK | reg << 8 | val; + rt2860_io_mac_write(sc, RT3070_RF_CSR_CFG, tmp); +} + +/* + * rt2860_rf_name + */ +const char *rt2860_rf_name(int rf_rev) +{ + switch (rf_rev) + { + case RT2860_EEPROM_RF_2820: + return "RT2820 2.4G 2T3R"; + + case RT2860_EEPROM_RF_2850: + return "RT2850 2.4G/5G 2T3R"; + + case RT2860_EEPROM_RF_2720: + return "RT2720 2.4G 1T2R"; + + case RT2860_EEPROM_RF_2750: + return "RT2750 2.4G/5G 1T2R"; + + case RT2860_EEPROM_RF_3020: + return "RT3020 2.4G 1T1R"; + + case RT2860_EEPROM_RF_2020: + return "RT2020 2.4G B/G"; + + case RT2860_EEPROM_RF_3021: + return "RT3021 2.4G 1T2R"; + + case RT2860_EEPROM_RF_3022: + return "RT3022 2.4G 2T2R"; + + case RT2860_EEPROM_RF_3052: + return "RT3052 2.4G/5G 2T2R"; + + case RT2860_EEPROM_RF_2853: + return "RT2853 2.4G.5G 3T3R"; + + case RT2860_EEPROM_RF_3320: + return "RT3320 2.4G 1T1R with PA"; + + case RT2860_EEPROM_RF_3322: + return "RT3322 2.4G 2T2R with PA"; + + case RT2860_EEPROM_RF_3053: + return "RT3053 2.4G/5G 3T3R"; + + default: + return "unknown"; + } +} + +/* + * rt2860_rf_select_chan_group + */ +void rt2860_rf_select_chan_group(struct rt2860_softc *sc, + struct ieee80211_channel *c) +{ + struct ieee80211com *ic; + int chan, group; + uint32_t tmp; + + ic = &sc->sc_ic; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + if (chan <= 14) + group = 0; + else if (chan <= 64) + group = 1; + else if (chan <= 128) + group = 2; + else + group = 3; + + rt2860_io_bbp_write(sc, 62, 0x37 - sc->lna_gain[group]); + rt2860_io_bbp_write(sc, 63, 0x37 - sc->lna_gain[group]); + rt2860_io_bbp_write(sc, 64, 0x37 - sc->lna_gain[group]); + rt2860_io_bbp_write(sc, 86, 0x00); + + if (group == 0) + { + if (sc->ext_lna_2ghz) + { + rt2860_io_bbp_write(sc, 82, 0x62); + rt2860_io_bbp_write(sc, 75, 0x46); + } + else + { + rt2860_io_bbp_write(sc, 82, 0x84); + rt2860_io_bbp_write(sc, 75, 0x50); + } + } + else + { + rt2860_io_bbp_write(sc, 82, 0xf2); + + if (sc->ext_lna_5ghz) + rt2860_io_bbp_write(sc, 75, 0x46); + else + rt2860_io_bbp_write(sc, 75, 0x50); + } + + if (group == 0) + { + tmp = 0x2e + sc->lna_gain[group]; + } + else + { + if ((ic->ic_flags & IEEE80211_F_SCAN) || !IEEE80211_IS_CHAN_HT40(c)) + tmp = 0x32 + sc->lna_gain[group] * 5 / 3; + else + tmp = 0x3a + sc->lna_gain[group] * 5 / 3; + } + + rt2860_io_bbp_write(sc, 66, tmp); + + tmp = RT2860_REG_RFTR_ENABLE | + RT2860_REG_TRSW_ENABLE | + RT2860_REG_LNA_PE_G1_ENABLE | + RT2860_REG_LNA_PE_A1_ENABLE | + RT2860_REG_LNA_PE_G0_ENABLE | + RT2860_REG_LNA_PE_A0_ENABLE; + + if (group == 0) + tmp |= RT2860_REG_PA_PE_G1_ENABLE | + RT2860_REG_PA_PE_G0_ENABLE; + else + tmp |= RT2860_REG_PA_PE_A1_ENABLE | + RT2860_REG_PA_PE_A0_ENABLE; + + if (sc->ntxpath == 1) + tmp &= ~(RT2860_REG_PA_PE_G1_ENABLE | RT2860_REG_PA_PE_A1_ENABLE); + + if (sc->nrxpath == 1) + tmp &= ~(RT2860_REG_LNA_PE_G1_ENABLE | RT2860_REG_LNA_PE_A1_ENABLE); + + rt2860_io_mac_write(sc, RT2860_REG_TX_PIN_CFG, tmp); + + tmp = rt2860_io_mac_read(sc, RT2860_REG_TX_BAND_CFG); + + tmp &= ~(RT2860_REG_TX_BAND_BG | RT2860_REG_TX_BAND_A | RT2860_REG_TX_BAND_HT40_ABOVE); + + if (group == 0) + tmp |= RT2860_REG_TX_BAND_BG; + else + tmp |= RT2860_REG_TX_BAND_A; + + /* set central channel position */ + + if (IEEE80211_IS_CHAN_HT40U(c)) + tmp |= RT2860_REG_TX_BAND_HT40_BELOW; + else if (IEEE80211_IS_CHAN_HT40D(c)) + tmp |= RT2860_REG_TX_BAND_HT40_ABOVE; + else + tmp |= RT2860_REG_TX_BAND_HT40_BELOW; + + rt2860_io_mac_write(sc, RT2860_REG_TX_BAND_CFG, tmp); + + /* set bandwidth (20MHz or 40MHz) */ + + tmp = rt2860_io_bbp_read(sc, 4); + + tmp &= ~0x18; + + if (IEEE80211_IS_CHAN_HT40(c)) + tmp |= 0x10; + + rt2860_io_bbp_write(sc, 4, tmp); + + /* set central channel position */ + + tmp = rt2860_io_bbp_read(sc, 3); + + tmp &= ~0x20; + + if (IEEE80211_IS_CHAN_HT40D(c)) + tmp |= 0x20; + + rt2860_io_bbp_write(sc, 3, tmp); + +// if (sc->mac_rev == 0x28600100) + if (sc->mac_rev == 0x28600102) + { + if (!IEEE80211_IS_CHAN_HT40(c)) + { + rt2860_io_bbp_write(sc, 69, 0x16); + rt2860_io_bbp_write(sc, 70, 0x08); + rt2860_io_bbp_write(sc, 73, 0x12); + } + else + { + rt2860_io_bbp_write(sc, 69, 0x1a); + rt2860_io_bbp_write(sc, 70, 0x0a); + rt2860_io_bbp_write(sc, 73, 0x16); + } + } + +} + +void +rt3090_set_chan(struct rt2860_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2; + uint8_t rf; + int i; + + KASSERT((chan >= 1 && chan <= 14), "RT3090 is 2GHz only"); /* RT3090 is 2GHz only */ + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + rt3090_rf_write(sc, 2, rt3090_freqs[i].n); + rf = rt3090_rf_read(sc, 3); + rf = (rf & ~0x0f) | rt3090_freqs[i].k; + rt3090_rf_write(sc, 3, rf); + rf = rt3090_rf_read(sc, 6); + rf = (rf & ~0x03) | rt3090_freqs[i].r; + rt3090_rf_write(sc, 6, rf); + + /* set Tx0 power */ + rf = rt3090_rf_read(sc, 12); + rf = (rf & ~0x1f) | txpow1; + rt3090_rf_write(sc, 12, rf); + + /* set Tx1 power */ + rf = rt3090_rf_read(sc, 13); + rf = (rf & ~0x1f) | txpow2; + rt3090_rf_write(sc, 13, rf); + + rf = rt3090_rf_read(sc, 1); + rf &= ~0xfc; + if (sc->ntxpath == 1) + rf |= RT3070_TX1_PD | RT3070_TX2_PD; + else if (sc->ntxpath == 2) + rf |= RT3070_TX2_PD; + if (sc->nrxpath == 1) + rf |= RT3070_RX1_PD | RT3070_RX2_PD; + else if (sc->nrxpath == 2) + rf |= RT3070_RX2_PD; + rt3090_rf_write(sc, 1, rf); + + /* set RF offset */ + rf = rt3090_rf_read(sc, 23); + rf = (rf & ~0x7f) | sc->rf_freq_off; + rt3090_rf_write(sc, 23, rf); + + /* program RF filter */ + rf = rt3090_rf_read(sc, 24); /* Tx */ + rf = (rf & ~0x3f) | sc->rf24_20mhz; + rt3090_rf_write(sc, 24, rf); + rf = rt3090_rf_read(sc, 31); /* Rx */ + rf = (rf & ~0x3f) | sc->rf24_20mhz; + rt3090_rf_write(sc, 31, rf); + + /* enable RF tuning */ + rf = rt3090_rf_read(sc, 7); + rt3090_rf_write(sc, 7, rf | RT3070_TUNE); +} + +int +rt3090_rf_init(struct rt2860_softc *sc) +{ + uint32_t tmp; + uint8_t rf, bbp; + int i; + + rf = rt3090_rf_read(sc, 30); + /* toggle RF R30 bit 7 */ + rt3090_rf_write(sc, 30, rf | 0x80); + DELAY(1000); + rt3090_rf_write(sc, 30, rf & ~0x80); + + tmp = rt2860_io_mac_read(sc, RT3070_LDO_CFG0); + tmp &= ~0x1f000000; + if (sc->patch_dac && (sc->mac_rev & 0x0000ffff) < 0x0211) + tmp |= 0x0d000000; /* 1.35V */ + else + tmp |= 0x01000000; /* 1.2V */ + rt2860_io_mac_write(sc, RT3070_LDO_CFG0, tmp); + + /* patch LNA_PE_G1 */ + tmp = rt2860_io_mac_read(sc, RT3070_GPIO_SWITCH); + rt2860_io_mac_write(sc, RT3070_GPIO_SWITCH, tmp & ~0x20); + + /* initialize RF registers to default value */ + for (i = 0; i < (sizeof(rt3090_def_rf)/2); i++) { + rt3090_rf_write(sc, rt3090_def_rf[i].reg, + rt3090_def_rf[i].val); + } + + /* select 20MHz bandwidth */ + rt3090_rf_write(sc, 31, 0x14); + + rf = rt3090_rf_read(sc, 6); + rt3090_rf_write(sc, 6, rf | 0x40); + + if ((sc->mac_rev & 0xffff0000) != 0x35930000) { + /* calibrate filter for 20MHz bandwidth */ + sc->rf24_20mhz = 0x1f; /* default value */ + rt3090_filter_calib(sc, 0x07, 0x16, &sc->rf24_20mhz); + + /* select 40MHz bandwidth */ + bbp = rt2860_io_bbp_read(sc, 4); + rt2860_io_bbp_write(sc, 4, (bbp & ~0x08) | 0x10); + rf = rt3090_rf_read(sc, 31); + rt3090_rf_write(sc, 31, rf | 0x20); + + /* calibrate filter for 40MHz bandwidth */ + sc->rf24_40mhz = 0x2f; /* default value */ + rt3090_filter_calib(sc, 0x27, 0x19, &sc->rf24_40mhz); + + /* go back to 20MHz bandwidth */ + bbp = rt2860_io_bbp_read(sc, 4); + rt2860_io_bbp_write(sc, 4, bbp & ~0x18); + } + if ((sc->mac_rev & 0x0000ffff) < 0x0211) + rt3090_rf_write(sc, 27, 0x03); + + tmp = rt2860_io_mac_read(sc, RT3070_OPT_14); + rt2860_io_mac_write(sc, RT3070_OPT_14, tmp | 1); + + if (sc->rf_rev == RT2860_EEPROM_RF_3020) + rt3090_set_rx_antenna(sc, 0); + + bbp = rt2860_io_bbp_read(sc, 138); + if ((sc->mac_rev & 0xffff0000) == 0x35930000) { + if (sc->ntxpath == 1) + bbp |= 0x60; /* turn off DAC1 and DAC2 */ + else if (sc->ntxpath == 2) + bbp |= 0x40; /* turn off DAC2 */ + if (sc->nrxpath == 1) + bbp &= ~0x06; /* turn off ADC1 and ADC2 */ + else if (sc->nrxpath == 2) + bbp &= ~0x04; /* turn off ADC2 */ + } else { + if (sc->ntxpath == 1) + bbp |= 0x20; /* turn off DAC1 */ + if (sc->nrxpath == 1) + bbp &= ~0x02; /* turn off ADC1 */ + } + rt2860_io_bbp_write(sc, 138, bbp); + + rf = rt3090_rf_read(sc, 1); + rf &= ~(RT3070_RX0_PD | RT3070_TX0_PD); + rf |= RT3070_RF_BLOCK | RT3070_RX1_PD | RT3070_TX1_PD; + rt3090_rf_write(sc, 1, rf); + + rf = rt3090_rf_read(sc, 15); + rt3090_rf_write(sc, 15, rf & ~RT3070_TX_LO2); + + rf = rt3090_rf_read(sc, 17); + rf &= ~RT3070_TX_LO1; + if ((sc->mac_rev & 0x0000ffff) >= 0x0211 && !sc->ext_lna_2ghz) + rf |= 0x20; /* fix for long range Rx issue */ + if (sc->txmixgain_2ghz >= 2) + rf = (rf & ~0x7) | sc->txmixgain_2ghz; + rt3090_rf_write(sc, 17, rf); + + rf = rt3090_rf_read(sc, 20); + rt3090_rf_write(sc, 20, rf & ~RT3070_RX_LO1); + + rf = rt3090_rf_read(sc, 21); + rt3090_rf_write(sc, 21, rf & ~RT3070_RX_LO2); + + return 0; +} + +void +rt3090_set_rx_antenna(struct rt2860_softc *sc, int aux) +{ + uint32_t tmp; + + if (aux) { + tmp = rt2860_io_mac_read(sc, RT2860_REG_EEPROM_CSR); + rt2860_io_mac_write(sc, RT2860_REG_EEPROM_CSR, tmp & ~RT2860_REG_EESK); + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_GPIO_CTRL_CFG); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_GPIO_CTRL_CFG, (tmp & ~0x0808) | 0x08); + } else { + tmp = rt2860_io_mac_read(sc, RT2860_REG_EEPROM_CSR); + rt2860_io_mac_write(sc, RT2860_REG_EEPROM_CSR, tmp | RT2860_REG_EESK); + tmp = rt2860_io_mac_read(sc, RT2860_REG_SCHDMA_GPIO_CTRL_CFG); + rt2860_io_mac_write(sc, RT2860_REG_SCHDMA_GPIO_CTRL_CFG, tmp & ~0x0808); + } +} + +void +rt3090_rf_wakeup(struct rt2860_softc *sc) +{ + uint32_t tmp; + uint8_t rf; + + if ((sc->mac_rev & 0xffff0000) == 0x35930000) { + /* enable VCO */ + rf = rt3090_rf_read(sc, 1); + rt3090_rf_write(sc, 1, rf | RT3593_VCO); + + /* initiate VCO calibration */ + rf = rt3090_rf_read(sc, 3); + rt3090_rf_write(sc, 3, rf | RT3593_VCOCAL); + + /* enable VCO bias current control */ + rf = rt3090_rf_read(sc, 6); + rt3090_rf_write(sc, 6, rf | RT3593_VCO_IC); + + /* initiate res calibration */ + rf = rt3090_rf_read(sc, 2); + rt3090_rf_write(sc, 2, rf | RT3593_RESCAL); + + /* set reference current control to 0.33 mA */ + rf = rt3090_rf_read(sc, 22); + rf &= ~RT3593_CP_IC_MASK; + rf |= 1 << RT3593_CP_IC_SHIFT; + rt3090_rf_write(sc, 22, rf); + + /* enable RX CTB */ + rf = rt3090_rf_read(sc, 46); + rt3090_rf_write(sc, 46, rf | RT3593_RX_CTB); + + rf = rt3090_rf_read(sc, 20); + rf &= ~(RT3593_LDO_RF_VC_MASK | RT3593_LDO_PLL_VC_MASK); + rt3090_rf_write(sc, 20, rf); + } else { + /* enable RF block */ + rf = rt3090_rf_read(sc, 1); + rt3090_rf_write(sc, 1, rf | RT3070_RF_BLOCK); + + /* enable VCO bias current control */ + rf = rt3090_rf_read(sc, 7); + rt3090_rf_write(sc, 7, rf | 0x30); + + rf = rt3090_rf_read(sc, 9); + rt3090_rf_write(sc, 9, rf | 0x0e); + + /* enable RX CTB */ + rf = rt3090_rf_read(sc, 21); + rt3090_rf_write(sc, 21, rf | RT3070_RX_CTB); + + /* fix Tx to Rx IQ glitch by raising RF voltage */ + rf = rt3090_rf_read(sc, 27); + rf &= ~0x77; + if ((sc->mac_rev & 0x0000ffff) < 0x0211) + rf |= 0x03; + rt3090_rf_write(sc, 27, rf); + } + if (sc->patch_dac && (sc->mac_rev & 0x0000ffff) < 0x0211) { + tmp = rt2860_io_mac_read(sc, RT3070_LDO_CFG0); + tmp = (tmp & ~0x1f000000) | 0x0d000000; + rt2860_io_mac_write(sc, RT3070_LDO_CFG0, tmp); + } +} + +int +rt3090_filter_calib(struct rt2860_softc *sc, uint8_t init, uint8_t target, + uint8_t *val) +{ + uint8_t rf22, rf24; + uint8_t bbp55_pb, bbp55_sb, delta; + int ntries; + + /* program filter */ + rf24 = rt3090_rf_read(sc, 24); + rf24 = (rf24 & 0xc0) | init; /* initial filter value */ + rt3090_rf_write(sc, 24, rf24); + + /* enable baseband loopback mode */ + rf22 = rt3090_rf_read(sc, 22); + rt3090_rf_write(sc, 22, rf22 | RT3070_BB_LOOPBACK); + + /* set power and frequency of passband test tone */ + rt2860_io_bbp_write(sc, 24, 0x00); + for (ntries = 0; ntries < 100; ntries++) { + /* transmit test tone */ + rt2860_io_bbp_write(sc, 25, 0x90); + DELAY(1000); + /* read received power */ + bbp55_pb = rt2860_io_bbp_read(sc, 55); + if (bbp55_pb != 0) + break; + } + if (ntries == 100) + return ETIMEDOUT; + + /* set power and frequency of stopband test tone */ + rt2860_io_bbp_write(sc, 24, 0x06); + for (ntries = 0; ntries < 100; ntries++) { + /* transmit test tone */ + rt2860_io_bbp_write(sc, 25, 0x90); + DELAY(1000); + /* read received power */ + bbp55_sb = rt2860_io_bbp_read(sc, 55); + + delta = bbp55_pb - bbp55_sb; + if (delta > target) + break; + + /* reprogram filter */ + rf24++; + rt3090_rf_write(sc, 24, rf24); + } + if (ntries < 100) { + if (rf24 != init) + rf24--; /* backtrack */ + *val = rf24; + rt3090_rf_write(sc, 24, rf24); + } + + /* restore initial state */ + rt2860_io_bbp_write(sc, 24, 0x00); + + /* disable baseband loopback mode */ + rf22 = rt3090_rf_read(sc, 22); + rt3090_rf_write(sc, 22, rf22 & ~RT3070_BB_LOOPBACK); + + return 0; +} + +void +rt3090_rf_setup(struct rt2860_softc *sc) +{ + uint8_t bbp; + int i; + + if ((sc->mac_rev & 0x0000ffff) >= 0x0211) { + /* enable DC filter */ + rt2860_io_bbp_write(sc, 103, 0xc0); + + /* improve power consumption */ + bbp = rt2860_io_bbp_read(sc, 31); + rt2860_io_bbp_write(sc, 31, bbp & ~0x03); + } + + rt2860_io_mac_write(sc, RT2860_REG_TX_SW_CFG1, 0); + if ((sc->mac_rev & 0x0000ffff) < 0x0211) { + rt2860_io_mac_write(sc, RT2860_REG_TX_SW_CFG2, + sc->patch_dac ? 0x2c : 0x0f); + } else + rt2860_io_mac_write(sc, RT2860_REG_TX_SW_CFG2, 0); + + /* initialize RF registers from ROM */ + for (i = 0; i < 10; i++) { + if (sc->rf[i].reg == 0 || sc->rf[i].reg == 0xff) + continue; + rt3090_rf_write(sc, sc->rf[i].reg, sc->rf[i].val); + } +} + + +/* + * rt2860_rf_set_chan + */ +void rt2860_rf_set_chan(struct rt2860_softc *sc, + struct ieee80211_channel *c) +{ + struct ieee80211com *ic; + const struct rt2860_rf_prog *prog; + uint32_t r1, r2, r3, r4; + int8_t txpow1, txpow2; + int i, chan; + + if (sc->mac_rev == 0x28720200) { + rt2872_rf_set_chan(sc, c); + return; + } + + ic = &sc->sc_ic; + prog = rt2860_rf_2850; + + /* get central channel position */ + + chan = ieee80211_chan2ieee(ic, c); + + if ((sc->mac_rev & 0xffff0000) >= 0x30710000) { + rt3090_set_chan(sc, chan); + return; + } + + if (IEEE80211_IS_CHAN_HT40U(c)) + chan += 2; + else if (IEEE80211_IS_CHAN_HT40D(c)) + chan -= 2; + + RT2860_DPRINTF(sc, RT2860_DEBUG_CHAN, + "%s: RF set channel: channel=%u, HT%s%s\n", + device_get_nameunit(sc->dev), + ieee80211_chan2ieee(ic, c), + !IEEE80211_IS_CHAN_HT(c) ? " disabled" : + IEEE80211_IS_CHAN_HT20(c) ? "20": + IEEE80211_IS_CHAN_HT40U(c) ? "40U" : "40D", + (ic->ic_flags & IEEE80211_F_SCAN) ? ", scanning" : ""); + + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + for (i = 0; prog[i].chan != chan; i++); + + r1 = prog[i].r1; + r2 = prog[i].r2; + r3 = prog[i].r3; + r4 = prog[i].r4; + + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + if (sc->ntxpath == 1) + r2 |= (1 << 14); + + if (sc->nrxpath == 2) + r2 |= (1 << 6); + else if (sc->nrxpath == 1) + r2 |= (1 << 17) | (1 << 6); + + if (IEEE80211_IS_CHAN_2GHZ(c)) + { + r3 = (r3 & 0xffffc1ff) | (txpow1 << 9); + r4 = (r4 & ~0x001f87c0) | (sc->rf_freq_off << 15) | + (txpow2 << 6); + } + else + { + r3 = r3 & 0xffffc1ff; + r4 = (r4 & ~0x001f87c0) | (sc->rf_freq_off << 15); + + if (txpow1 >= RT2860_EEPROM_TXPOW_5GHZ_MIN && txpow1 < 0) + { + txpow1 = (-RT2860_EEPROM_TXPOW_5GHZ_MIN + txpow1); + if (txpow1 > RT2860_EEPROM_TXPOW_5GHZ_MAX) + txpow1 = RT2860_EEPROM_TXPOW_5GHZ_MAX; + + r3 |= (txpow1 << 10); + } + else + { + if (txpow1 > RT2860_EEPROM_TXPOW_5GHZ_MAX) + txpow1 = RT2860_EEPROM_TXPOW_5GHZ_MAX; + + r3 |= (txpow1 << 10) | (1 << 9); + } + + if (txpow2 >= RT2860_EEPROM_TXPOW_5GHZ_MIN && txpow2 < 0) + { + txpow2 = (-RT2860_EEPROM_TXPOW_5GHZ_MIN + txpow2); + if (txpow2 > RT2860_EEPROM_TXPOW_5GHZ_MAX) + txpow2 = RT2860_EEPROM_TXPOW_5GHZ_MAX; + + r4 |= (txpow2 << 7); + } + else + { + if (txpow2 > RT2860_EEPROM_TXPOW_5GHZ_MAX) + txpow2 = RT2860_EEPROM_TXPOW_5GHZ_MAX; + + r4 |= (txpow2 << 7) | (1 << 6); + } + } + + if (!(ic->ic_flags & IEEE80211_F_SCAN) && IEEE80211_IS_CHAN_HT40(c)) + r4 |= (1 << 21); + + rt2860_io_rf_write(sc, RT2860_REG_RF_R1, r1); + rt2860_io_rf_write(sc, RT2860_REG_RF_R2, r2); + rt2860_io_rf_write(sc, RT2860_REG_RF_R3, r3 & ~(1 << 2)); + rt2860_io_rf_write(sc, RT2860_REG_RF_R4, r4); + + DELAY(200); + + rt2860_io_rf_write(sc, RT2860_REG_RF_R1, r1); + rt2860_io_rf_write(sc, RT2860_REG_RF_R2, r2); + rt2860_io_rf_write(sc, RT2860_REG_RF_R3, r3 | (1 << 2)); + rt2860_io_rf_write(sc, RT2860_REG_RF_R4, r4); + + DELAY(200); + + rt2860_io_rf_write(sc, RT2860_REG_RF_R1, r1); + rt2860_io_rf_write(sc, RT2860_REG_RF_R2, r2); + rt2860_io_rf_write(sc, RT2860_REG_RF_R3, r3 & ~(1 << 2)); + rt2860_io_rf_write(sc, RT2860_REG_RF_R4, r4); + + rt2860_rf_select_chan_group(sc, c); + + DELAY(1000); +} + +/* + * rt2872_rf_set_chan + */ +static void +rt2872_rf_set_chan(struct rt2860_softc *sc, + struct ieee80211_channel *c) +{ + struct ieee80211com *ic; + const struct rt2860_rf_prog *prog; + uint32_t r1, r2, r3, r4; + uint32_t r6, r7, r12, r13, r23, r24; + int8_t txpow1, txpow2; + int i, chan; + + ic = &sc->sc_ic; + prog = rt2860_rf_2850; + + /* get central channel position */ + + chan = ieee80211_chan2ieee(ic, c); + + if (IEEE80211_IS_CHAN_HT40U(c)) + chan += 2; + else if (IEEE80211_IS_CHAN_HT40D(c)) + chan -= 2; + + RT2860_DPRINTF(sc, RT2860_DEBUG_CHAN, + "%s: RF set channel: channel=%u, HT%s%s\n", + device_get_nameunit(sc->dev), + ieee80211_chan2ieee(ic, c), + !IEEE80211_IS_CHAN_HT(c) ? " disabled" : + IEEE80211_IS_CHAN_HT20(c) ? "20": + IEEE80211_IS_CHAN_HT40U(c) ? "40U" : "40D", + (ic->ic_flags & IEEE80211_F_SCAN) ? ", scanning" : ""); + + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + for (i = 0; prog[i].chan != chan; i++); + + r1 = prog[i].r1; + r2 = prog[i].r2; + r3 = prog[i].r3; + r4 = prog[i].r4; + +// txpow1 = sc->txpow1[i]; +// txpow2 = sc->txpow2[i]; + txpow1 = 30; + txpow2 = 30; + r3 = 16; + + for (i = 0; rt2860_rf_fi3020[i].channel != chan; i++); + + /* Program channel parameters */ + r2 = rt2860_rf_fi3020[i].n; + rt2860_io_rf_write(sc, 2 , r2 ); + r3 = rt2860_rf_fi3020[i].k; + rt2860_io_rf_write(sc, 3 , r3 ); + + r6 = (rt3052_rf_default[6] & 0xFC) | (rt2860_rf_fi3020[i].r & 0x03); + rt2860_io_rf_write(sc, 6 , r6 ); + + /* Set Tx Power */ + r12 = (rt3052_rf_default[12] & 0xE0) | (txpow1 & 0x1f); + rt2860_io_rf_write(sc, 12, r12); + + /* Set Tx1 Power */ + r13 = (rt3052_rf_default[13] & 0xE0) | (txpow2 & 0x1f); + rt2860_io_rf_write(sc, 13, r13); + + /* Set RF offset */ + r23 = (rt3052_rf_default[23] & 0x80) | (sc->rf_freq_off); + rt2860_io_rf_write(sc, 23, r23); + + /* Set BW */ + r24 = (rt3052_rf_default[24] & 0xDF); + if (!(ic->ic_flags & IEEE80211_F_SCAN) && IEEE80211_IS_CHAN_HT40(c)) + r24 |= 0x20; + rt2860_io_rf_write(sc, 24, r24); + + /* Enable RF tuning */ + r7 = (rt3052_rf_default[7]) | 1; + rt2860_io_rf_write(sc, 7 , r7 ); + + /* Antenna */ +/* + r1 = (rt3052_rf_default[1] & 0xab) | ((sc->nrxpath == 1)?0x10:0) | + ((sc->ntxpath == 1)?0x20:0); + rt2860_io_rf_write(sc, 1 , r1 ); +*/ + r1 = (rt3052_rf_default[1] & 0xab) | 0x10; + rt2860_io_rf_write(sc, 1 , r1 ); + r1 = (r1 & 0x57) | 0x20; + rt2860_io_rf_write(sc, 1 , r1 ); + + DELAY(200); + + rt2860_rf_select_chan_group(sc, c); + + DELAY(1000); +} + Index: sys/dev/rt2860/rt2860_rxdesc.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_rxdesc.h @@ -0,0 +1,62 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_RXDESC_H_ +#define _RT2860_RXDESC_H_ + +#define RT2860_RXDESC_SDL0_DDONE (1 << 15) + +#define RT2860_RXDESC_FLAGS_LAST_AMSDU (1 << 19) +#define RT2860_RXDESC_FLAGS_CIPHER_ALG (1 << 18) +#define RT2860_RXDESC_FLAGS_PLCP_RSSIL (1 << 17) +#define RT2860_RXDESC_FLAGS_DECRYPTED (1 << 16) +#define RT2860_RXDESC_FLAGS_AMPDU (1 << 15) +#define RT2860_RXDESC_FLAGS_L2PAD (1 << 14) +#define RT2860_RXDESC_FLAGS_RSSI (1 << 13) +#define RT2860_RXDESC_FLAGS_HTC (1 << 12) +#define RT2860_RXDESC_FLAGS_AMSDU (1 << 11) +#define RT2860_RXDESC_FLAGS_CRC_ERR (1 << 8) +#define RT2860_RXDESC_FLAGS_MYBSS (1 << 7) +#define RT2860_RXDESC_FLAGS_BCAST (1 << 6) +#define RT2860_RXDESC_FLAGS_MCAST (1 << 5) +#define RT2860_RXDESC_FLAGS_U2M (1 << 4) +#define RT2860_RXDESC_FLAGS_FRAG (1 << 3) +#define RT2860_RXDESC_FLAGS_NULL_DATA (1 << 2) +#define RT2860_RXDESC_FLAGS_DATA (1 << 1) +#define RT2860_RXDESC_FLAGS_BA (1 << 0) + +#define RT2860_RXDESC_FLAGS_CIPHER_ERR_SHIFT 9 +#define RT2860_RXDESC_FLAGS_CIPHER_ERR_MASK 0x3 +#define RT2860_RXDESC_FLAGS_CIPHER_ERR_NONE 0 +#define RT2860_RXDESC_FLAGS_CIPHER_ERR_ICV 1 +#define RT2860_RXDESC_FLAGS_CIPHER_ERR_MIC 2 +#define RT2860_RXDESC_FLAGS_CIPHER_ERR_INVALID_KEY 3 + +#define RT2860_RXDESC_FLAGS_PLCP_SIGNAL_SHIFT 20 +#define RT2860_RXDESC_FLAGS_PLCP_SIGNAL_MASK 0xfff + +struct rt2860_rxdesc +{ + uint32_t sdp0; + uint16_t sdl1; + uint16_t sdl0; + uint32_t sdp1; + uint32_t flags; +} __packed; + +#endif /* #ifndef _RT2860_RXDESC_H_ */ Index: sys/dev/rt2860/rt2860_rxwi.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_rxwi.h @@ -0,0 +1,79 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_RXWI_H_ +#define _RT2860_RXWI_H_ + +#define RT2860_RXWI_KEYIDX_SHIFT 0 +#define RT2860_RXWI_KEYIDX_MASK 0x3 + +#define RT2860_RXWI_BSSIDX_SHIFT 2 +#define RT2860_RXWI_BSSIDX_MASK 0x7 + +#define RT2860_RXWI_UDF_SHIFT 5 +#define RT2860_RXWI_UDF_MASK 0x7 + +#define RT2860_RXWI_SIZE_SHIFT 0 +#define RT2860_RXWI_SIZE_MASK 0xfff + +#define RT2860_RXWI_TID_SHIFT 12 +#define RT2860_RXWI_TID_MASK 0xf + +#define RT2860_RXWI_FRAG_SHIFT 0 +#define RT2860_RXWI_FRAG_MASK 0xf + +#define RT2860_RXWI_SEQ_SHIFT 4 +#define RT2860_RXWI_SEQ_MASK 0xfff + +#define RT2860_RXWI_MCS_SHIFT 0 +#define RT2860_RXWI_MCS_MASK 0x7f +#define RT2860_RXWI_MCS_SHOTPRE (1 << 3) + +#define RT2860_RXWI_BW_SHIFT 7 +#define RT2860_RXWI_BW_MASK 0x1 +#define RT2860_RXWI_BW_20 0 +#define RT2860_RXWI_BW_40 1 + +#define RT2860_RXWI_SHORTGI_SHIFT 0 +#define RT2860_RXWI_SHORTGI_MASK 0x1 + +#define RT2860_RXWI_STBC_SHIFT 1 +#define RT2860_RXWI_STBC_MASK 0x3 + +#define RT2860_RXWI_PHYMODE_SHIFT 6 +#define RT2860_RXWI_PHYMODE_MASK 0x3 +#define RT2860_RXWI_PHYMODE_CCK 0 +#define RT2860_RXWI_PHYMODE_OFDM 1 +#define RT2860_RXWI_PHYMODE_HT_MIXED 2 +#define RT2860_RXWI_PHYMODE_HT_GF 3 + +struct rt2860_rxwi +{ + uint8_t wcid; + uint8_t udf_bssidx_keyidx; + uint16_t tid_size; + uint16_t seq_frag; + uint8_t bw_mcs; + uint8_t phymode_stbc_shortgi; + uint8_t rssi[3]; + uint8_t reserved1; + uint8_t snr[2]; + uint16_t reserved2; +} __packed; + +#endif /* #ifndef _RT2860_RXWI_H_ */ Index: sys/dev/rt2860/rt2860_softc.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_softc.h @@ -0,0 +1,437 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_SOFTC_H_ +#define _RT2860_SOFTC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//#include "opt_rt2860.h" +#define RT2860_DEBUG +#define RT2860_HW_CRYPTO + +#include +#include +#include +#include + +#define RT_CHIPID_RT3050 0x3050 +#define RT_CHIPID_RT3052 0x3052 + +#define RT2860_SOFTC_LOCK(sc) mtx_lock(&(sc)->lock) +#define RT2860_SOFTC_UNLOCK(sc) \ + mtx_unlock(&(sc)->lock) +#define RT2860_SOFTC_ASSERT_LOCKED(sc) \ + mtx_assert(&(sc)->lock, MA_OWNED) + +#define RT2860_SOFTC_TX_RING_LOCK(ring) mtx_lock(&(ring)->lock) +#define RT2860_SOFTC_TX_RING_UNLOCK(ring) \ + mtx_unlock(&(ring)->lock) +#define RT2860_SOFTC_TX_RING_ASSERT_LOCKED(ring) \ + mtx_assert(&(ring)->lock, MA_OWNED) + +#define RT2860_SOFTC_FLAGS_UCODE_LOADED (1 << 0) + +#define RT2860_SOFTC_LED_OFF_COUNT 3 + +#define RT2860_SOFTC_RSSI_OFF_COUNT 3 + +#define RT2860_SOFTC_LNA_GAIN_COUNT 4 + +#define RT2860_SOFTC_TXPOW_COUNT 50 + +#define RT2860_SOFTC_TXPOW_RATE_COUNT 5 + +#define RT2860_SOFTC_TSSI_COUNT 9 + +#define RT2860_SOFTC_BBP_EEPROM_COUNT 8 + +#define RT2860_SOFTC_RSSI_COUNT 3 + +#define RT2860_SOFTC_STAID_COUNT 64 + +#define RT2860_SOFTC_TX_RING_COUNT 6 + +#define RT2860_SOFTC_RX_RING_DATA_COUNT 128 + +#define RT2860_SOFTC_MAX_SCATTER 10 + +#define RT2860_SOFTC_TX_RING_DATA_COUNT 256 +#define RT2860_SOFTC_TX_RING_DESC_COUNT \ + (RT2860_SOFTC_TX_RING_DATA_COUNT * RT2860_SOFTC_MAX_SCATTER) + +#define RT2860_SOFTC_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_XCHANNEL)) + +#define RT2860_SOFTC_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_XCHANNEL)) + +struct rt2860_softc_rx_data +{ + bus_dmamap_t dma_map; + struct mbuf *m; +}; + +struct rt2860_softc_rx_ring +{ + bus_dma_tag_t desc_dma_tag; + bus_dmamap_t desc_dma_map; + bus_addr_t desc_phys_addr; + struct rt2860_rxdesc *desc; + bus_dma_tag_t data_dma_tag; + bus_dmamap_t spare_dma_map; + struct rt2860_softc_rx_data data[RT2860_SOFTC_RX_RING_DATA_COUNT]; + int cur; +}; + +struct rt2860_softc_tx_data +{ + bus_dmamap_t dma_map; + struct ieee80211_node *ni; + struct mbuf *m; +}; + +struct rt2860_softc_tx_ring +{ + struct mtx lock; + bus_dma_tag_t desc_dma_tag; + bus_dmamap_t desc_dma_map; + bus_addr_t desc_phys_addr; + struct rt2860_txdesc *desc; + int desc_queued; + int desc_cur; + int desc_next; + bus_dma_tag_t seg0_dma_tag; + bus_dmamap_t seg0_dma_map; + bus_addr_t seg0_phys_addr; + uint8_t *seg0; + bus_dma_tag_t data_dma_tag; + struct rt2860_softc_tx_data data[RT2860_SOFTC_TX_RING_DATA_COUNT]; + int data_queued; + int data_cur; + int data_next; + int qid; +}; + +struct rt2860_softc_node +{ + struct ieee80211_node ni; + + uint8_t staid; + + uint8_t last_rssi[RT2860_SOFTC_RSSI_COUNT]; + int8_t last_rssi_dbm[RT2860_SOFTC_RSSI_COUNT]; +}; + +struct rt2860_softc_vap +{ + struct ieee80211vap vap; + + struct ieee80211_beacon_offsets beacon_offsets; + struct mbuf *beacon_mbuf; + struct rt2860_txwi beacon_txwi; + + struct rt2860_amrr amrr; + + int (*newstate)(struct ieee80211vap *vap, + enum ieee80211_state nstate, int arg); +}; + +struct rt2860_softc_rx_radiotap_header +{ + struct ieee80211_radiotap_header ihdr; + uint8_t flags; + uint8_t rate; + int8_t dbm_antsignal; + int8_t dbm_antnoise; + uint8_t antenna; + uint8_t antsignal; + uint8_t pad[2]; + uint32_t chan_flags; + uint16_t chan_freq; + uint8_t chan_ieee; + int8_t chan_maxpow; +} __packed; + +struct rt2860_softc_tx_radiotap_header +{ + struct ieee80211_radiotap_header ihdr; + uint8_t flags; + uint8_t rate; + uint8_t pad[2]; + uint32_t chan_flags; + uint16_t chan_freq; + uint8_t chan_ieee; + int8_t chan_maxpow; +} __packed; + +struct rt2860_softc +{ + struct ieee80211com sc_ic; + + struct mtx lock; + + struct mbufq sc_snd; + + uint32_t flags; + uint32_t sc_flags; +#define RT2860_ENABLED (1 << 0) +#define RT2860_ADVANCED_PS (1 << 1) +#define RT2860_PCIE (1 << 2) +#define RT2860_RUNNING (1 << 3) + + device_t dev; + + int pid; + + int mem_rid; + struct resource *mem; + + int irq_rid; + struct resource *irq; + void *irqh; + + bus_space_tag_t bst; + bus_space_handle_t bsh; + + int nvaps; + int napvaps; + int nadhocvaps; + int nstavaps; + int nwdsvaps; + + void (*node_cleanup)(struct ieee80211_node *ni); + + int (*recv_action)(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm); + + int (*send_action)(struct ieee80211_node *ni, + int cat, int act, void *sa); + + int (*addba_response)(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap, + int status, int baparamset, int batimeout); + + void (*addba_stop)(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); + + int (*ampdu_rx_start)(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap, + int baparamset, int batimeout, int baseqctl); + + void (*ampdu_rx_stop)(struct ieee80211_node *ni, + struct ieee80211_rx_ampdu *rap); + + struct rt2860_amrr_node amrr_node[RT2860_SOFTC_STAID_COUNT]; + + uint32_t mac_rev; + uint8_t eeprom_addr_num; + uint16_t eeprom_rev; + uint8_t rf_rev; + + uint8_t mac_addr[IEEE80211_ADDR_LEN]; + + uint8_t ntxpath; + uint8_t nrxpath; + + int hw_radio_cntl; + int tx_agc_cntl; + int ext_lna_2ghz; + int ext_lna_5ghz; + + uint8_t country_2ghz; + uint8_t country_5ghz; + + uint8_t rf_freq_off; + + uint8_t led_cntl; + uint16_t led_off[RT2860_SOFTC_LED_OFF_COUNT]; + + int8_t rssi_off_2ghz[RT2860_SOFTC_RSSI_OFF_COUNT]; + int8_t rssi_off_5ghz[RT2860_SOFTC_RSSI_OFF_COUNT]; + + int8_t lna_gain[RT2860_SOFTC_LNA_GAIN_COUNT]; + + int8_t txpow1[RT2860_SOFTC_TXPOW_COUNT]; + int8_t txpow2[RT2860_SOFTC_TXPOW_COUNT]; + + int8_t txpow_rate_delta_2ghz; + int8_t txpow_rate_delta_5ghz; + uint32_t txpow_rate_20mhz[RT2860_SOFTC_TXPOW_RATE_COUNT]; + uint32_t txpow_rate_40mhz_2ghz[RT2860_SOFTC_TXPOW_RATE_COUNT]; + uint32_t txpow_rate_40mhz_5ghz[RT2860_SOFTC_TXPOW_RATE_COUNT]; + + int tx_agc_cntl_2ghz; + int tx_agc_cntl_5ghz; + + uint8_t tssi_2ghz[RT2860_SOFTC_TSSI_COUNT]; + uint8_t tssi_step_2ghz; + uint8_t tssi_5ghz[RT2860_SOFTC_TSSI_COUNT]; + uint8_t tssi_step_5ghz; + + struct + { + uint8_t val; + uint8_t reg; + } __packed bbp_eeprom[RT2860_SOFTC_BBP_EEPROM_COUNT], rf[10]; + + uint16_t powersave_level; + + uint8_t staid_mask[RT2860_SOFTC_STAID_COUNT / NBBY]; + + uint32_t intr_enable_mask; + uint32_t intr_disable_mask; + uint32_t intr_pending_mask; + + struct task rx_done_task; + int rx_process_limit; + + struct task tx_done_task; + + struct task fifo_sta_full_task; + + struct task periodic_task; + struct callout periodic_ch; + unsigned long periodic_round; + + struct taskqueue *taskqueue; + + struct rt2860_softc_rx_ring rx_ring; + + struct rt2860_softc_tx_ring tx_ring[RT2860_SOFTC_TX_RING_COUNT]; + int tx_ring_mgtqid; + + struct callout tx_watchdog_ch; + int tx_timer; + + struct rt2860_softc_rx_radiotap_header rxtap; + struct rt2860_softc_tx_radiotap_header txtap; + + /* statistic counters */ + + int interrupts; + int tx_coherent_interrupts; + int rx_coherent_interrupts; + int txrx_coherent_interrupts; + int fifo_sta_full_interrupts; + int rx_interrupts; + int rx_delay_interrupts; + int tx_interrupts[RT2860_SOFTC_TX_RING_COUNT]; + int tx_delay_interrupts; + int pre_tbtt_interrupts; + int tbtt_interrupts; + int mcu_cmd_interrupts; + int auto_wakeup_interrupts; + int gp_timer_interrupts; + + int tx_data_queue_full[RT2860_SOFTC_TX_RING_COUNT]; + + int tx_watchdog_timeouts; + + int tx_defrag_packets; + + int no_tx_desc_avail; + + int rx_mbuf_alloc_errors; + int rx_mbuf_dmamap_errors; + + int tx_queue_not_empty[2]; + + int tx_beacons; + int tx_noretryok; + int tx_retryok; + int tx_failed; + int tx_underflows; + int tx_zerolen; + int tx_nonagg; + int tx_agg; + int tx_ampdu; + int tx_mpdu_zero_density; + int tx_ampdu_sessions; + + int rx_packets; + int rx_ampdu; + int rx_ampdu_retries; + int rx_mpdu_zero_density; + int rx_ampdu_sessions; + int rx_amsdu; + int rx_crc_errors; + int rx_phy_errors; + int rx_false_ccas; + int rx_plcp_errors; + int rx_dup_packets; + int rx_fifo_overflows; + int rx_cipher_no_errors; + int rx_cipher_icv_errors; + int rx_cipher_mic_errors; + int rx_cipher_invalid_key_errors; + + int tx_stbc; + + uint8_t rf24_20mhz; + uint8_t rf24_40mhz; + uint8_t patch_dac; + uint8_t txmixgain_2ghz; + uint8_t txmixgain_5ghz; + +#ifdef RT2860_DEBUG + int debug; +#endif +}; + +#endif /* #ifndef _RT2860_SOFTC_H_ */ Index: sys/dev/rt2860/rt2860_txdesc.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_txdesc.h @@ -0,0 +1,48 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_TXDESC_H_ +#define _RT2860_TXDESC_H_ + +#define RT2860_TXDESC_SDL1_BURST (1 << 15) +#define RT2860_TXDESC_SDL1_LASTSEG (1 << 14) + +#define RT2860_TXDESC_SDL0_DDONE (1 << 15) +#define RT2860_TXDESC_SDL0_LASTSEG (1 << 14) + +#define RT2860_TXDESC_FLAGS_SHIFT 0 +#define RT2860_TXDESC_FLAGS_MASK 0xf9 +#define RT2860_TXDESC_FLAGS_WI_VALID (1 << 0) + +#define RT2860_TXDESC_QSEL_SHIFT 1 +#define RT2860_TXDESC_QSEL_MASK 0x3 +#define RT2860_TXDESC_QSEL_MGMT 0 +#define RT2860_TXDESC_QSEL_HCCA 1 +#define RT2860_TXDESC_QSEL_EDCA 2 + +struct rt2860_txdesc +{ + uint32_t sdp0; + uint16_t sdl1; + uint16_t sdl0; + uint32_t sdp1; + uint8_t reserved[3]; + uint8_t qsel_flags; +} __packed; + +#endif /* #ifndef _RT2860_TXDESC_H_ */ Index: sys/dev/rt2860/rt2860_txwi.h =================================================================== --- /dev/null +++ sys/dev/rt2860/rt2860_txwi.h @@ -0,0 +1,92 @@ + +/*- + * Copyright (c) 2009-2010 Alexander Egorenkov + * Copyright (c) 2009 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. + */ + +#ifndef _RT2860_TXWI_H_ +#define _RT2860_TXWI_H_ + +#define RT2860_TXWI_FLAGS_SHIFT 0 +#define RT2860_TXWI_FLAGS_MASK 0x1f +#define RT2860_TXWI_FLAGS_AMPDU (1 << 4) +#define RT2860_TXWI_FLAGS_TS (1 << 3) +#define RT2860_TXWI_FLAGS_CFACK (1 << 2) +#define RT2860_TXWI_FLAGS_MIMOPS (1 << 1) +#define RT2860_TXWI_FLAGS_FRAG (1 << 0) + +#define RT2860_TXWI_MPDU_DENSITY_SHIFT 5 +#define RT2860_TXWI_MPDU_DENSITY_MASK 0x7 + +#define RT2860_TXWI_TXOP_SHIFT 0 +#define RT2860_TXWI_TXOP_MASK 0x3 +#define RT2860_TXWI_TXOP_HT 0 +#define RT2860_TXWI_TXOP_PIFS 1 +#define RT2860_TXWI_TXOP_SIFS 2 +#define RT2860_TXWI_TXOP_BACKOFF 3 + +#define RT2860_TXWI_MCS_SHIFT 0 +#define RT2860_TXWI_MCS_MASK 0x7f +#define RT2860_TXWI_MCS_SHOTPRE (1 << 3) + +#define RT2860_TXWI_BW_SHIFT 7 +#define RT2860_TXWI_BW_MASK 0x1 +#define RT2860_TXWI_BW_20 0 +#define RT2860_TXWI_BW_40 1 + +#define RT2860_TXWI_SHORTGI_SHIFT 0 +#define RT2860_TXWI_SHORTGI_MASK 0x1 + +#define RT2860_TXWI_STBC_SHIFT 1 +#define RT2860_TXWI_STBC_MASK 0x3 + +#define RT2860_TXWI_IFS_SHIFT 3 +#define RT2860_TXWI_IFS_MASK 0x1 + +#define RT2860_TXWI_PHYMODE_SHIFT 6 +#define RT2860_TXWI_PHYMODE_MASK 0x3 +#define RT2860_TXWI_PHYMODE_CCK 0 +#define RT2860_TXWI_PHYMODE_OFDM 1 +#define RT2860_TXWI_PHYMODE_HT_MIXED 2 +#define RT2860_TXWI_PHYMODE_HT_GF 3 + +#define RT2860_TXWI_XFLAGS_SHIFT 0 +#define RT2860_TXWI_XFLAGS_MASK 0x3 +#define RT2860_TXWI_XFLAGS_NSEQ (1 << 1) +#define RT2860_TXWI_XFLAGS_ACK (1 << 0) + +#define RT2860_TXWI_BAWIN_SIZE_SHIFT 2 +#define RT2860_TXWI_BAWIN_SIZE_MASK 0x3f + +#define RT2860_TXWI_MPDU_LEN_SHIFT 0 +#define RT2860_TXWI_MPDU_LEN_MASK 0xfff + +#define RT2860_TXWI_PID_SHIFT 12 +#define RT2860_TXWI_PID_MASK 0xf + +struct rt2860_txwi +{ + uint8_t mpdu_density_flags; + uint8_t txop; + uint8_t bw_mcs; + uint8_t phymode_ifs_stbc_shortgi; + uint8_t bawin_size_xflags; + uint8_t wcid; + uint16_t pid_mpdu_len; + uint32_t iv; + uint32_t eiv; +} __packed; + +#endif /* #ifndef _RT2860_TXWI_H_ */