Index: sys/dev/ic/rt2860.c *************** *** 0 **** --- 1,3269 ---- + /* $OpenBSD: rt2860.c,v 1.33 2009/03/29 21:53:52 sthen Exp $ */ + + /*- + * Copyright (c) 2007, 2008 + * 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. + */ + + /*- + * Ralink Technology RT2860 chipset driver + * http://www.ralinktech.com/ + */ + + #include "bpfilter.h" + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #if NBPFILTER > 0 + #include + #endif + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + + #include + #include + #include + + #include + #include + + #include + #include + #include + + #include + + #define nitems(_a) __arraycount(_a) + #define letoh16 le16toh + #define ic_softc ic_ifp->if_softc + #define caddr_t char * + + #define IEEE80211_QOS_ACK_POLICY_NOACK 0x20 /* XXX */ + #define IEEE80211_CIPHER_CCMP IEEE80211_CIPHER_AES_CCM + #define IEEE80211_KEY_TX IEEE80211_KEY_XMIT + #define IEEE80211_F_WEPON IEEE80211_F_PRIVACY + + #define ieee80211_notify_dtim(ic) ((void)0) + #define ieee80211_release_node(ic, ni) ieee80211_free_node(ni) + + #define ic_set_key ic_crypto.cs_key_set + #define ic_delete_key ic_crypto.cs_key_delete + #define ic_updateedca ic_wme.wme_update + #define ic_edca_ac ic_wme.wme_params + + #define ac_ecwmax wmep_logcwmax + #define ac_ecwmin wmep_logcwmin + #define ac_aifsn wmep_aifsn + #define ac_txoplimit wmep_txopLimit + + #define k_cipher wk_cipher->ic_cipher + #define k_flags wk_flags + #define k_id wk_keyix + #define k_key wk_key + #define k_len wk_keylen + #define k_tsc wk_keytsc + + #define EDCA_AC_VO WME_AC_VO + #define EDCA_AC_VI WME_AC_VI + #define EDCA_AC_BK WME_AC_BK + #define EDCA_AC_BE WME_AC_BE + + #define EDCA_NUM_AC WME_NUM_AC + + #ifdef RAL_DEBUG + #define DPRINTF(x) do { if (rt2860_debug > 0) printf x; } while (0) + #define DPRINTFN(n, x) do { if (rt2860_debug >= (n)) printf x; } while (0) + int rt2860_debug = 1; + #else + #define DPRINTF(x) + #define DPRINTFN(n, x) + #endif + + int rt2860_alloc_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); + void rt2860_reset_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); + void rt2860_free_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); + int rt2860_alloc_tx_pool(struct rt2860_softc *); + void rt2860_free_tx_pool(struct rt2860_softc *); + int rt2860_alloc_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); + void rt2860_reset_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); + void rt2860_free_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); + struct ieee80211_node *rt2860_node_alloc(struct ieee80211_node_table *); + int rt2860_media_change(struct ifnet *); + void rt2860_iter_func(void *, struct ieee80211_node *); + void rt2860_updatestats(struct rt2860_softc *); + void rt2860_newassoc(struct ieee80211_node *, int); + int rt2860_newstate(struct ieee80211com *, enum ieee80211_state, + int); + uint16_t rt2860_eeprom_read(struct rt2860_softc *, uint8_t); + void rt2860_intr_coherent(struct rt2860_softc *); + void rt2860_drain_stats_fifo(struct rt2860_softc *); + void rt2860_tx_intr(struct rt2860_softc *, int); + void rt2860_rx_intr(struct rt2860_softc *); + void rt2860_tbtt_intr(struct rt2860_softc *); + void rt2860_gp_intr(struct rt2860_softc *); + int rt2860_tx(struct rt2860_softc *, struct mbuf *, + struct ieee80211_node *); + void rt2860_start(struct ifnet *); + void rt2860_watchdog(struct ifnet *); + int rt2860_ioctl(struct ifnet *, u_long, void *); + void rt2860_mcu_bbp_write(struct rt2860_softc *, uint8_t, uint8_t); + uint8_t rt2860_mcu_bbp_read(struct rt2860_softc *, uint8_t); + void rt2860_rf_write(struct rt2860_softc *, uint8_t, uint32_t); + int rt2860_mcu_cmd(struct rt2860_softc *, uint8_t, uint16_t); + void rt2860_enable_mrr(struct rt2860_softc *); + void rt2860_set_txpreamble(struct rt2860_softc *); + void rt2860_set_basicrates(struct rt2860_softc *); + void rt2860_select_chan_group(struct rt2860_softc *, int); + void rt2860_set_chan(struct rt2860_softc *, + struct ieee80211_channel *); + void rt2860_set_leds(struct rt2860_softc *, uint16_t); + void rt2860_set_gp_timer(struct rt2860_softc *, int); + void rt2860_set_bssid(struct rt2860_softc *, const uint8_t *); + void rt2860_set_macaddr(struct rt2860_softc *, const uint8_t *); + void rt2860_updateslot(struct ifnet *ifp); + void rt2860_updateprot(struct ieee80211com *); + int rt2860_updateedca(struct ieee80211com *); + int rt2860_set_key(struct ieee80211com *, + const struct ieee80211_key *, const u_int8_t *); + int rt2860_delete_key(struct ieee80211com *, + const struct ieee80211_key *); + #if NBPFILTER > 0 + int8_t rt2860_rssi2dbm(struct rt2860_softc *, uint8_t, uint8_t); + #endif + const char * rt2860_get_rf(uint8_t); + int rt2860_read_eeprom(struct rt2860_softc *); + int rt2860_bbp_init(struct rt2860_softc *); + int rt2860_txrx_enable(struct rt2860_softc *); + int rt2860_init(struct ifnet *); + void rt2860_stop(struct ifnet *, int); + int rt2860_load_microcode(struct rt2860_softc *); + void rt2860_calib(struct rt2860_softc *); + #ifndef IEEE80211_STA_ONLY + int rt2860_setup_beacon(struct rt2860_softc *); + #endif + void rt2860_enable_tsf_sync(struct rt2860_softc *); + void rt2860_power(int, void *); + + static const struct { + uint32_t reg; + uint32_t val; + } rt2860_def_mac[] = { + RT2860_DEF_MAC + }; + + static const struct { + uint8_t reg; + uint8_t val; + } rt2860_def_bbp[] = { + RT2860_DEF_BBP + }; + + static const struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; + } rt2860_rf2850[] = { + RT2860_RF2850 + }; + + int + rt2860_attach(void *xsc, int id) + { + struct rt2860_softc *sc = xsc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &sc->sc_if; + int i, qid, ntries, error; + + sc->amrr.amrr_min_success_threshold = 1; + sc->amrr.amrr_max_success_threshold = 15; + + /* wait for NIC to initialize */ + for (ntries = 0; ntries < 100; ntries++) { + sc->mac_rev = RAL_READ(sc, RT2860_ASIC_VER_ID); + if (sc->mac_rev != 0 && sc->mac_rev != 0xffffffff) + break; + DELAY(10); + } + if (ntries == 100) { + printf("%s: timeout waiting for NIC to initialize\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + if ((sc->mac_rev >> 16) != 0x2860 && + (id == PCI_PRODUCT_RALINK_RT2890 || + id == PCI_PRODUCT_RALINK_RT2790 || + id == PCI_PRODUCT_AWT_RT2890)) + sc->sc_flags |= RT2860_ADVANCED_PS; + + /* retrieve RF rev. no and various other things from EEPROM */ + rt2860_read_eeprom(sc); + printf("%s: address %s\n", sc->sc_dev.dv_xname, + ether_sprintf(ic->ic_myaddr)); + printf("%s: MAC/BBP RT%X (rev 0x%04X), RF %s (MIMO %dT%dR)\n", + sc->sc_dev.dv_xname, sc->mac_rev >> 16, sc->mac_rev & 0xffff, + rt2860_get_rf(sc->rf_rev), sc->ntxchains, sc->nrxchains); + + /* + * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings. + */ + for (qid = 0; qid < 6; qid++) { + if ((error = rt2860_alloc_tx_ring(sc, &sc->txq[qid])) != 0) { + printf("%s: could not allocate Tx ring %d\n", + sc->sc_dev.dv_xname, qid); + goto fail1; + } + } + + if ((error = rt2860_alloc_rx_ring(sc, &sc->rxq)) != 0) { + printf("%s: could not allocate Rx ring\n", + sc->sc_dev.dv_xname); + goto fail1; + } + + if ((error = rt2860_alloc_tx_pool(sc)) != 0) { + printf("%s: could not allocate Tx pool\n", + sc->sc_dev.dv_xname); + goto fail2; + } + + /* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */ + sc->mgtqid = (sc->mac_rev == 0x28600100) ? EDCA_AC_VO : 5; + + ic->ic_ifp = ifp; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + ic->ic_state = IEEE80211_S_INIT; + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_MONITOR | /* monitor mode supported */ + #ifndef IEEE80211_STA_ONLY + IEEE80211_C_IBSS | /* IBSS mode supported */ + IEEE80211_C_HOSTAP | /* HostAP mode supported */ + #ifdef notyet + IEEE80211_C_APPMGT | /* HostAP power management */ + #endif + #endif + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_WPA; /* 802.11i */ + + if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850) { + /* set supported .11a rates */ + ic->ic_sup_rates[IEEE80211_MODE_11A] = + ieee80211_std_rateset_11a; + + /* set supported .11a channels */ + for (i = 14; i < nitems(rt2860_rf2850); i++) { + uint8_t chan = rt2860_rf2850[i].chan; + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); + ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A; + } + } + + /* set supported .11b and .11g rates */ + ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; + + /* set supported .11b and .11g channels (1 through 14) */ + for (i = 1; i <= 14; i++) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); + ic->ic_channels[i].ic_flags = + IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; + } + + /* HW supports up to 255 STAs (0-254) in HostAP and IBSS modes */ + ic->ic_max_aid = min(IEEE80211_AID_MAX, RT2860_WCID_MAX); + + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = rt2860_init; + ifp->if_ioctl = rt2860_ioctl; + ifp->if_start = rt2860_start; + ifp->if_watchdog = rt2860_watchdog; + IFQ_SET_READY(&ifp->if_snd); + memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); + + if_attach(ifp); + ieee80211_ifattach(ic); + ic->ic_node_alloc = rt2860_node_alloc; + ic->ic_newassoc = rt2860_newassoc; + ic->ic_updateslot = rt2860_updateslot; + ic->ic_updateedca = rt2860_updateedca; + ic->ic_set_key = rt2860_set_key; + ic->ic_delete_key = rt2860_delete_key; + /* override state transition machine */ + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = rt2860_newstate; + ieee80211_media_init(ic, rt2860_media_change, ieee80211_media_status); + + #if NBPFILTER > 0 + bpfattach2(ifp, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + 64, + &sc->sc_drvbpf); + + sc->sc_rxtap_len = sizeof sc->sc_rxtapu; + sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); + sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2860_RX_RADIOTAP_PRESENT); + + sc->sc_txtap_len = sizeof sc->sc_txtapu; + sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); + sc->sc_txtap.wt_ihdr.it_present = htole32(RT2860_TX_RADIOTAP_PRESENT); + #endif + + ieee80211_announce(ic); + + /* + * Make sure the interface is shutdown during reboot. + */ + sc->sc_sdhook = shutdownhook_establish(rt2860_shutdown, sc); + if (sc->sc_sdhook == NULL) { + printf("%s: WARNING: unable to establish shutdown hook\n", + sc->sc_dev.dv_xname); + } + + sc->sc_powerhook = + powerhook_establish(sc->sc_dev.dv_xname, rt2860_power, sc); + if (sc->sc_powerhook == NULL) { + printf("%s: WARNING: unable to establish power hook\n", + sc->sc_dev.dv_xname); + } + + return 0; + + fail2: rt2860_free_rx_ring(sc, &sc->rxq); + fail1: while (--qid >= 0) + rt2860_free_tx_ring(sc, &sc->txq[qid]); + return error; + } + + int + rt2860_detach(void *xsc) + { + struct rt2860_softc *sc = xsc; + struct ifnet *ifp = &sc->sc_if; + int qid; + + ieee80211_ifdetach(&sc->sc_ic); /* free all nodes */ + if_detach(ifp); + + if (sc->sc_powerhook != NULL) + powerhook_disestablish(sc->sc_powerhook); + + if (sc->sc_sdhook != NULL) + shutdownhook_disestablish(sc->sc_sdhook); + + for (qid = 0; qid < 6; qid++) + rt2860_free_tx_ring(sc, &sc->txq[qid]); + rt2860_free_rx_ring(sc, &sc->rxq); + rt2860_free_tx_pool(sc); + + return 0; + } + + int + rt2860_alloc_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) + { + int nsegs, size, error; + + size = RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd); + + error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT, &ring->map); + if (error != 0) { + printf("%s: could not create DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + /* Tx rings must be 4-DWORD aligned */ + error = bus_dmamem_alloc(sc->sc_dmat, size, 16, 0, &ring->seg, 1, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &ring->seg, nsegs, size, + (void **)&ring->txd, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: can't map DMA memory\n", sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, ring->map, ring->txd, size, NULL, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + memset(ring->txd, 0, size); + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, size, BUS_DMASYNC_PREWRITE); + + ring->paddr = ring->map->dm_segs[0].ds_addr; + + return 0; + + fail: rt2860_free_tx_ring(sc, ring); + return error; + } + + void + rt2860_reset_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) + { + struct rt2860_tx_data *data; + int i; + + for (i = 0; i < RT2860_TX_RING_COUNT; i++) { + if ((data = ring->data[i]) == NULL) + continue; /* nothing mapped in this slot */ + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + data->m= NULL; + data->ni = NULL; /* node already freed */ + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + ring->data[i] = NULL; + } + + ring->queued = 0; + ring->cur = ring->next = 0; + } + + void + rt2860_free_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) + { + struct rt2860_tx_data *data; + int i; + + if (ring->txd != NULL) { + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, + ring->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ring->map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)ring->txd, + RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd)); + bus_dmamem_free(sc->sc_dmat, &ring->seg, 1); + } + if (ring->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, ring->map); + + for (i = 0; i < RT2860_TX_RING_COUNT; i++) { + if ((data = ring->data[i]) == NULL) + continue; /* nothing mapped in this slot */ + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + } + } + + /* + * Allocate a pool of TX Wireless Information blocks. + */ + int + rt2860_alloc_tx_pool(struct rt2860_softc *sc) + { + caddr_t vaddr; + bus_addr_t paddr; + int i, nsegs, size, error; + + size = RT2860_TX_POOL_COUNT * RT2860_TXWI_DMASZ; + + /* init data_pool early in case of failure.. */ + SLIST_INIT(&sc->data_pool); + + error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT, &sc->txwi_map); + if (error != 0) { + printf("%s: could not create DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, + &sc->txwi_seg, 1, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &sc->txwi_seg, nsegs, size, + (void **)&sc->txwi_vaddr, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: can't map DMA memory\n", sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, sc->txwi_map, sc->txwi_vaddr, + size, NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + memset(sc->txwi_vaddr, 0, size); + bus_dmamap_sync(sc->sc_dmat, sc->txwi_map, 0, size, + BUS_DMASYNC_PREWRITE); + + vaddr = sc->txwi_vaddr; + paddr = sc->txwi_map->dm_segs[0].ds_addr; + for (i = 0; i < RT2860_TX_POOL_COUNT; i++) { + struct rt2860_tx_data *data = &sc->data[i]; + + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, + RT2860_MAX_SCATTER, MCLBYTES, 0, BUS_DMA_NOWAIT, + &data->map); + if (error != 0) { + printf("%s: could not create DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + data->txwi = (struct rt2860_txwi *)vaddr; + data->paddr = paddr; + vaddr += RT2860_TXWI_DMASZ; + paddr += RT2860_TXWI_DMASZ; + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + } + + return 0; + + fail: rt2860_free_tx_pool(sc); + return error; + } + + void + rt2860_free_tx_pool(struct rt2860_softc *sc) + { + if (sc->txwi_vaddr != NULL) { + bus_dmamap_sync(sc->sc_dmat, sc->txwi_map, 0, + sc->txwi_map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, sc->txwi_map); + bus_dmamem_unmap(sc->sc_dmat, sc->txwi_vaddr, + RT2860_TX_POOL_COUNT * RT2860_TXWI_DMASZ); + bus_dmamem_free(sc->sc_dmat, &sc->txwi_seg, 1); + } + if (sc->txwi_map != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->txwi_map); + + while (!SLIST_EMPTY(&sc->data_pool)) { + struct rt2860_tx_data *data; + data = SLIST_FIRST(&sc->data_pool); + bus_dmamap_destroy(sc->sc_dmat, data->map); + SLIST_REMOVE_HEAD(&sc->data_pool, next); + } + } + + int + rt2860_alloc_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) + { + int i, nsegs, size, error; + + size = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd); + + error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT, &ring->map); + if (error != 0) { + printf("%s: could not create DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + /* Rx ring must be 4-DWORD aligned */ + error = bus_dmamem_alloc(sc->sc_dmat, size, 16, 0, &ring->seg, 1, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &ring->seg, nsegs, size, + (void **)&ring->rxd, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: can't map DMA memory\n", sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, ring->map, ring->rxd, size, NULL, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + memset(ring->rxd, 0, size); + ring->paddr = ring->map->dm_segs[0].ds_addr; + + for (i = 0; i < RT2860_RX_RING_COUNT; i++) { + struct rt2860_rx_data *data = &ring->data[i]; + struct rt2860_rxd *rxd = &ring->rxd[i]; + + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, + 0, BUS_DMA_NOWAIT, &data->map); + if (error != 0) { + printf("%s: could not create DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + MGETHDR(data->m, M_DONTWAIT, MT_DATA); + if (data->m == NULL) { + printf("%s: could not allocate Rx mbuf\n", + sc->sc_dev.dv_xname); + error = ENOBUFS; + goto fail; + } + MCLGET(data->m, M_DONTWAIT); + if (!(data->m->m_flags & M_EXT)) { + printf("%s: could not allocate Rx mbuf cluster\n", + sc->sc_dev.dv_xname); + error = ENOBUFS; + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(data->m, void *), MCLBYTES, NULL, + BUS_DMA_READ | BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + rxd->sdp0 = htole32(data->map->dm_segs[0].ds_addr); + rxd->sdl0 = htole16(MCLBYTES); + } + + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, size, BUS_DMASYNC_PREWRITE); + + return 0; + + fail: rt2860_free_rx_ring(sc, ring); + return error; + } + + void + rt2860_reset_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) + { + int i; + + for (i = 0; i < RT2860_RX_RING_COUNT; i++) + ring->rxd[i].sdl0 &= ~htole16(RT2860_RX_DDONE); + + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, ring->map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + ring->cur = 0; + } + + void + rt2860_free_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) + { + int i; + + if (ring->rxd != NULL) { + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, + ring->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ring->map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)ring->rxd, + RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd)); + bus_dmamem_free(sc->sc_dmat, &ring->seg, 1); + } + if (ring->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, ring->map); + + for (i = 0; i < RT2860_RX_RING_COUNT; i++) { + struct rt2860_rx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + } + if (data->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, data->map); + } + } + + struct ieee80211_node * + rt2860_node_alloc(struct ieee80211_node_table *nt) + { + struct rt2860_node *rn; + + rn = malloc(sizeof (struct rt2860_node), M_80211_NODE, + M_NOWAIT | M_ZERO); + + return (rn != NULL) ? &rn->ni : NULL; + } + + int + rt2860_media_change(struct ifnet *ifp) + { + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + uint8_t rate, ridx; + int error; + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return error; + + if (ic->ic_fixed_rate != -1) { + rate = ic->ic_sup_rates[ic->ic_curmode]. + rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; + for (ridx = 0; ridx <= RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + sc->fixed_ridx = ridx; + } + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + rt2860_init(ifp); + + return 0; + } + + void + rt2860_iter_func(void *arg, struct ieee80211_node *ni) + { + struct rt2860_softc *sc = arg; + uint8_t wcid; + + wcid = RT2860_AID2WCID(ni->ni_associd); + ieee80211_amrr_choose(&sc->amrr, ni, &sc->amn[wcid]); + } + + void + rt2860_updatestats(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + + #ifndef IEEE80211_STA_ONLY + /* + * In IBSS or HostAP modes (when the hardware sends beacons), the + * MAC can run into a livelock and start sending CTS-to-self frames + * like crazy if protection is enabled. Fortunately, we can detect + * when such a situation occurs and reset the MAC. + */ + if (ic->ic_curmode != IEEE80211_M_STA) { + /* check if we're in a livelock situation.. */ + uint32_t tmp = RAL_READ(sc, RT2860_DEBUG); + if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) { + /* ..and reset MAC/BBP for a while.. */ + DPRINTF(("CTS-to-self livelock detected\n")); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST); + RAL_BARRIER_WRITE(sc); + DELAY(1); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, + RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + } + } + #endif + if (ic->ic_opmode == IEEE80211_M_STA) + rt2860_iter_func(sc, ic->ic_bss); + #ifndef IEEE80211_STA_ONLY + else + ieee80211_iterate_nodes(&ic->ic_sta, rt2860_iter_func, sc); + #endif + } + + void + rt2860_newassoc(struct ieee80211_node *ni, int isnew) + { + struct ieee80211com *ic = ni->ni_ic; + struct rt2860_softc *sc = ic->ic_ifp->if_softc; + struct rt2860_node *rn = (void *)ni; + struct ieee80211_rateset *rs = &ni->ni_rates; + uint8_t rate, wcid = 0; + int ridx, i, j; + + if (isnew && ni->ni_associd != 0) { + /* only interested in true associations */ + wcid = RT2860_AID2WCID(ni->ni_associd); + + /* init WCID table entry */ + RAL_WRITE_REGION_1(sc, RT2860_WCID_ENTRY(wcid), + ni->ni_macaddr, IEEE80211_ADDR_LEN); + } + DPRINTF(("new assoc isnew=%d addr=%s WCID=%d\n", + isnew, ether_sprintf(ni->ni_macaddr), wcid)); + + ieee80211_amrr_node_init(&sc->amrr, &sc->amn[wcid]); + /* start at lowest available bit-rate, AMRR will raise */ + ni->ni_txrate = 0; + + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i] & IEEE80211_RATE_VAL; + /* convert 802.11 rate to hardware rate index */ + for (ridx = 0; ridx <= RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + rn->ridx[i] = ridx; + /* determine rate of control response frames */ + for (j = i; j >= 0; j--) { + if ((rs->rs_rates[j] & IEEE80211_RATE_BASIC) && + rt2860_rates[rn->ridx[i]].phy == + rt2860_rates[rn->ridx[j]].phy) + break; + } + if (j >= 0) { + rn->ctl_ridx[i] = rn->ridx[j]; + } else { + /* no basic rate found, use mandatory one */ + rn->ctl_ridx[i] = rt2860_rates[ridx].ctl_ridx; + } + DPRINTF(("rate=0x%02x ridx=%d ctl_ridx=%d\n", + rs->rs_rates[i], rn->ridx[i], rn->ctl_ridx[i])); + } + } + + int + rt2860_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) + { + struct rt2860_softc *sc = ic->ic_ifp->if_softc; + enum ieee80211_state ostate; + uint32_t tmp; + + ostate = ic->ic_state; + + if (ostate == IEEE80211_S_RUN) { + /* turn link LED off */ + rt2860_set_leds(sc, RT2860_LED_RADIO); + } + + switch (nstate) { + case IEEE80211_S_INIT: + if (ostate == IEEE80211_S_RUN) { + /* abort TSF synchronization */ + tmp = RAL_READ(sc, RT2860_BCN_TIME_CFG); + RAL_WRITE(sc, RT2860_BCN_TIME_CFG, + tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | + RT2860_TBTT_TIMER_EN)); + } + rt2860_set_gp_timer(sc, 0); + break; + + case IEEE80211_S_SCAN: + rt2860_set_chan(sc, ic->ic_bss->ni_chan); + if (ostate != IEEE80211_S_SCAN) + rt2860_set_gp_timer(sc, 150); + break; + + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + rt2860_set_gp_timer(sc, 0); + rt2860_set_chan(sc, ic->ic_bss->ni_chan); + break; + + case IEEE80211_S_RUN: + rt2860_set_gp_timer(sc, 0); + rt2860_set_chan(sc, ic->ic_bss->ni_chan); + + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + rt2860_updateslot(ic->ic_ifp); + rt2860_enable_mrr(sc); + rt2860_set_txpreamble(sc); + rt2860_set_basicrates(sc); + rt2860_set_bssid(sc, ic->ic_bss->ni_bssid); + } + + #ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS) + (void)rt2860_setup_beacon(sc); + #endif + + if (ic->ic_opmode == IEEE80211_M_STA) { + /* fake a join to init the tx rate */ + rt2860_newassoc(ic->ic_bss, 1); + } + + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + rt2860_enable_tsf_sync(sc); + rt2860_set_gp_timer(sc, 500); + } + + /* turn link LED on */ + rt2860_set_leds(sc, RT2860_LED_RADIO | + (IEEE80211_IS_CHAN_2GHZ(ic->ic_bss->ni_chan) ? + RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ)); + break; + } + + return sc->sc_newstate(ic, nstate, arg); + } + + /* + * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46, + * 93C66 or 93C86). + */ + uint16_t + rt2860_eeprom_read(struct rt2860_softc *sc, uint8_t addr) + { + uint32_t tmp; + uint16_t val; + int n; + + /* clock C once before the first command */ + RT2860_EEPROM_CTL(sc, 0); + + RT2860_EEPROM_CTL(sc, RT2860_S); + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); + RT2860_EEPROM_CTL(sc, RT2860_S); + + /* write start bit (1) */ + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D); + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C); + + /* write READ opcode (10) */ + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D); + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C); + RT2860_EEPROM_CTL(sc, RT2860_S); + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); + + /* write address (A5-A0 or A7-A0) */ + n = ((RAL_READ(sc, RT2860_PCI_EECTRL) & 0x30) == 0) ? 5 : 7; + for (; n >= 0; n--) { + RT2860_EEPROM_CTL(sc, RT2860_S | + (((addr >> n) & 1) << RT2860_SHIFT_D)); + RT2860_EEPROM_CTL(sc, RT2860_S | + (((addr >> n) & 1) << RT2860_SHIFT_D) | RT2860_C); + } + + RT2860_EEPROM_CTL(sc, RT2860_S); + + /* read data Q15-Q0 */ + val = 0; + for (n = 15; n >= 0; n--) { + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); + tmp = RAL_READ(sc, RT2860_PCI_EECTRL); + val |= ((tmp & RT2860_Q) >> RT2860_SHIFT_Q) << n; + RT2860_EEPROM_CTL(sc, RT2860_S); + } + + RT2860_EEPROM_CTL(sc, 0); + + /* clear Chip Select and clock C */ + RT2860_EEPROM_CTL(sc, RT2860_S); + RT2860_EEPROM_CTL(sc, 0); + RT2860_EEPROM_CTL(sc, RT2860_C); + + return val; + } + + void + rt2860_intr_coherent(struct rt2860_softc *sc) + { + uint32_t tmp; + + /* DMA finds data coherent event when checking the DDONE bit */ + + DPRINTF(("Tx/Rx Coherent interrupt\n")); + + /* restart DMA engine */ + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + tmp &= ~(RT2860_TX_WB_DDONE | RT2860_RX_DMA_EN | RT2860_TX_DMA_EN); + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + (void)rt2860_txrx_enable(sc); + } + + void + rt2860_drain_stats_fifo(struct rt2860_softc *sc) + { + struct ifnet *ifp = &sc->sc_if; + struct ieee80211_amrr_node *amn; + uint32_t stat; + uint8_t wcid, mcs, pid; + + /* drain Tx status FIFO (maxsize = 16) */ + while ((stat = RAL_READ(sc, RT2860_TX_STAT_FIFO)) & RT2860_TXQ_VLD) { + DPRINTFN(4, ("tx stat 0x%08x\n", stat)); + + wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff; + + /* if no ACK was requested, no feedback is available */ + if (!(stat & RT2860_TXQ_ACKREQ) || wcid == 0xff) + continue; + + /* update per-STA AMRR stats */ + amn = &sc->amn[wcid]; + amn->amn_txcnt++; + if (stat & RT2860_TXQ_OK) { + /* + * Check if there were retries, ie if the Tx success + * rate is different from the requested rate. Note + * that it works only because we do not allow rate + * fallback from OFDM to CCK. + */ + mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f; + pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf; + if (mcs + 1 != pid) + amn->amn_retrycnt++; + } else { + amn->amn_retrycnt++; + ifp->if_oerrors++; + } + } + } + + void + rt2860_tx_intr(struct rt2860_softc *sc, int qid) + { + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = ic->ic_ifp; + struct rt2860_tx_ring *ring = &sc->txq[qid]; + uint32_t hw; + + rt2860_drain_stats_fifo(sc); + + hw = RAL_READ(sc, RT2860_TX_DTX_IDX(qid)); + while (ring->next != hw) { + struct rt2860_tx_data *data = ring->data[ring->next]; + + if (data != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + data->m= NULL; + ieee80211_release_node(ic, data->ni); + data->ni = NULL; + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + ring->data[ring->next] = NULL; + + ifp->if_opackets++; + } + ring->queued--; + ring->next = (ring->next + 1) % RT2860_TX_RING_COUNT; + } + + sc->sc_tx_timer = 0; + if (ring->queued < RT2860_TX_RING_COUNT) + sc->qfullmsk &= ~(1 << qid); + ifp->if_flags &= ~IFF_OACTIVE; + rt2860_start(ifp); + } + + /* + * Return the Rx chain with the highest RSSI for a given frame. + */ + static __inline uint8_t + rt2860_maxrssi_chain(struct rt2860_softc *sc, const struct rt2860_rxwi *rxwi) + { + uint8_t rxchain = 0; + + if (sc->nrxchains > 1) { + if (rxwi->rssi[1] > rxwi->rssi[rxchain]) + rxchain = 1; + if (sc->nrxchains > 2) + if (rxwi->rssi[2] > rxwi->rssi[rxchain]) + rxchain = 2; + } + return rxchain; + } + + void + rt2860_rx_intr(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = ic->ic_ifp; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m, *m1; + uint32_t hw; + uint8_t ant, rssi; + int error; + int decrypted; + #if NBPFILTER > 0 + struct rt2860_rx_radiotap_header *tap; + uint16_t phy; + #endif + + hw = RAL_READ(sc, RT2860_FS_DRX_IDX) & 0xfff; + while (sc->rxq.cur != hw) { + struct rt2860_rx_data *data = &sc->rxq.data[sc->rxq.cur]; + struct rt2860_rxd *rxd = &sc->rxq.rxd[sc->rxq.cur]; + struct rt2860_rxwi *rxwi; + + bus_dmamap_sync(sc->sc_dmat, sc->rxq.map, + sc->rxq.cur * sizeof (struct rt2860_rxd), + sizeof (struct rt2860_rxd), BUS_DMASYNC_POSTREAD); + + if (__predict_false(!(rxd->sdl0 & htole16(RT2860_RX_DDONE)))) { + DPRINTF(("RXD DDONE bit not set!\n")); + break; /* should not happen */ + } + + if (__predict_false(rxd->flags & + htole32(RT2860_RX_CRCERR | RT2860_RX_ICVERR))) { + ifp->if_ierrors++; + goto skip; + } + + if (__predict_false(rxd->flags & htole32(RT2860_RX_MICERR))) { + /* report MIC failures to net80211 for TKIP */ + ic->ic_stats.is_rx_tkipmic++; + rxwi = mtod(data->m, struct rt2860_rxwi *); + wh = (void *)(rxwi + 1); + ieee80211_notify_michael_failure(ic, wh, 0/* XXX */); + ifp->if_ierrors++; + goto skip; + } + + MGETHDR(m1, M_DONTWAIT, MT_DATA); + if (__predict_false(m1 == NULL)) { + ifp->if_ierrors++; + goto skip; + } + MCLGET(m1, M_DONTWAIT); + if (__predict_false(!(m1->m_flags & M_EXT))) { + m_freem(m1); + ifp->if_ierrors++; + goto skip; + } + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, data->map); + + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(m1, void *), MCLBYTES, NULL, + BUS_DMA_READ | BUS_DMA_NOWAIT); + if (__predict_false(error != 0)) { + m_freem(m1); + + /* try to reload the old mbuf */ + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(data->m, void *), MCLBYTES, NULL, + BUS_DMA_READ | BUS_DMA_NOWAIT); + if (__predict_false(error != 0)) { + panic("%s: could not load old rx mbuf", + sc->sc_dev.dv_xname); + } + /* physical address may have changed */ + rxd->sdp0 = htole32(data->map->dm_segs[0].ds_addr); + ifp->if_ierrors++; + goto skip; + } + + /* + * New mbuf successfully loaded, update Rx ring and continue + * processing. + */ + m = data->m; + data->m = m1; + rxd->sdp0 = htole32(data->map->dm_segs[0].ds_addr); + + rxwi = mtod(m, struct rt2860_rxwi *); + + /* finalize mbuf */ + m->m_pkthdr.rcvif = ifp; + m->m_data = (caddr_t)(rxwi + 1); + m->m_pkthdr.len = m->m_len = letoh16(rxwi->len) & 0xfff; + + wh = mtod(m, struct ieee80211_frame *); + decrypted = 0; + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + /* frame is decrypted by hardware */ + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + decrypted = 1; + } + + /* HW may insert 2 padding bytes after 802.11 header */ + if (rxd->flags & htole32(RT2860_RX_L2PAD)) { + u_int hdrlen = ieee80211_hdrspace(ic, wh); + ovbcopy(wh, (caddr_t)wh + 2, hdrlen); + m->m_data += 2; + wh = mtod(m, struct ieee80211_frame *); + } + + ant = rt2860_maxrssi_chain(sc, rxwi); + rssi = rxwi->rssi[ant]; + + #if NBPFILTER > 0 + if (__predict_true(sc->sc_drvbpf == NULL)) + goto skipbpf; + + tap = &sc->sc_rxtap; + tap->wr_flags = 0; + tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); + tap->wr_antsignal = rssi; + tap->wr_antenna = ant; + tap->wr_dbm_antsignal = rt2860_rssi2dbm(sc, rssi, ant); + tap->wr_rate = 2; /* in case it can't be found below */ + phy = letoh16(rxwi->phy); + switch (phy & RT2860_PHY_MODE) { + case RT2860_PHY_CCK: + switch ((phy & RT2860_PHY_MCS) & ~RT2860_PHY_SHPRE) { + case 0: tap->wr_rate = 2; break; + case 1: tap->wr_rate = 4; break; + case 2: tap->wr_rate = 11; break; + case 3: tap->wr_rate = 22; break; + } + if (phy & RT2860_PHY_SHPRE) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + break; + case RT2860_PHY_OFDM: + switch (phy & RT2860_PHY_MCS) { + case 0: tap->wr_rate = 12; break; + case 1: tap->wr_rate = 18; break; + case 2: tap->wr_rate = 24; break; + case 3: tap->wr_rate = 36; break; + case 4: tap->wr_rate = 48; break; + case 5: tap->wr_rate = 72; break; + case 6: tap->wr_rate = 96; break; + case 7: tap->wr_rate = 108; break; + } + break; + } + bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); + skipbpf: + #endif + /* grab a reference to the source node */ + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + + /* send the frame to the 802.11 layer */ + if (decrypted) { + u_int icflags = ic->ic_flags; + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; /* XXX */ + ieee80211_input(ic, m, ni, rssi, 0); + ic->ic_flags = icflags; + } else + ieee80211_input(ic, m, ni, rssi, 0); + + /* node is no longer needed */ + ieee80211_release_node(ic, ni); + + skip: rxd->sdl0 &= ~htole16(RT2860_RX_DDONE); + + bus_dmamap_sync(sc->sc_dmat, sc->rxq.map, + sc->rxq.cur * sizeof (struct rt2860_rxd), + sizeof (struct rt2860_rxd), BUS_DMASYNC_PREWRITE); + + sc->rxq.cur = (sc->rxq.cur + 1) % RT2860_RX_RING_COUNT; + } + + /* tell HW what we have processed */ + RAL_WRITE(sc, RT2860_RX_CALC_IDX, + (sc->rxq.cur - 1) % RT2860_RX_RING_COUNT); + + /* + * In HostAP mode, ieee80211_input() will enqueue packets in if_snd + * without calling if_start(). + */ + if (!IFQ_IS_EMPTY(&ifp->if_snd) && !(ifp->if_flags & IFF_OACTIVE)) + rt2860_start(ifp); + } + + void + rt2860_tbtt_intr(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + + #ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* one less beacon until next DTIM */ + if (ic->ic_dtim_count == 0) + ic->ic_dtim_count = ic->ic_dtim_period - 1; + else + ic->ic_dtim_count--; + + /* update dynamic parts of beacon */ + rt2860_setup_beacon(sc); + + /* flush buffered multicast frames */ + if (ic->ic_dtim_count == 0) + ieee80211_notify_dtim(ic); + } + #endif + /* check if protection mode has changed */ + if ((sc->sc_ic_flags ^ ic->ic_flags) & IEEE80211_F_USEPROT) { + rt2860_updateprot(ic); + sc->sc_ic_flags = ic->ic_flags; + } + } + + void + rt2860_gp_intr(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + + DPRINTFN(2, ("GP timeout state=%d\n", ic->ic_state)); + + if (ic->ic_state == IEEE80211_S_SCAN) + ieee80211_next_scan(ic); + else if (ic->ic_state == IEEE80211_S_RUN) + rt2860_updatestats(sc); + } + + int + rt2860_intr(void *arg) + { + struct rt2860_softc *sc = arg; + uint32_t r; + + r = RAL_READ(sc, RT2860_INT_STATUS); + if (__predict_false(r == 0xffffffff)) + return 0; /* device likely went away */ + if (r == 0) + return 0; /* not for us */ + + /* acknowledge interrupts */ + RAL_WRITE(sc, RT2860_INT_STATUS, r); + + if (r & RT2860_TX_RX_COHERENT) + rt2860_intr_coherent(sc); + + if (r & RT2860_MAC_INT_2) /* TX status */ + rt2860_drain_stats_fifo(sc); + + if (r & RT2860_TX_DONE_INT5) + rt2860_tx_intr(sc, 5); + + if (r & RT2860_RX_DONE_INT) + rt2860_rx_intr(sc); + + if (r & RT2860_TX_DONE_INT4) + rt2860_tx_intr(sc, 4); + + if (r & RT2860_TX_DONE_INT3) + rt2860_tx_intr(sc, 3); + + if (r & RT2860_TX_DONE_INT2) + rt2860_tx_intr(sc, 2); + + if (r & RT2860_TX_DONE_INT1) + rt2860_tx_intr(sc, 1); + + if (r & RT2860_TX_DONE_INT0) + rt2860_tx_intr(sc, 0); + + if (r & RT2860_MAC_INT_0) /* TBTT */ + rt2860_tbtt_intr(sc); + + if (r & RT2860_MAC_INT_3) /* Auto wakeup */ + /* TBD wakeup */; + + if (r & RT2860_MAC_INT_4) /* GP timer */ + rt2860_gp_intr(sc); + + return 1; + } + + int + rt2860_tx(struct rt2860_softc *sc, struct mbuf *m, struct ieee80211_node *ni) + { + struct ieee80211com *ic = &sc->sc_ic; + struct rt2860_node *rn = (void *)ni; + struct rt2860_tx_ring *ring; + struct rt2860_tx_data *data; + struct rt2860_txd *txd; + struct rt2860_txwi *txwi; + struct ieee80211_frame *wh; + struct mbuf *m1; + bus_dma_segment_t *seg; + u_int hdrlen; + uint16_t qos, dur; + uint8_t type, qsel, mcs, pid, tid, qid; + int nsegs, ntxds, hasqos, ridx, ctl_ridx, error; + + ntxds = 0; /* XXX GCC */ + qos = 0; /* XXX GCC */ + + /* the data pool contains at least one element, pick the first */ + data = SLIST_FIRST(&sc->data_pool); + + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_hdrspace(ic, wh); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) { + qos = ((struct ieee80211_qosframe *)wh)->i_qos[0]; + tid = qos & IEEE80211_QOS_TID; + qid = TID_TO_WME_AC(tid); + } else { + tid = 0; + qid = (type == IEEE80211_FC0_TYPE_MGT) ? + sc->mgtqid : EDCA_AC_BE; + } + ring = &sc->txq[qid]; + + /* pickup a rate index */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + type != IEEE80211_FC0_TYPE_DATA) { + ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? + RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; + ctl_ridx = rt2860_rates[ridx].ctl_ridx; + } else if (ic->ic_fixed_rate != -1) { + ridx = sc->fixed_ridx; + ctl_ridx = rt2860_rates[ridx].ctl_ridx; + } else { + ridx = rn->ridx[ni->ni_txrate]; + ctl_ridx = rn->ctl_ridx[ni->ni_txrate]; + } + + /* get MCS code from rate index */ + mcs = rt2860_rates[ridx].mcs; + + /* setup TX Wireless Information */ + txwi = data->txwi; + txwi->flags = 0; + txwi->xflags = 0; + txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ? + RT2860_AID2WCID(ni->ni_associd) : 0xff; + txwi->len = htole16(m->m_pkthdr.len); + if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { + txwi->phy = htole16(RT2860_PHY_CCK); + if (ridx != RT2860_RIDX_CCK1 && + (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_PHY_SHPRE; + } else + txwi->phy = htole16(RT2860_PHY_OFDM); + txwi->phy |= htole16(mcs); + + /* + * We store the MCS code into the driver-private PacketID field. + * The PacketID is latched into TX_STAT_FIFO when Tx completes so + * that we know at which initial rate the frame was transmitted. + * We add 1 to the MCS code because setting the PacketID field to + * 0 means that we don't want feedback in TX_STAT_FIFO. + */ + pid = (mcs + 1) & 0xf; + txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT); + + /* check if RTS/CTS or CTS-to-self protection is required */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold || + ((ic->ic_flags & IEEE80211_F_USEPROT) && + rt2860_rates[ridx].phy == IEEE80211_T_OFDM))) + txwi->txop = RT2860_TX_TXOP_HT; + else + txwi->txop = RT2860_TX_TXOP_BACKOFF; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACK_POLICY_NOACK)) { + txwi->xflags |= RT2860_TX_ACK; + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + dur = rt2860_rates[ctl_ridx].sp_ack_dur; + else + dur = rt2860_rates[ctl_ridx].lp_ack_dur; + *(uint16_t *)wh->i_dur = htole16(dur + sc->sifs); + } + #ifndef IEEE80211_STA_ONLY + /* 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)) + /* NOTE: beacons do not pass through tx_data() */ + txwi->flags |= RT2860_TX_TS; + #endif + + #if NBPFILTER > 0 + if (__predict_false(sc->sc_drvbpf != NULL)) { + struct rt2860_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = rt2860_rates[ridx].rate; + tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); + tap->wt_hwqueue = qid; + if (mcs & RT2860_PHY_SHPRE) + tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m); + } + #endif + + /* copy and trim 802.11 header */ + memcpy(txwi + 1, wh, hdrlen); + m_adj(m, hdrlen); + + error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, + BUS_DMA_NOWAIT); + if (__predict_false(error != 0 && error != EFBIG)) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, error); + m_freem(m); + return error; + } + if (__predict_true(error == 0)) { + /* determine how many TXDs are required */ + ntxds = 1 + (data->map->dm_nsegs / 2); + + if (ring->queued + ntxds >= RT2860_TX_RING_COUNT) { + /* not enough free TXDs, force mbuf defrag */ + bus_dmamap_unload(sc->sc_dmat, data->map); + error = EFBIG; + } + } + if (__predict_false(error != 0)) { + /* too many fragments, linearize */ + MGETHDR(m1, M_DONTWAIT, MT_DATA); + if (m1 == NULL) { + m_freem(m); + return ENOBUFS; + } + if (m->m_pkthdr.len > MHLEN) { + MCLGET(m1, M_DONTWAIT); + if (!(m1->m_flags & M_EXT)) { + m_freem(m); + m_freem(m1); + return ENOBUFS; + } + } + m_copydata(m, 0, m->m_pkthdr.len, mtod(m1, caddr_t)); + m1->m_pkthdr.len = m1->m_len = m->m_pkthdr.len; + m_freem(m); + m = m1; + + error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, + BUS_DMA_NOWAIT); + if (__predict_false(error != 0)) { + printf("%s: can't map mbuf (error %d)\n", + sc->sc_dev.dv_xname, error); + m_freem(m); + return error; + } + + /* determine how many TXDs are now required */ + ntxds = 1 + (data->map->dm_nsegs / 2); + + if (ring->queued + ntxds >= RT2860_TX_RING_COUNT) { + /* this is a hopeless case, drop the mbuf! */ + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(m); + return ENOBUFS; + } + } + + qsel = (qid < EDCA_NUM_AC) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_MGMT; + + /* first segment is TXWI + 802.11 header */ + txd = &ring->txd[ring->cur]; + txd->sdp0 = htole32(data->paddr); + txd->sdl0 = htole16(sizeof (struct rt2860_txwi) + hdrlen); + txd->flags = qsel; + + /* setup payload segments */ + seg = data->map->dm_segs; + for (nsegs = data->map->dm_nsegs; nsegs >= 2; nsegs -= 2) { + txd->sdp1 = htole32(seg->ds_addr); + txd->sdl1 = htole16(seg->ds_len); + seg++; + ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT; + /* grab a new Tx descriptor */ + txd = &ring->txd[ring->cur]; + txd->sdp0 = htole32(seg->ds_addr); + txd->sdl0 = htole16(seg->ds_len); + txd->flags = qsel; + seg++; + } + /* finalize last segment */ + if (nsegs > 0) { + txd->sdp1 = htole32(seg->ds_addr); + txd->sdl1 = htole16(seg->ds_len | RT2860_TX_LS1); + } else { + txd->sdl0 |= htole16(RT2860_TX_LS0); + txd->sdl1 = 0; + } + + /* remove from the free pool and link it into the SW Tx slot */ + SLIST_REMOVE_HEAD(&sc->data_pool, next); + data->m = m; + data->ni = ni; + ring->data[ring->cur] = data; + + bus_dmamap_sync(sc->sc_dmat, sc->txwi_map, + (caddr_t)txwi - sc->txwi_vaddr, RT2860_TXWI_DMASZ, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, data->map, 0, data->map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, ring->map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + DPRINTFN(4, ("sending frame qid=%d wcid=%d nsegs=%d ridx=%d\n", + qid, txwi->wcid, data->map->dm_nsegs, ridx)); + + ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT; + ring->queued += ntxds; + if (ring->queued >= RT2860_TX_RING_COUNT) + sc->qfullmsk |= 1 << qid; + + /* kick Tx */ + RAL_WRITE(sc, RT2860_TX_CTX_IDX(qid), ring->cur); + + return 0; + } + + void + rt2860_start(struct ifnet *ifp) + { + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct mbuf *m; + struct ether_header *eh; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + for (;;) { + if (SLIST_EMPTY(&sc->data_pool) || sc->qfullmsk != 0) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + /* send pending management frames first */ + IF_DEQUEUE(&ic->ic_mgtq, m); + if (m != NULL) { + ni = (void *)m->m_pkthdr.rcvif; + goto sendit; + } + if (ic->ic_state != IEEE80211_S_RUN) + break; + + #if 0 + /* send buffered frames for power-save mode */ + IF_DEQUEUE(&ic->ic_pwrsaveq, m); + if (m != NULL) { + ni = (void *)m->m_pkthdr.rcvif; + goto sendit; + } + #endif + + /* encapsulate and send data frames */ + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + if (m->m_len < sizeof(struct ether_header) && + !(m = m_pullup(m, sizeof(struct ether_header)))) + continue; + + eh = mtod(m, struct ether_header *); + ni = ieee80211_find_txnode(ic, eh->ether_dhost); + if (ni == NULL) { + m_freem(m); + continue; + } + + #if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m); + #endif + if ((m = ieee80211_encap(ic, m, ni)) == NULL) { + ieee80211_release_node(ic, ni); + ifp->if_oerrors++; + continue; + } + sendit: + #if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m); + #endif + if (rt2860_tx(sc, m, ni) != 0) { + ieee80211_release_node(ic, ni); + ifp->if_oerrors++; + continue; + } + + sc->sc_tx_timer = 5; + ifp->if_timer = 1; + } + } + + void + rt2860_watchdog(struct ifnet *ifp) + { + struct rt2860_softc *sc = ifp->if_softc; + + ifp->if_timer = 0; + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + printf("%s: device timeout\n", sc->sc_dev.dv_xname); + rt2860_init(ifp); + ifp->if_oerrors++; + return; + } + ifp->if_timer = 1; + } + + ieee80211_watchdog(&sc->sc_ic); + } + + int + rt2860_ioctl(struct ifnet *ifp, u_long cmd, void *data) + { + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifaddr *ifa; + struct ifreq *ifr; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; + #ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&ic->ic_ac, ifa); + #endif + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + rt2860_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + rt2860_stop(ifp, 1); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + ifr = (struct ifreq *)data; + if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) + error = 0; + break; + + case SIOCS80211CHANNEL: + /* + * This allows for fast channel switching in monitor mode + * (used by kismet). In IBSS mode, we must explicitly reset + * the interface to generate a new beacon frame. + */ + error = ieee80211_ioctl(ic, cmd, data); + if (error == ENETRESET && + ic->ic_opmode == IEEE80211_M_MONITOR) { + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) + rt2860_set_chan(sc, ic->ic_ibss_chan); + error = 0; + } + break; + + default: + error = ieee80211_ioctl(ic, cmd, data); + } + + if (error == ENETRESET) { + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) + rt2860_init(ifp); + error = 0; + } + + splx(s); + + return error; + } + + /* + * Reading and writing from/to the BBP is different from RT2560 and RT2661. + * We access the BBP through the 8051 microcontroller unit which means that + * the microcode must be loaded first. + */ + void + rt2860_mcu_bbp_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val) + { + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK)) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s: could not write to BBP through MCU\n", + sc->sc_dev.dv_xname); + return; + } + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | + RT2860_BBP_CSR_KICK | reg << 8 | val); + RAL_BARRIER_WRITE(sc); + + (void)rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0); + DELAY(1000); + } + + uint8_t + rt2860_mcu_bbp_read(struct rt2860_softc *sc, uint8_t reg) + { + uint32_t val; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK)) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s: could not read from BBP through MCU\n", + sc->sc_dev.dv_xname); + return 0; + } + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | + RT2860_BBP_CSR_KICK | RT2860_BBP_CSR_READ | reg << 8); + RAL_BARRIER_WRITE(sc); + + (void)rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0); + DELAY(1000); + + for (ntries = 0; ntries < 100; ntries++) { + val = RAL_READ(sc, RT2860_H2M_BBPAGENT); + if (!(val & RT2860_BBP_CSR_KICK)) + return val & 0xff; + DELAY(1); + } + printf("%s: could not read from BBP through MCU\n", + sc->sc_dev.dv_xname); + + return 0; + } + + /* + * Write to one of the 4 programmable 24-bit RF registers. + */ + void + rt2860_rf_write(struct rt2860_softc *sc, uint8_t reg, uint32_t val) + { + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_RF_CSR_CFG0) & RT2860_RF_REG_CTRL)) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s: could not write to RF\n", sc->sc_dev.dv_xname); + return; + } + + /* RF registers are 24-bit on the RT2860 */ + tmp = RT2860_RF_REG_CTRL | 24 << RT2860_RF_REG_WIDTH_SHIFT | + (val & 0x3fffff) << 2 | (reg & 3); + RAL_WRITE(sc, RT2860_RF_CSR_CFG0, tmp); + } + + /* + * Send a command to the 8051 microcontroller unit. + */ + int + rt2860_mcu_cmd(struct rt2860_softc *sc, uint8_t cmd, uint16_t arg) + { + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_H2M_MAILBOX) & RT2860_H2M_BUSY)) + break; + DELAY(2); + } + if (ntries == 100) + return EIO; + + RAL_WRITE(sc, RT2860_H2M_MAILBOX, + RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_HOST_CMD, cmd); + + return 0; + } + + void + rt2860_enable_mrr(struct rt2860_softc *sc) + { + #define CCK(mcs) (mcs) + #define OFDM(mcs) (1 << 3 | (mcs)) + RAL_WRITE(sc, RT2860_LG_FBK_CFG0, + OFDM(6) << 28 | /* 54->48 */ + OFDM(5) << 24 | /* 48->36 */ + OFDM(4) << 20 | /* 36->24 */ + OFDM(3) << 16 | /* 24->18 */ + OFDM(2) << 12 | /* 18->12 */ + OFDM(1) << 8 | /* 12-> 9 */ + OFDM(0) << 4 | /* 9-> 6 */ + OFDM(0)); /* 6-> 6 */ + + RAL_WRITE(sc, RT2860_LG_FBK_CFG1, + CCK(2) << 12 | /* 11->5.5 */ + CCK(1) << 8 | /* 5.5-> 2 */ + CCK(0) << 4 | /* 2-> 1 */ + CCK(0)); /* 1-> 1 */ + #undef OFDM + #undef CCK + } + + void + rt2860_set_txpreamble(struct rt2860_softc *sc) + { + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_AUTO_RSP_CFG); + tmp &= ~RT2860_CCK_SHORT_EN; + if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RT2860_CCK_SHORT_EN; + RAL_WRITE(sc, RT2860_AUTO_RSP_CFG, tmp); + } + + void + rt2860_set_basicrates(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + + /* set basic rates mask */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x003); + else if (ic->ic_curmode == IEEE80211_MODE_11A) + RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x150); + else /* 11g */ + RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x15f); + } + + void + rt2860_select_chan_group(struct rt2860_softc *sc, int group) + { + uint32_t tmp; + + rt2860_mcu_bbp_write(sc, 62, 0x37 - sc->lna[group]); + rt2860_mcu_bbp_write(sc, 63, 0x37 - sc->lna[group]); + rt2860_mcu_bbp_write(sc, 64, 0x37 - sc->lna[group]); + rt2860_mcu_bbp_write(sc, 86, 0x00); + + if (group == 0) { + if (sc->ext_2ghz_lna) { + rt2860_mcu_bbp_write(sc, 82, 0x62); + rt2860_mcu_bbp_write(sc, 75, 0x46); + } else { + rt2860_mcu_bbp_write(sc, 82, 0x84); + rt2860_mcu_bbp_write(sc, 75, 0x50); + } + } else { + if (sc->ext_5ghz_lna) { + rt2860_mcu_bbp_write(sc, 82, 0xf2); + rt2860_mcu_bbp_write(sc, 75, 0x46); + } else { + rt2860_mcu_bbp_write(sc, 82, 0xf2); + rt2860_mcu_bbp_write(sc, 75, 0x50); + } + } + + tmp = RAL_READ(sc, RT2860_TX_BAND_CFG); + tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P); + tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P; + RAL_WRITE(sc, RT2860_TX_BAND_CFG, tmp); + + /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ + tmp = RT2860_RFTR_EN | RT2860_TRSW_EN; + if (group == 0) { /* 2GHz */ + tmp |= RT2860_PA_PE_G0_EN | RT2860_LNA_PE_G0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_G1_EN; + if (sc->nrxchains > 1) + tmp |= RT2860_LNA_PE_G1_EN; + } else { /* 5GHz */ + tmp |= RT2860_PA_PE_A0_EN | RT2860_LNA_PE_A0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_A1_EN; + if (sc->nrxchains > 1) + tmp |= RT2860_LNA_PE_A1_EN; + } + RAL_WRITE(sc, RT2860_TX_PIN_CFG, tmp); + + /* set initial AGC value */ + if (group == 0) + rt2860_mcu_bbp_write(sc, 66, 0x2e + sc->lna[0]); + else + rt2860_mcu_bbp_write(sc, 66, 0x32 + (sc->lna[group] * 5) / 3); + } + + void + rt2860_set_chan(struct rt2860_softc *sc, struct ieee80211_channel *c) + { + struct ieee80211com *ic = &sc->sc_ic; + const struct rfprog *rfprog = rt2860_rf2850; + uint32_t r2, r3, r4; + int8_t txpow1, txpow2; + u_int i, chan, group; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rfprog[i].chan != chan; i++); + + r2 = rfprog[i].r2; + if (sc->ntxchains == 1) + r2 |= 1 << 12; /* 1T: disable Tx chain 2 */ + if (sc->nrxchains == 1) + r2 |= 1 << 15 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ + else if (sc->nrxchains == 2) + r2 |= 1 << 4; /* 2R: disable Rx chain 3 */ + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + if (IEEE80211_IS_CHAN_5GHZ(c)) { + if (txpow1 >= 0) + txpow1 = txpow1 << 1; + else + txpow1 = (7 + txpow1) << 1 | 1; + if (txpow2 >= 0) + txpow2 = txpow2 << 1; + else + txpow2 = (7 + txpow2) << 1 | 1; + } + r3 = rfprog[i].r3 | txpow1 << 7; + r4 = rfprog[i].r4 | sc->freq << 13 | txpow2 << 4; + + rt2860_rf_write(sc, RT2860_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RT2860_RF2, r2); + rt2860_rf_write(sc, RT2860_RF3, r3); + rt2860_rf_write(sc, RT2860_RF4, r4); + + DELAY(200); + + rt2860_rf_write(sc, RT2860_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RT2860_RF2, r2); + rt2860_rf_write(sc, RT2860_RF3, r3 | 1); + rt2860_rf_write(sc, RT2860_RF4, r4); + + DELAY(200); + + rt2860_rf_write(sc, RT2860_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RT2860_RF2, r2); + rt2860_rf_write(sc, RT2860_RF3, r3); + rt2860_rf_write(sc, RT2860_RF4, r4); + + /* 802.11a uses a 16 microseconds short interframe space */ + sc->sifs = IEEE80211_IS_CHAN_5GHZ(c) ? 16 : 10; + + /* determine channel group */ + if (chan <= 14) + group = 0; + else if (chan <= 64) + group = 1; + else if (chan <= 128) + group = 2; + else + group = 3; + + /* XXX necessary only when group has changed! */ + rt2860_select_chan_group(sc, group); + + DELAY(1000); + } + + void + rt2860_set_leds(struct rt2860_softc *sc, uint16_t which) + { + (void)rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LEDS, + which | (sc->leds & 0x7f)); + } + + /* + * Hardware has a general-purpose programmable timer interrupt that can + * periodically raise MAC_INT_4. + */ + void + rt2860_set_gp_timer(struct rt2860_softc *sc, int ms) + { + uint32_t tmp; + + /* disable GP timer before reprogramming it */ + tmp = RAL_READ(sc, RT2860_INT_TIMER_EN); + RAL_WRITE(sc, RT2860_INT_TIMER_EN, tmp & ~RT2860_GP_TIMER_EN); + + if (ms == 0) + return; + + tmp = RAL_READ(sc, RT2860_INT_TIMER_CFG); + ms *= 16; /* Unit: 64us */ + tmp = (tmp & 0xffff) | ms << RT2860_GP_TIMER_SHIFT; + RAL_WRITE(sc, RT2860_INT_TIMER_CFG, tmp); + + /* enable GP timer */ + tmp = RAL_READ(sc, RT2860_INT_TIMER_EN); + RAL_WRITE(sc, RT2860_INT_TIMER_EN, tmp | RT2860_GP_TIMER_EN); + } + + void + rt2860_set_bssid(struct rt2860_softc *sc, const uint8_t *bssid) + { + RAL_WRITE(sc, RT2860_MAC_BSSID_DW0, + bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); + RAL_WRITE(sc, RT2860_MAC_BSSID_DW1, + bssid[4] | bssid[5] << 8); + } + + void + rt2860_set_macaddr(struct rt2860_softc *sc, const uint8_t *addr) + { + RAL_WRITE(sc, RT2860_MAC_ADDR_DW0, + addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + RAL_WRITE(sc, RT2860_MAC_ADDR_DW1, + addr[4] | addr[5] << 8); + } + + void + rt2860_updateslot(struct ifnet *ifp) + { + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_BKOFF_SLOT_CFG); + tmp &= ~0xff; + tmp |= (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; + RAL_WRITE(sc, RT2860_BKOFF_SLOT_CFG, tmp); + } + + void + rt2860_updateprot(struct ieee80211com *ic) + { + struct rt2860_softc *sc = ic->ic_softc; + uint32_t tmp; + + tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; + /* setup protection frame rate (MCS code) */ + tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? + rt2860_rates[RT2860_RIDX_OFDM6].mcs : + rt2860_rates[RT2860_RIDX_CCK11].mcs; + + /* CCK frames don't require protection */ + RAL_WRITE(sc, RT2860_CCK_PROT_CFG, tmp); + + if (ic->ic_flags & IEEE80211_F_USEPROT) { + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + tmp |= RT2860_PROT_CTRL_RTS_CTS; + else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + tmp |= RT2860_PROT_CTRL_CTS; + } + RAL_WRITE(sc, RT2860_OFDM_PROT_CFG, tmp); + } + + int + rt2860_updateedca(struct ieee80211com *ic) + { + struct rt2860_softc *sc = ic->ic_softc; + int aci; + + /* update MAC TX configuration registers */ + for (aci = 0; aci < EDCA_NUM_AC; aci++) { + RAL_WRITE(sc, RT2860_EDCA_AC_CFG(aci), + ic->ic_edca_ac[aci].ac_ecwmax << 16 | + ic->ic_edca_ac[aci].ac_ecwmin << 12 | + ic->ic_edca_ac[aci].ac_aifsn << 8 | + ic->ic_edca_ac[aci].ac_txoplimit); + } + + /* update SCH/DMA registers too */ + RAL_WRITE(sc, RT2860_WMM_AIFSN_CFG, + ic->ic_edca_ac[EDCA_AC_VO].ac_aifsn << 12 | + ic->ic_edca_ac[EDCA_AC_VI].ac_aifsn << 8 | + ic->ic_edca_ac[EDCA_AC_BK].ac_aifsn << 4 | + ic->ic_edca_ac[EDCA_AC_BE].ac_aifsn); + RAL_WRITE(sc, RT2860_WMM_CWMIN_CFG, + ic->ic_edca_ac[EDCA_AC_VO].ac_ecwmin << 12 | + ic->ic_edca_ac[EDCA_AC_VI].ac_ecwmin << 8 | + ic->ic_edca_ac[EDCA_AC_BK].ac_ecwmin << 4 | + ic->ic_edca_ac[EDCA_AC_BE].ac_ecwmin); + RAL_WRITE(sc, RT2860_WMM_CWMAX_CFG, + ic->ic_edca_ac[EDCA_AC_VO].ac_ecwmax << 12 | + ic->ic_edca_ac[EDCA_AC_VI].ac_ecwmax << 8 | + ic->ic_edca_ac[EDCA_AC_BK].ac_ecwmax << 4 | + ic->ic_edca_ac[EDCA_AC_BE].ac_ecwmax); + RAL_WRITE(sc, RT2860_WMM_TXOP0_CFG, + ic->ic_edca_ac[EDCA_AC_BK].ac_txoplimit << 16 | + ic->ic_edca_ac[EDCA_AC_BE].ac_txoplimit); + RAL_WRITE(sc, RT2860_WMM_TXOP1_CFG, + ic->ic_edca_ac[EDCA_AC_VO].ac_txoplimit << 16 | + ic->ic_edca_ac[EDCA_AC_VI].ac_txoplimit); + return 0; + } + + int + rt2860_set_key(struct ieee80211com *ic, const struct ieee80211_key *k, + const u_int8_t *mac) + { + struct rt2860_softc *sc = ic->ic_softc; + struct ieee80211_node *ni = ic->ic_bss; + bus_size_t base; + uint32_t attr; + uint8_t mode, wcid, iv[8]; + + /* map net80211 cipher to RT2860 security mode */ + switch (k->k_cipher) { + case IEEE80211_CIPHER_WEP: + ((struct ieee80211_key *)k)->k_flags |= IEEE80211_KEY_GROUP; /* XXX */ + if (k->wk_keylen == 5) + mode = RT2860_MODE_WEP40; + else + mode = RT2860_MODE_WEP104; + break; + case IEEE80211_CIPHER_TKIP: + mode = RT2860_MODE_TKIP; + break; + case IEEE80211_CIPHER_CCMP: + mode = RT2860_MODE_AES_CCMP; + break; + default: + return 0; + } + + if (k->k_flags & IEEE80211_KEY_GROUP) { + wcid = 0; /* NB: update WCID0 for group keys */ + base = RT2860_SKEY(0, k->k_id); + } else { + wcid = RT2860_AID2WCID(ni->ni_associd); + base = RT2860_PKEY(wcid); + } + + if (k->k_cipher == IEEE80211_CIPHER_TKIP) { + RAL_WRITE_REGION_1(sc, base, k->k_key, 16); + #ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + RAL_WRITE_REGION_1(sc, base + 16, &k->k_key[16], 8); + RAL_WRITE_REGION_1(sc, base + 24, &k->k_key[24], 8); + } else + #endif + { + RAL_WRITE_REGION_1(sc, base + 16, &k->k_key[24], 8); + RAL_WRITE_REGION_1(sc, base + 24, &k->k_key[16], 8); + } + } else + RAL_WRITE_REGION_1(sc, base, k->k_key, k->k_len); + + if (!(k->k_flags & IEEE80211_KEY_GROUP) || + (k->k_flags & IEEE80211_KEY_TX)) { + /* set initial packet number in IV+EIV */ + if (k->k_cipher == IEEE80211_CIPHER_WEP) { + uint32_t val = arc4random(); + /* skip weak IVs from Fluhrer/Mantin/Shamir */ + if (val >= 0x03ff00 && (val & 0xf8ff00) == 0x00ff00) + val += 0x000100; + iv[0] = val; + iv[1] = val >> 8; + iv[2] = val >> 16; + iv[3] = k->k_id << 6; + iv[4] = iv[5] = iv[6] = iv[7] = 0; + } else { + if (k->k_cipher == IEEE80211_CIPHER_TKIP) { + iv[0] = k->k_tsc >> 8; + iv[1] = (iv[0] | 0x20) & 0x7f; + iv[2] = k->k_tsc; + } else /* CCMP */ { + iv[0] = k->k_tsc; + iv[1] = k->k_tsc >> 8; + iv[2] = 0; + } + iv[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; + iv[4] = k->k_tsc >> 16; + iv[5] = k->k_tsc >> 24; + iv[6] = k->k_tsc >> 32; + iv[7] = k->k_tsc >> 40; + } + RAL_WRITE_REGION_1(sc, RT2860_IVEIV(wcid), iv, 8); + } + + if (k->k_flags & IEEE80211_KEY_GROUP) { + /* install group key */ + attr = RAL_READ(sc, RT2860_SKEY_MODE_0_7); + attr &= ~(0xf << (k->k_id * 4)); + attr |= mode << (k->k_id * 4); + RAL_WRITE(sc, RT2860_SKEY_MODE_0_7, attr); + } else { + /* install pairwise key */ + attr = RAL_READ(sc, RT2860_WCID_ATTR(wcid)); + attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN; + RAL_WRITE(sc, RT2860_WCID_ATTR(wcid), attr); + } + return 1; + } + + int + rt2860_delete_key(struct ieee80211com *ic, const struct ieee80211_key *k) + { + struct rt2860_softc *sc = ic->ic_softc; + struct ieee80211_node *ni = ic->ic_bss; + uint32_t attr; + uint8_t wcid; + + if (k->k_cipher == IEEE80211_CIPHER_WEP) + ((struct ieee80211_key *)k)->k_flags |= IEEE80211_KEY_GROUP; /* XXX */ + + if (k->k_flags & IEEE80211_KEY_GROUP) { + /* remove group key */ + attr = RAL_READ(sc, RT2860_SKEY_MODE_0_7); + attr &= ~(0xf << (k->k_id * 4)); + RAL_WRITE(sc, RT2860_SKEY_MODE_0_7, attr); + + } else { + /* remove pairwise key */ + wcid = RT2860_AID2WCID(ni->ni_associd); + attr = RAL_READ(sc, RT2860_WCID_ATTR(wcid)); + attr &= ~0xf; + RAL_WRITE(sc, RT2860_WCID_ATTR(wcid), attr); + } + return 1; + } + + #if NBPFILTER > 0 + int8_t + rt2860_rssi2dbm(struct rt2860_softc *sc, uint8_t rssi, uint8_t rxchain) + { + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *c = ic->ic_ibss_chan; + int delta; + + if (IEEE80211_IS_CHAN_5GHZ(c)) { + u_int chan = ieee80211_chan2ieee(ic, c); + delta = sc->rssi_5ghz[rxchain]; + + /* determine channel group */ + if (chan <= 64) + delta -= sc->lna[1]; + else if (chan <= 128) + delta -= sc->lna[2]; + else + delta -= sc->lna[3]; + } else + delta = sc->rssi_2ghz[rxchain] - sc->lna[0]; + + return -12 - delta - rssi; + } + #endif + + /* + * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. + * Used to adjust per-rate Tx power registers. + */ + static __inline uint32_t + b4inc(uint32_t b32, int8_t delta) + { + int8_t i, b4; + + for (i = 0; i < 8; i++) { + b4 = b32 & 0xf; + b4 += delta; + if (b4 < 0) + b4 = 0; + else if (b4 > 0xf) + b4 = 0xf; + b32 = b32 >> 4 | b4 << 28; + } + return b32; + } + + const char * + rt2860_get_rf(uint8_t rev) + { + switch (rev) { + case RT2860_RF_2820: return "RT2820"; + case RT2860_RF_2850: return "RT2850"; + case RT2860_RF_2720: return "RT2720"; + case RT2860_RF_2750: return "RT2750"; + default: return "unknown"; + } + } + + int + rt2860_read_eeprom(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + uint16_t val; + int8_t delta_2ghz, delta_5ghz; + int ridx, ant, i; + + /* read EEPROM version */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_VERSION); + DPRINTF(("EEPROM rev=%d, FAE=%d\n", val & 0xff, val >> 8)); + + /* read MAC address */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC01); + ic->ic_myaddr[0] = val & 0xff; + ic->ic_myaddr[1] = val >> 8; + val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC23); + ic->ic_myaddr[2] = val & 0xff; + ic->ic_myaddr[3] = val >> 8; + val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC45); + ic->ic_myaddr[4] = val & 0xff; + ic->ic_myaddr[5] = val >> 8; + + /* read country code */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_COUNTRY); + DPRINTF(("EEPROM region code=0x%04x\n", val)); + + /* read default BBP settings */ + for (i = 0; i < 8; i++) { + val = rt2860_eeprom_read(sc, RT2860_EEPROM_BBP_BASE + i); + sc->bbp[i].val = val & 0xff; + sc->bbp[i].reg = val >> 8; + DPRINTF(("BBP%d=0x%02x\n", sc->bbp[i].reg, sc->bbp[i].val)); + } + + /* read RF frequency offset from EEPROM */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_FREQ_LEDS); + sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; + DPRINTF(("EEPROM freq offset %d\n", sc->freq & 0xff)); + + if ((sc->leds = val >> 8) != 0xff) { + /* read LEDs operating mode */ + sc->led[0] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED1); + sc->led[1] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED2); + sc->led[2] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED3); + } else { + /* broken EEPROM, use default settings */ + sc->leds = 0x01; + sc->led[0] = 0x5555; + sc->led[1] = 0x2221; + sc->led[2] = 0xa9f8; + } + DPRINTF(("EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", + sc->leds, sc->led[0], sc->led[1], sc->led[2])); + + /* read RF information */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_ANTENNA); + if (val == 0xffff) { + /* broken EEPROM, default to RF2820 1T2R */ + DPRINTF(("invalid EEPROM antenna info, using default\n")); + sc->rf_rev = RT2860_RF_2820; + sc->ntxchains = 1; + sc->nrxchains = 2; + } else { + sc->rf_rev = (val >> 8) & 0xf; + sc->ntxchains = (val >> 4) & 0xf; + sc->nrxchains = val & 0xf; + } + DPRINTF(("EEPROM RF rev=0x%02x chains=%dT%dR\n", + sc->rf_rev, sc->ntxchains, sc->nrxchains)); + + /* check if RF supports automatic Tx access gain control */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_CONFIG); + DPRINTF(("EEPROM CFG 0x%04x\n", val)); + if ((val & 0xff) != 0xff) { + sc->ext_5ghz_lna = (val >> 3) & 1; + sc->ext_2ghz_lna = (val >> 2) & 1; + sc->calib_2ghz = sc->calib_5ghz = 0; /* XXX (val >> 1) & 1 */; + } + + if (sc->sc_flags & RT2860_ADVANCED_PS) { + /* read PCIe power save level */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_PCIE_PSLEVEL); + if ((val & 0xff) != 0xff) { + sc->pslevel = val & 0x3; + val = rt2860_eeprom_read(sc, RT2860_EEPROM_REV); + if (val >> 8 != 0x92 || !(val & 0x80)) + sc->pslevel = MIN(sc->pslevel, 1); + DPRINTF(("EEPROM PCIe PS Level=%d\n", sc->pslevel)); + } + } + /* read power settings for 2GHz channels */ + for (i = 0; i < 14; i += 2) { + val = rt2860_eeprom_read(sc, + RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2); + sc->txpow1[i + 0] = (int8_t)(val & 0xff); + sc->txpow1[i + 1] = (int8_t)(val >> 8); + + val = rt2860_eeprom_read(sc, + RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2); + sc->txpow2[i + 0] = (int8_t)(val & 0xff); + sc->txpow2[i + 1] = (int8_t)(val >> 8); + } + /* fix broken Tx power entries */ + for (i = 0; i < 14; i++) { + if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31) + sc->txpow1[i] = 5; + if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31) + sc->txpow2[i] = 5; + DPRINTF(("chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i])); + } + /* read power settings for 5GHz channels */ + for (i = 0; i < 36; i += 2) { + val = rt2860_eeprom_read(sc, + RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2); + sc->txpow1[i + 14] = (int8_t)(val & 0xff); + sc->txpow1[i + 15] = (int8_t)(val >> 8); + + val = rt2860_eeprom_read(sc, + RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2); + sc->txpow2[i + 14] = (int8_t)(val & 0xff); + sc->txpow2[i + 15] = (int8_t)(val >> 8); + } + /* fix broken Tx power entries */ + for (i = 0; i < 36; i++) { + if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15) + sc->txpow1[14 + i] = 5; + if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15) + sc->txpow2[14 + i] = 5; + DPRINTF(("chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i], + sc->txpow2[14 + i])); + } + + /* read Tx power compensation for each Tx rate */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_DELTAPWR); + delta_2ghz = delta_5ghz = 0; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_2ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_2ghz = -delta_2ghz; + } + val >>= 8; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_5ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_5ghz = -delta_5ghz; + } + DPRINTF(("power compensation=%d (2GHz), %d (5GHz)\n", + delta_2ghz, delta_5ghz)); + + for (ridx = 0; ridx < 5; ridx++) { + uint32_t reg; + + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx); + reg = (uint32_t)val << 16; + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx + 1); + reg |= val; + + sc->txpow20mhz[ridx] = reg; + sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); + sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); + + DPRINTF(("ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " + "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx], + sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx])); + } + + /* read factory-calibrated samples for temperature compensation */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_2GHZ); + sc->tssi_2ghz[0] = val & 0xff; /* [-4] */ + sc->tssi_2ghz[1] = val >> 8; /* [-3] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_2GHZ); + sc->tssi_2ghz[2] = val & 0xff; /* [-2] */ + sc->tssi_2ghz[3] = val >> 8; /* [-1] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_2GHZ); + sc->tssi_2ghz[4] = val & 0xff; /* [+0] */ + sc->tssi_2ghz[5] = val >> 8; /* [+1] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_2GHZ); + sc->tssi_2ghz[6] = val & 0xff; /* [+2] */ + sc->tssi_2ghz[7] = val >> 8; /* [+3] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_2GHZ); + sc->tssi_2ghz[8] = val & 0xff; /* [+4] */ + sc->step_2ghz = val >> 8; + DPRINTF(("TSSI 2GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x step=%d\n", 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->step_2ghz)); + /* check that ref value is correct, otherwise disable calibration */ + if (sc->tssi_2ghz[4] == 0xff) + sc->calib_2ghz = 0; + + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_5GHZ); + sc->tssi_5ghz[0] = val & 0xff; /* [-4] */ + sc->tssi_5ghz[1] = val >> 8; /* [-3] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_5GHZ); + sc->tssi_5ghz[2] = val & 0xff; /* [-2] */ + sc->tssi_5ghz[3] = val >> 8; /* [-1] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_5GHZ); + sc->tssi_5ghz[4] = val & 0xff; /* [+0] */ + sc->tssi_5ghz[5] = val >> 8; /* [+1] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_5GHZ); + sc->tssi_5ghz[6] = val & 0xff; /* [+2] */ + sc->tssi_5ghz[7] = val >> 8; /* [+3] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_5GHZ); + sc->tssi_5ghz[8] = val & 0xff; /* [+4] */ + sc->step_5ghz = val >> 8; + DPRINTF(("TSSI 5GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x step=%d\n", 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->step_5ghz)); + /* check that ref value is correct, otherwise disable calibration */ + if (sc->tssi_5ghz[4] == 0xff) + sc->calib_5ghz = 0; + + /* read RSSI offsets and LNA gains from EEPROM */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_2GHZ); + sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_2ghz[1] = val >> 8; /* Ant B */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_2GHZ); + sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ + sc->lna[2] = val >> 8; /* channel group 2 */ + + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_5GHZ); + sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_5ghz[1] = val >> 8; /* Ant B */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_5GHZ); + sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ + sc->lna[3] = val >> 8; /* channel group 3 */ + + val = rt2860_eeprom_read(sc, RT2860_EEPROM_LNA); + sc->lna[0] = val & 0xff; /* channel group 0 */ + sc->lna[1] = val >> 8; /* channel group 1 */ + + /* fix broken 5GHz LNA entries */ + if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { + DPRINTF(("invalid LNA for channel group %d\n", 2)); + sc->lna[2] = sc->lna[1]; + } + if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { + DPRINTF(("invalid LNA for channel group %d\n", 3)); + sc->lna[3] = sc->lna[1]; + } + + /* fix broken RSSI offset entries */ + for (ant = 0; ant < 3; ant++) { + if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { + DPRINTF(("invalid RSSI%d offset: %d (2GHz)\n", + ant + 1, sc->rssi_2ghz[ant])); + sc->rssi_2ghz[ant] = 0; + } + if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { + DPRINTF(("invalid RSSI%d offset: %d (5GHz)\n", + ant + 1, sc->rssi_5ghz[ant])); + sc->rssi_5ghz[ant] = 0; + } + } + + return 0; + } + + int + rt2860_bbp_init(struct rt2860_softc *sc) + { + int i, ntries; + + /* wait for BBP to wake up */ + for (ntries = 0; ntries < 20; ntries++) { + uint8_t bbp0 = rt2860_mcu_bbp_read(sc, 0); + if (bbp0 != 0 && bbp0 != 0xff) + break; + } + if (ntries == 20) { + printf("%s: timeout waiting for BBP to wake up\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + + /* initialize BBP registers to default values */ + for (i = 0; i < nitems(rt2860_def_bbp); i++) { + rt2860_mcu_bbp_write(sc, rt2860_def_bbp[i].reg, + rt2860_def_bbp[i].val); + } + + /* fix BBP84 for RT2860E */ + if ((sc->mac_rev & 0xffff) != 0x0101) + rt2860_mcu_bbp_write(sc, 84, 0x19); + + /* fix BBP69 and BBP73 for RT2860C */ + if (sc->mac_rev == 0x28600100) { + rt2860_mcu_bbp_write(sc, 69, 0x16); + rt2860_mcu_bbp_write(sc, 73, 0x12); + } + + return 0; + } + + int + rt2860_txrx_enable(struct rt2860_softc *sc) + { + uint32_t tmp; + int ntries; + + /* enable Tx/Rx DMA engine */ + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); + RAL_BARRIER_READ_WRITE(sc); + for (ntries = 0; ntries < 200; ntries++) { + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + DELAY(1000); + } + if (ntries == 200) { + printf("%s: timeout waiting for DMA engine\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + + DELAY(50); + + tmp |= RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | + RT2860_WPDMA_BT_SIZE64 << RT2860_WPDMA_BT_SIZE_SHIFT; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* set Rx filter */ + tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR; + if (sc->sc_ic.ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL | + RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK | + RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV | + RT2860_DROP_CFACK | RT2860_DROP_CFEND; + if (sc->sc_ic.ic_opmode == IEEE80211_M_STA) + tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL; + } + RAL_WRITE(sc, RT2860_RX_FILTR_CFG, tmp); + + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, + RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + + return 0; + } + + int + rt2860_init(struct ifnet *ifp) + { + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + uint8_t bbp1, bbp3; + int i, qid, ridx, ntries, error; + + /* for CardBus, power on the socket */ + if (!(sc->sc_flags & RT2860_ENABLED)) { + if (sc->sc_enable != NULL && (*sc->sc_enable)(sc) != 0) { + printf("%s: could not enable device\n", + sc->sc_dev.dv_xname); + return EIO; + } + sc->sc_flags |= RT2860_ENABLED; + } + + rt2860_stop(ifp, 0); + + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + RAL_WRITE(sc, RT2860_WPDMA_RST_IDX, 0xffffffff); + + /* PBF hardware reset */ + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe00); + + if (!(sc->sc_flags & RT2860_FWLOADED)) { + if ((error = rt2860_load_microcode(sc)) != 0) { + printf("%s: could not load 8051 microcode\n", + sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return error; + } + sc->sc_flags |= RT2860_FWLOADED; + } + + IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); + rt2860_set_macaddr(sc, ic->ic_myaddr); + + /* init Tx power for all Tx rates (from EEPROM) */ + for (ridx = 0; ridx < 5; ridx++) { + if (sc->txpow20mhz[ridx] == 0xffffffff) + continue; + RAL_WRITE(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); + } + + for (ntries = 0; ntries < 100; ntries++) { + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + DELAY(1000); + } + if (ntries == 100) { + printf("%s: timeout waiting for DMA engine\n", + sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return ETIMEDOUT; + } + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* reset Rx ring and all 6 Tx rings */ + RAL_WRITE(sc, RT2860_WPDMA_RST_IDX, 0x1003f); + + /* PBF hardware reset */ + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe00); + + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); + + for (i = 0; i < nitems(rt2860_def_mac); i++) + RAL_WRITE(sc, rt2860_def_mac[i].reg, rt2860_def_mac[i].val); + + /* wait while MAC is busy */ + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_MAC_STATUS_REG) & + (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY))) + break; + DELAY(1000); + } + if (ntries == 100) { + printf("%s: timeout waiting for MAC\n", sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return ETIMEDOUT; + } + + /* clear Host to MCU mailbox */ + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, 0); + RAL_WRITE(sc, RT2860_H2M_MAILBOX, 0); + + if ((error = rt2860_bbp_init(sc)) != 0) { + rt2860_stop(ifp, 1); + return error; + } + + /* clear RX WCID search table */ + RAL_SET_REGION_4(sc, RT2860_WCID_ENTRY(0), 0, 512); + /* clear pairwise key table */ + RAL_SET_REGION_4(sc, RT2860_PKEY(0), 0, 2048); + /* clear IV/EIV table */ + RAL_SET_REGION_4(sc, RT2860_IVEIV(0), 0, 512); + /* clear WCID attribute table */ + RAL_SET_REGION_4(sc, RT2860_WCID_ATTR(0), 0, 256); + /* clear shared key table */ + RAL_SET_REGION_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32); + /* clear shared key mode */ + RAL_SET_REGION_4(sc, RT2860_SKEY_MODE_0_7, 0, 4); + + /* init Tx rings (4 EDCAs + HCCA + Mgt) */ + for (qid = 0; qid < 6; qid++) { + RAL_WRITE(sc, RT2860_TX_BASE_PTR(qid), sc->txq[qid].paddr); + RAL_WRITE(sc, RT2860_TX_MAX_CNT(qid), RT2860_TX_RING_COUNT); + RAL_WRITE(sc, RT2860_TX_CTX_IDX(qid), 0); + } + + /* init Rx ring */ + RAL_WRITE(sc, RT2860_RX_BASE_PTR, sc->rxq.paddr); + RAL_WRITE(sc, RT2860_RX_MAX_CNT, RT2860_RX_RING_COUNT); + RAL_WRITE(sc, RT2860_RX_CALC_IDX, RT2860_RX_RING_COUNT - 1); + + /* setup maximum buffer sizes */ + RAL_WRITE(sc, RT2860_MAX_LEN_CFG, 1 << 12 | + (MCLBYTES - sizeof (struct rt2860_rxwi) - 2)); + + for (ntries = 0; ntries < 100; ntries++) { + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + DELAY(1000); + } + if (ntries == 100) { + printf("%s: timeout waiting for DMA engine\n", + sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return ETIMEDOUT; + } + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* disable interrupts mitigation */ + RAL_WRITE(sc, RT2860_DELAY_INT_CFG, 0); + + /* write vendor-specific BBP values (from EEPROM) */ + for (i = 0; i < 8; i++) { + if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff) + continue; + rt2860_mcu_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val); + } + + /* send LEDs operating mode to microcontroller */ + (void)rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]); + (void)rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]); + (void)rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]); + + /* disable non-existing Rx chains */ + bbp3 = rt2860_mcu_bbp_read(sc, 3); + bbp3 &= ~(1 << 3 | 1 << 4); + if (sc->nrxchains == 2) + bbp3 |= 1 << 3; + else if (sc->nrxchains == 3) + bbp3 |= 1 << 4; + rt2860_mcu_bbp_write(sc, 3, bbp3); + + /* disable non-existing Tx chains */ + bbp1 = rt2860_mcu_bbp_read(sc, 1); + if (sc->ntxchains == 1) + bbp1 &= ~(1 << 3 | 1 << 4); + rt2860_mcu_bbp_write(sc, 1, bbp1); + + /* select default channel */ + ic->ic_bss->ni_chan = ic->ic_ibss_chan; + rt2860_set_chan(sc, ic->ic_ibss_chan); + + /* XXX not clear what the following 8051 command does.. */ + (void)rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BOOT, 0); + + /* set RTS threshold */ + tmp = RAL_READ(sc, RT2860_TX_RTS_CFG); + tmp &= ~0xffff00; + tmp |= ic->ic_rtsthreshold << 8; + RAL_WRITE(sc, RT2860_TX_RTS_CFG, tmp); + + /* setup initial protection mode */ + sc->sc_ic_flags = ic->ic_flags; + rt2860_updateprot(ic); + + /* turn radio LED on */ + rt2860_set_leds(sc, RT2860_LED_RADIO); + + if (ic->ic_flags & IEEE80211_F_WEPON) { + /* install WEP keys */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) + (void)rt2860_set_key(ic, &ic->ic_nw_keys[i], NULL); + } + + /* enable Tx/Rx DMA engine */ + if ((error = rt2860_txrx_enable(sc)) != 0) { + rt2860_stop(ifp, 1); + return error; + } + + /* clear pending interrupts */ + RAL_WRITE(sc, RT2860_INT_STATUS, 0xffffffff); + /* enable interrupts */ + RAL_WRITE(sc, RT2860_INT_MASK, 0x3fffc); + + if (sc->sc_flags & RT2860_ADVANCED_PS) + (void)rt2860_mcu_cmd(sc, RT2860_MCU_CMD_PSLEVEL, sc->pslevel); + + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_flags |= IFF_RUNNING; + + if (ic->ic_opmode != IEEE80211_M_MONITOR) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + else + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + + return 0; + } + + void + rt2860_stop(struct ifnet *ifp, int disable) + { + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + int qid; + + if (ifp->if_flags & IFF_RUNNING) + rt2860_set_leds(sc, 0); /* turn all LEDs off */ + + sc->sc_tx_timer = 0; + ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); /* free all nodes */ + + /* disable interrupts */ + RAL_WRITE(sc, RT2860_INT_MASK, 0); + + /* disable GP timer */ + rt2860_set_gp_timer(sc, 0); + + /* disable Rx */ + tmp = RAL_READ(sc, RT2860_MAC_SYS_CTRL); + tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, tmp); + + /* reset adapter */ + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); + + /* reset Tx and Rx rings (and reclaim TXWIs) */ + sc->qfullmsk = 0; + for (qid = 0; qid < 6; qid++) + rt2860_reset_tx_ring(sc, &sc->txq[qid]); + rt2860_reset_rx_ring(sc, &sc->rxq); + + /* for CardBus, power down the socket */ + if (disable && sc->sc_disable != NULL) { + if (sc->sc_flags & RT2860_ENABLED) { + (*sc->sc_disable)(sc); + sc->sc_flags &= ~(RT2860_ENABLED | RT2860_FWLOADED); + } + } + } + + int + rt2860_load_microcode(struct rt2860_softc *sc) + { + u_char *ucode; + size_t size; + int error, ntries; + firmware_handle_t fh; + + if (firmware_open("ral", "ral-rt2860", &fh) != 0) { + printf("%s: failed to open firmware file\n", + sc->sc_dev.dv_xname); + return EIO; + } + size = firmware_get_size(fh); + ucode = firmware_malloc(size); + if (ucode == NULL) { + printf("%s: failed to alloc firmware memory\n", + sc->sc_dev.dv_xname); + firmware_close(fh); + return ENOMEM; + } + error = firmware_read(fh, 0, ucode, size); + if (error) { + printf("%s: error %d, could not read firmware file %s\n", + sc->sc_dev.dv_xname, error, "ral-rt2860"); + firmware_free(ucode, 0); + firmware_close(fh); + return error; + } + + /* set "host program ram write selection" bit */ + RAL_WRITE(sc, RT2860_SYS_CTRL, RT2860_HST_PM_SEL); + /* write microcode image */ + RAL_WRITE_REGION_1(sc, RT2860_FW_BASE, ucode, size); + /* kick microcontroller unit */ + RAL_WRITE(sc, RT2860_SYS_CTRL, 0); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_SYS_CTRL, RT2860_MCU_RESET); + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, 0); + RAL_WRITE(sc, RT2860_H2M_MAILBOX, 0); + + firmware_free(ucode, 0); + firmware_close(fh); + + /* wait until microcontroller is ready */ + RAL_BARRIER_READ_WRITE(sc); + for (ntries = 0; ntries < 1000; ntries++) { + if (RAL_READ(sc, RT2860_SYS_CTRL) & RT2860_MCU_READY) + break; + DELAY(1000); + } + if (ntries == 1000) { + printf("%s: timeout waiting for MCU to initialize\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + return 0; + } + + /* + * This function is called periodically to adjust Tx power based on + * temperature variation. + */ + void + rt2860_calib(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + const uint8_t *tssi; + uint8_t step, bbp49; + int8_t ridx, d; + + /* read current temperature */ + bbp49 = rt2860_mcu_bbp_read(sc, 49); + + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_bss->ni_chan)) { + tssi = &sc->tssi_2ghz[4]; + step = sc->step_2ghz; + } else { + tssi = &sc->tssi_5ghz[4]; + step = sc->step_5ghz; + } + + if (bbp49 < tssi[0]) { /* lower than reference */ + /* use higher Tx power than default */ + for (d = 0; d > -4 && bbp49 <= tssi[d - 1]; d--); + } else if (bbp49 > tssi[0]) { /* greater than reference */ + /* use lower Tx power than default */ + for (d = 0; d < +4 && bbp49 >= tssi[d + 1]; d++); + } else { + /* use default Tx power */ + d = 0; + } + d *= step; + + DPRINTF(("BBP49=0x%02x, adjusting Tx power by %d\n", bbp49, d)); + + /* write adjusted Tx power values for each Tx rate */ + for (ridx = 0; ridx < 5; ridx++) { + if (sc->txpow20mhz[ridx] == 0xffffffff) + continue; + RAL_WRITE(sc, RT2860_TX_PWR_CFG(ridx), + b4inc(sc->txpow20mhz[ridx], d)); + } + } + + #ifndef IEEE80211_STA_ONLY + int + rt2860_setup_beacon(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + struct rt2860_txwi txwi; + struct mbuf *m; + struct ieee80211_beacon_offsets bo; + int ridx; + + if ((m = ieee80211_beacon_alloc(ic, ic->ic_bss, &bo)) == NULL) + return ENOBUFS; + + memset(&txwi, 0, sizeof txwi); + txwi.wcid = 0xff; + txwi.len = htole16(m->m_pkthdr.len); + /* send beacons at the lowest available rate */ + ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? + RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; + txwi.phy = htole16(rt2860_rates[ridx].mcs); + if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) + txwi.phy |= htole16(RT2860_PHY_OFDM); + txwi.txop = RT2860_TX_TXOP_HT; + txwi.flags = RT2860_TX_TS; + + RAL_WRITE_REGION_1(sc, RT2860_BCN_BASE(0), + (uint8_t *)&txwi, sizeof txwi); + RAL_WRITE_REGION_1(sc, RT2860_BCN_BASE(0) + sizeof txwi, + mtod(m, uint8_t *), m->m_pkthdr.len); + + m_freem(m); + + return 0; + } + #endif + + void + rt2860_enable_tsf_sync(struct rt2860_softc *sc) + { + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_BCN_TIME_CFG); + + tmp &= ~0x1fffff; + tmp |= ic->ic_bss->ni_intval * 16; + tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; + if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * Local TSF is always updated with remote TSF on beacon + * reception. + */ + tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; + } + #ifndef IEEE80211_STA_ONLY + else if (ic->ic_opmode == IEEE80211_M_IBSS) { + tmp |= RT2860_BCN_TX_EN; + /* + * Local TSF is updated with remote TSF on beacon reception + * only if the remote TSF is greater than local TSF. + */ + tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT; + } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + tmp |= RT2860_BCN_TX_EN; + /* SYNC with nobody */ + tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT; + } + #endif + + RAL_WRITE(sc, RT2860_BCN_TIME_CFG, tmp); + } + + void + rt2860_power(int why, void *arg) + { + struct rt2860_softc *sc = arg; + struct ifnet *ifp = &sc->sc_if; + int s; + + DPRINTF(("%s: rt2860_power(%d)\n", sc->sc_dev.dv_xname, why)); + + s = splnet(); + switch (why) { + case PWR_SUSPEND: + case PWR_STANDBY: + rt2860_stop(ifp, 1); + sc->sc_flags &= ~RT2860_FWLOADED; + if (sc->sc_power != NULL) + (*sc->sc_power)(sc, why); + break; + case PWR_RESUME: + if (ifp->if_flags & IFF_UP) { + rt2860_init(ifp); + if (sc->sc_power != NULL) + (*sc->sc_power)(sc, why); + if (ifp->if_flags & IFF_RUNNING) + rt2860_start(ifp); + } + break; + } + splx(s); + } + + void + rt2860_shutdown(void *arg) + { + struct rt2860_softc *sc = arg; + struct ifnet *ifp = &sc->sc_if; + + rt2860_stop(ifp, 1); + } Index: sys/dev/ic/rt2860var.h *************** *** 0 **** --- 1,206 ---- + /* $OpenBSD: rt2860var.h,v 1.11 2008/12/29 13:27:27 damien Exp $ */ + + /*- + * Copyright (c) 2007 + * 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. + */ + + #define RT2860_TX_RING_COUNT 64 + #define RT2860_RX_RING_COUNT 128 + #define RT2860_TX_POOL_COUNT (RT2860_TX_RING_COUNT * 2) + + #define RT2860_MAX_SCATTER ((RT2860_TX_RING_COUNT * 2) - 1) + + /* HW supports up to 255 STAs */ + #define RT2860_WCID_MAX 254 + #define RT2860_AID2WCID(aid) ((aid) & 0xff) + + struct rt2860_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_dbm_antsignal; + uint8_t wr_antenna; + uint8_t wr_antsignal; + } __packed; + + #define RT2860_RX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL | \ + 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL | \ + 1 << IEEE80211_RADIOTAP_ANTENNA | \ + 1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) + + struct rt2860_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; + uint8_t wt_hwqueue; + } __packed; + + #define RT2860_TX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL) + + struct rt2860_tx_data { + struct rt2860_txwi *txwi; + struct mbuf *m; + struct ieee80211_node *ni; + bus_dmamap_t map; + bus_addr_t paddr; + SLIST_ENTRY(rt2860_tx_data) next; + }; + + struct rt2860_tx_ring { + struct rt2860_txd *txd; + bus_addr_t paddr; + bus_dmamap_t map; + bus_dma_segment_t seg; + struct rt2860_tx_data *data[RT2860_TX_RING_COUNT]; + int cur; + int next; + int queued; + }; + + struct rt2860_rx_data { + struct mbuf *m; + bus_dmamap_t map; + }; + + struct rt2860_rx_ring { + struct rt2860_rxd *rxd; + bus_addr_t paddr; + bus_dmamap_t map; + bus_dma_segment_t seg; + unsigned int cur; /* must be unsigned */ + struct rt2860_rx_data data[RT2860_RX_RING_COUNT]; + }; + + struct rt2860_node { + struct ieee80211_node ni; + uint8_t ridx[IEEE80211_RATE_MAXSIZE]; + uint8_t ctl_ridx[IEEE80211_RATE_MAXSIZE]; + }; + + struct rt2860_softc { + struct device sc_dev; + + struct ieee80211com sc_ic; + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + + int (*sc_enable)(struct rt2860_softc *); + void (*sc_disable)(struct rt2860_softc *); + + bus_dma_tag_t sc_dmat; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + + struct ieee80211_amrr amrr; + void (*sc_power)(struct rt2860_softc *, int); + + struct ethercom sc_ec; + + int sc_flags; + #define RT2860_ENABLED (1 << 0) + #define RT2860_FWLOADED (1 << 1) + #define RT2860_ADVANCED_PS (1 << 2) + + uint32_t sc_ic_flags; + int fixed_ridx; + + struct rt2860_tx_ring txq[6]; + struct rt2860_rx_ring rxq; + + SLIST_HEAD(, rt2860_tx_data) data_pool; + struct rt2860_tx_data data[RT2860_TX_POOL_COUNT]; + bus_dmamap_t txwi_map; + bus_dma_segment_t txwi_seg; + char *txwi_vaddr; + + int sc_tx_timer; + int mgtqid; + int sifs; + uint8_t qfullmsk; + + uint32_t mac_rev; + uint8_t rf_rev; + uint8_t freq; + uint8_t ntxchains; + uint8_t nrxchains; + uint8_t pslevel; + int8_t txpow1[50]; + int8_t txpow2[50]; + int8_t rssi_2ghz[3]; + int8_t rssi_5ghz[3]; + uint8_t lna[4]; + uint8_t ext_2ghz_lna; + uint8_t ext_5ghz_lna; + uint8_t calib_2ghz; + uint8_t calib_5ghz; + uint8_t tssi_2ghz[9]; + uint8_t tssi_5ghz[9]; + uint8_t step_2ghz; + uint8_t step_5ghz; + struct { + uint8_t reg; + uint8_t val; + } bbp[8]; + uint8_t leds; + uint16_t led[3]; + uint32_t txpow20mhz[5]; + uint32_t txpow40mhz_2ghz[5]; + uint32_t txpow40mhz_5ghz[5]; + + struct ieee80211_amrr_node amn[RT2860_WCID_MAX + 1]; + + #if NBPFILTER > 0 + void *sc_drvbpf; + union { + struct rt2860_rx_radiotap_header th; + uint8_t pad[64]; + } sc_rxtapu; + #define sc_rxtap sc_rxtapu.th + int sc_rxtap_len; + + union { + struct rt2860_tx_radiotap_header th; + uint8_t pad[64]; + } sc_txtapu; + #define sc_txtap sc_txtapu.th + int sc_txtap_len; + #endif + void *sc_sdhook; + void *sc_powerhook; + }; + + #define sc_if sc_ec.ec_if + + int rt2860_attach(void *, int); + int rt2860_detach(void *); + int rt2860_intr(void *); + void rt2860_shutdown(void *); + + #define PCI_PRODUCT_RALINK_RT2860 0x0601 + #define PCI_PRODUCT_RALINK_RT2890 0x0681 + #define PCI_PRODUCT_RALINK_RT2760 0x0701 + #define PCI_PRODUCT_RALINK_RT2790 0x0781 + #define PCI_PRODUCT_AWT_RT2890 0x1059 Index: sys/dev/ic/rt2860reg.h *************** *** 0 **** --- 1,1119 ---- + /* $OpenBSD: rt2860reg.h,v 1.16 2009/01/06 18:56:07 damien Exp $ */ + + /*- + * Copyright (c) 2007 + * 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. + */ + + /* PCI registers */ + #define RT2860_PCI_CFG 0x0000 + #define RT2860_PCI_EECTRL 0x0004 + #define RT2860_PCI_MCUCTRL 0x0008 + #define RT2860_PCI_SYSCTRL 0x000c + #define RT2860_PCIE_JTAG 0x0010 + + #define RT3070_OPT_14 0x0114 + + /* SCH/DMA registers */ + #define RT2860_INT_STATUS 0x0200 + #define RT2860_INT_MASK 0x0204 + #define RT2860_WPDMA_GLO_CFG 0x0208 + #define RT2860_WPDMA_RST_IDX 0x020c + #define RT2860_DELAY_INT_CFG 0x0210 + #define RT2860_WMM_AIFSN_CFG 0x0214 + #define RT2860_WMM_CWMIN_CFG 0x0218 + #define RT2860_WMM_CWMAX_CFG 0x021c + #define RT2860_WMM_TXOP0_CFG 0x0220 + #define RT2860_WMM_TXOP1_CFG 0x0224 + #define RT2860_GPIO_CTRL 0x0228 + #define RT2860_MCU_CMD_REG 0x022c + #define RT2860_TX_BASE_PTR(qid) (0x0230 + (qid) * 16) + #define RT2860_TX_MAX_CNT(qid) (0x0234 + (qid) * 16) + #define RT2860_TX_CTX_IDX(qid) (0x0238 + (qid) * 16) + #define RT2860_TX_DTX_IDX(qid) (0x023c + (qid) * 16) + #define RT2860_RX_BASE_PTR 0x0290 + #define RT2860_RX_MAX_CNT 0x0294 + #define RT2860_RX_CALC_IDX 0x0298 + #define RT2860_FS_DRX_IDX 0x029c + #define RT2860_USB_DMA_CFG 0x02a0 /* RT2870 only */ + #define RT2860_US_CYC_CNT 0x02a4 + + /* PBF registers */ + #define RT2860_SYS_CTRL 0x0400 + #define RT2860_HOST_CMD 0x0404 + #define RT2860_PBF_CFG 0x0408 + #define RT2860_MAX_PCNT 0x040c + #define RT2860_BUF_CTRL 0x0410 + #define RT2860_MCU_INT_STA 0x0414 + #define RT2860_MCU_INT_ENA 0x0418 + #define RT2860_TXQ_IO(qid) (0x041c + (qid) * 4) + #define RT2860_RX0Q_IO 0x0424 + #define RT2860_BCN_OFFSET0 0x042c + #define RT2860_BCN_OFFSET1 0x0430 + #define RT2860_TXRXQ_STA 0x0434 + #define RT2860_TXRXQ_PCNT 0x0438 + #define RT2860_PBF_DBG 0x043c + #define RT2860_CAP_CTRL 0x0440 + + /* RT3070 registers */ + #define RT3070_RF_CSR_CFG 0x0500 + #define RT3070_EFUSE_CTRL 0x0580 + #define RT3070_EFUSE_DATA0 0x0590 + #define RT3070_EFUSE_DATA1 0x0594 + #define RT3070_EFUSE_DATA2 0x0598 + #define RT3070_EFUSE_DATA3 0x059c + #define RT3070_LDO_CFG0 0x05d4 + #define RT3070_GPIO_SWITCH 0x05dc + + /* MAC registers */ + #define RT2860_ASIC_VER_ID 0x1000 + #define RT2860_MAC_SYS_CTRL 0x1004 + #define RT2860_MAC_ADDR_DW0 0x1008 + #define RT2860_MAC_ADDR_DW1 0x100c + #define RT2860_MAC_BSSID_DW0 0x1010 + #define RT2860_MAC_BSSID_DW1 0x1014 + #define RT2860_MAX_LEN_CFG 0x1018 + #define RT2860_BBP_CSR_CFG 0x101c + #define RT2860_RF_CSR_CFG0 0x1020 + #define RT2860_RF_CSR_CFG1 0x1024 + #define RT2860_RF_CSR_CFG2 0x1028 + #define RT2860_LED_CFG 0x102c + + /* undocumented registers */ + #define RT2860_DEBUG 0x10f4 + + /* MAC Timing control registers */ + #define RT2860_XIFS_TIME_CFG 0x1100 + #define RT2860_BKOFF_SLOT_CFG 0x1104 + #define RT2860_NAV_TIME_CFG 0x1108 + #define RT2860_CH_TIME_CFG 0x110c + #define RT2860_PBF_LIFE_TIMER 0x1110 + #define RT2860_BCN_TIME_CFG 0x1114 + #define RT2860_TBTT_SYNC_CFG 0x1118 + #define RT2860_TSF_TIMER_DW0 0x111c + #define RT2860_TSF_TIMER_DW1 0x1120 + #define RT2860_TBTT_TIMER 0x1124 + #define RT2860_INT_TIMER_CFG 0x1128 + #define RT2860_INT_TIMER_EN 0x112c + #define RT2860_CH_IDLE_TIME 0x1130 + + /* MAC Power Save configuration registers */ + #define RT2860_MAC_STATUS_REG 0x1200 + #define RT2860_PWR_PIN_CFG 0x1204 + #define RT2860_AUTO_WAKEUP_CFG 0x1208 + + /* MAC TX configuration registers */ + #define RT2860_EDCA_AC_CFG(aci) (0x1300 + (aci) * 4) + #define RT2860_EDCA_TID_AC_MAP 0x1310 + #define RT2860_TX_PWR_CFG(ridx) (0x1314 + (ridx) * 4) + #define RT2860_TX_PIN_CFG 0x1328 + #define RT2860_TX_BAND_CFG 0x132c + #define RT2860_TX_SW_CFG0 0x1330 + #define RT2860_TX_SW_CFG1 0x1334 + #define RT2860_TX_SW_CFG2 0x1338 + #define RT2860_TXOP_THRES_CFG 0x133c + #define RT2860_TXOP_CTRL_CFG 0x1340 + #define RT2860_TX_RTS_CFG 0x1344 + #define RT2860_TX_TIMEOUT_CFG 0x1348 + #define RT2860_TX_RTY_CFG 0x134c + #define RT2860_TX_LINK_CFG 0x1350 + #define RT2860_HT_FBK_CFG0 0x1354 + #define RT2860_HT_FBK_CFG1 0x1358 + #define RT2860_LG_FBK_CFG0 0x135c + #define RT2860_LG_FBK_CFG1 0x1360 + #define RT2860_CCK_PROT_CFG 0x1364 + #define RT2860_OFDM_PROT_CFG 0x1368 + #define RT2860_MM20_PROT_CFG 0x136c + #define RT2860_MM40_PROT_CFG 0x1370 + #define RT2860_GF20_PROT_CFG 0x1374 + #define RT2860_GF40_PROT_CFG 0x1378 + #define RT2860_EXP_CTS_TIME 0x137c + #define RT2860_EXP_ACK_TIME 0x1380 + + /* MAC RX configuration registers */ + #define RT2860_RX_FILTR_CFG 0x1400 + #define RT2860_AUTO_RSP_CFG 0x1404 + #define RT2860_LEGACY_BASIC_RATE 0x1408 + #define RT2860_HT_BASIC_RATE 0x140c + #define RT2860_HT_CTRL_CFG 0x1410 + #define RT2860_SIFS_COST_CFG 0x1414 + #define RT2860_RX_PARSER_CFG 0x1418 + + /* MAC Security configuration registers */ + #define RT2860_TX_SEC_CNT0 0x1500 + #define RT2860_RX_SEC_CNT0 0x1504 + #define RT2860_CCMP_FC_MUTE 0x1508 + + /* MAC HCCA/PSMP configuration registers */ + #define RT2860_TXOP_HLDR_ADDR0 0x1600 + #define RT2860_TXOP_HLDR_ADDR1 0x1604 + #define RT2860_TXOP_HLDR_ET 0x1608 + #define RT2860_QOS_CFPOLL_RA_DW0 0x160c + #define RT2860_QOS_CFPOLL_A1_DW1 0x1610 + #define RT2860_QOS_CFPOLL_QC 0x1614 + + /* MAC Statistics Counters */ + #define RT2860_RX_STA_CNT0 0x1700 + #define RT2860_RX_STA_CNT1 0x1704 + #define RT2860_RX_STA_CNT2 0x1708 + #define RT2860_TX_STA_CNT0 0x170c + #define RT2860_TX_STA_CNT1 0x1710 + #define RT2860_TX_STA_CNT2 0x1714 + #define RT2860_TX_STAT_FIFO 0x1718 + + /* RX WCID search table */ + #define RT2860_WCID_ENTRY(wcid) (0x1800 + (wcid) * 8) + + #define RT2860_FW_BASE 0x2000 + #define RT2870_FW_BASE 0x3000 + + /* Pair-wise key table */ + #define RT2860_PKEY(wcid) (0x4000 + (wcid) * 32) + + /* IV/EIV table */ + #define RT2860_IVEIV(wcid) (0x6000 + (wcid) * 8) + + /* WCID attribute table */ + #define RT2860_WCID_ATTR(wcid) (0x6800 + (wcid) * 4) + + /* Shared Key Table */ + #define RT2860_SKEY(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32) + + /* Shared Key Mode */ + #define RT2860_SKEY_MODE_0_7 0x7000 + #define RT2860_SKEY_MODE_8_15 0x7004 + #define RT2860_SKEY_MODE_16_23 0x7008 + #define RT2860_SKEY_MODE_24_31 0x700c + + /* Shared Memory between MCU and host */ + #define RT2860_H2M_MAILBOX 0x7010 + #define RT2860_H2M_MAILBOX_CID 0x7014 + #define RT2860_H2M_MAILBOX_STATUS 0x701c + #define RT2860_H2M_BBPAGENT 0x7028 + #define RT2860_BCN_BASE(vap) (0x7800 + (vap) * 512) + + + /* possible flags for register RT2860_PCI_EECTRL */ + #define RT2860_C (1 << 0) + #define RT2860_S (1 << 1) + #define RT2860_D (1 << 2) + #define RT2860_SHIFT_D 2 + #define RT2860_Q (1 << 3) + #define RT2860_SHIFT_Q 3 + + /* possible flags for registers INT_STATUS/INT_MASK */ + #define RT2860_TX_COHERENT (1 << 17) + #define RT2860_RX_COHERENT (1 << 16) + #define RT2860_MAC_INT_4 (1 << 15) + #define RT2860_MAC_INT_3 (1 << 14) + #define RT2860_MAC_INT_2 (1 << 13) + #define RT2860_MAC_INT_1 (1 << 12) + #define RT2860_MAC_INT_0 (1 << 11) + #define RT2860_TX_RX_COHERENT (1 << 10) + #define RT2860_MCU_CMD_INT (1 << 9) + #define RT2860_TX_DONE_INT5 (1 << 8) + #define RT2860_TX_DONE_INT4 (1 << 7) + #define RT2860_TX_DONE_INT3 (1 << 6) + #define RT2860_TX_DONE_INT2 (1 << 5) + #define RT2860_TX_DONE_INT1 (1 << 4) + #define RT2860_TX_DONE_INT0 (1 << 3) + #define RT2860_RX_DONE_INT (1 << 2) + #define RT2860_TX_DLY_INT (1 << 1) + #define RT2860_RX_DLY_INT (1 << 0) + + /* possible flags for register WPDMA_GLO_CFG */ + #define RT2860_HDR_SEG_LEN_SHIFT 8 + #define RT2860_BIG_ENDIAN (1 << 7) + #define RT2860_TX_WB_DDONE (1 << 6) + #define RT2860_WPDMA_BT_SIZE_SHIFT 4 + #define RT2860_WPDMA_BT_SIZE16 0 + #define RT2860_WPDMA_BT_SIZE32 1 + #define RT2860_WPDMA_BT_SIZE64 2 + #define RT2860_WPDMA_BT_SIZE128 3 + #define RT2860_RX_DMA_BUSY (1 << 3) + #define RT2860_RX_DMA_EN (1 << 2) + #define RT2860_TX_DMA_BUSY (1 << 1) + #define RT2860_TX_DMA_EN (1 << 0) + + /* possible flags for register DELAY_INT_CFG */ + #define RT2860_TXDLY_INT_EN (1 << 31) + #define RT2860_TXMAX_PINT_SHIFT 24 + #define RT2860_TXMAX_PTIME_SHIFT 16 + #define RT2860_RXDLY_INT_EN (1 << 15) + #define RT2860_RXMAX_PINT_SHIFT 8 + #define RT2860_RXMAX_PTIME_SHIFT 0 + + /* possible flags for register GPIO_CTRL */ + #define RT2860_GPIO_D_SHIFT 8 + #define RT2860_GPIO_O_SHIFT 0 + + /* possible flags for register USB_DMA_CFG */ + #define RT2860_USB_TX_BUSY (1 << 31) + #define RT2860_USB_RX_BUSY (1 << 30) + #define RT2860_USB_EPOUT_VLD_SHIFT 24 + #define RT2860_USB_TX_EN (1 << 23) + #define RT2860_USB_RX_EN (1 << 22) + #define RT2860_USB_RX_AGG_EN (1 << 21) + #define RT2860_USB_TXOP_HALT (1 << 20) + #define RT2860_USB_TX_CLEAR (1 << 19) + #define RT2860_USB_PHY_WD_EN (1 << 16) + #define RT2860_USB_PHY_MAN_RST (1 << 15) + #define RT2860_USB_RX_AGG_LMT_SHIFT 8 + #define RT2860_USB_RX_AGG_TO_SHIFT 0 + + /* possible flags for register US_CYC_CNT */ + #define RT2860_TEST_EN (1 << 24) + #define RT2860_TEST_SEL_SHIFT 16 + #define RT2860_BT_MODE_EN (1 << 8) + #define RT2860_US_CYC_CNT_SHIFT 0 + + /* possible flags for register SYS_CTRL */ + #define RT2860_HST_PM_SEL (1 << 16) + #define RT2860_CAP_MODE (1 << 14) + #define RT2860_PME_OEN (1 << 13) + #define RT2860_CLKSELECT (1 << 12) + #define RT2860_PBF_CLK_EN (1 << 11) + #define RT2860_MAC_CLK_EN (1 << 10) + #define RT2860_DMA_CLK_EN (1 << 9) + #define RT2860_MCU_READY (1 << 7) + #define RT2860_ASY_RESET (1 << 4) + #define RT2860_PBF_RESET (1 << 3) + #define RT2860_MAC_RESET (1 << 2) + #define RT2860_DMA_RESET (1 << 1) + #define RT2860_MCU_RESET (1 << 0) + + /* possible values for register HOST_CMD */ + #define RT2860_MCU_CMD_SLEEP 0x30 + #define RT2860_MCU_CMD_WAKEUP 0x31 + #define RT2860_MCU_CMD_LEDS 0x50 + #define RT2860_MCU_CMD_LED_RSSI 0x51 + #define RT2860_MCU_CMD_LED1 0x52 + #define RT2860_MCU_CMD_LED2 0x53 + #define RT2860_MCU_CMD_LED3 0x54 + #define RT2860_MCU_CMD_BOOT 0x72 + #define RT2860_MCU_CMD_BBP 0x80 + #define RT2860_MCU_CMD_PSLEVEL 0x83 + + /* possible flags for register PBF_CFG */ + #define RT2860_TX1Q_NUM_SHIFT 21 + #define RT2860_TX2Q_NUM_SHIFT 16 + #define RT2860_NULL0_MODE (1 << 15) + #define RT2860_NULL1_MODE (1 << 14) + #define RT2860_RX_DROP_MODE (1 << 13) + #define RT2860_TX0Q_MANUAL (1 << 12) + #define RT2860_TX1Q_MANUAL (1 << 11) + #define RT2860_TX2Q_MANUAL (1 << 10) + #define RT2860_RX0Q_MANUAL (1 << 9) + #define RT2860_HCCA_EN (1 << 8) + #define RT2860_TX0Q_EN (1 << 4) + #define RT2860_TX1Q_EN (1 << 3) + #define RT2860_TX2Q_EN (1 << 2) + #define RT2860_RX0Q_EN (1 << 1) + + /* possible flags for register BUF_CTRL */ + #define RT2860_WRITE_TXQ(qid) (1 << (11 - (qid))) + #define RT2860_NULL0_KICK (1 << 7) + #define RT2860_NULL1_KICK (1 << 6) + #define RT2860_BUF_RESET (1 << 5) + #define RT2860_READ_TXQ(qid) (1 << (3 - (qid)) + #define RT2860_READ_RX0Q (1 << 0) + + /* possible flags for registers MCU_INT_STA/MCU_INT_ENA */ + #define RT2860_MCU_MAC_INT_8 (1 << 24) + #define RT2860_MCU_MAC_INT_7 (1 << 23) + #define RT2860_MCU_MAC_INT_6 (1 << 22) + #define RT2860_MCU_MAC_INT_4 (1 << 20) + #define RT2860_MCU_MAC_INT_3 (1 << 19) + #define RT2860_MCU_MAC_INT_2 (1 << 18) + #define RT2860_MCU_MAC_INT_1 (1 << 17) + #define RT2860_MCU_MAC_INT_0 (1 << 16) + #define RT2860_DTX0_INT (1 << 11) + #define RT2860_DTX1_INT (1 << 10) + #define RT2860_DTX2_INT (1 << 9) + #define RT2860_DRX0_INT (1 << 8) + #define RT2860_HCMD_INT (1 << 7) + #define RT2860_N0TX_INT (1 << 6) + #define RT2860_N1TX_INT (1 << 5) + #define RT2860_BCNTX_INT (1 << 4) + #define RT2860_MTX0_INT (1 << 3) + #define RT2860_MTX1_INT (1 << 2) + #define RT2860_MTX2_INT (1 << 1) + #define RT2860_MRX0_INT (1 << 0) + + /* possible flags for register TXRXQ_PCNT */ + #define RT2860_RX0Q_PCNT_MASK 0xff000000 + #define RT2860_TX2Q_PCNT_MASK 0x00ff0000 + #define RT2860_TX1Q_PCNT_MASK 0x0000ff00 + #define RT2860_TX0Q_PCNT_MASK 0x000000ff + + /* possible flags for register CAP_CTRL */ + #define RT2860_CAP_ADC_FEQ (1 << 31) + #define RT2860_CAP_START (1 << 30) + #define RT2860_MAN_TRIG (1 << 29) + #define RT2860_TRIG_OFFSET_SHIFT 16 + #define RT2860_START_ADDR_SHIFT 0 + + /* possible flags for register RF_CSR_CFG */ + #define RT3070_RF_KICK (1 << 17) + #define RT3070_RF_WRITE (1 << 16) + + /* possible flags for register EFUSE_CTRL */ + #define RT3070_SEL_EFUSE (1 << 31) + #define RT3070_EFSROM_KICK (1 << 30) + #define RT3070_EFSROM_AIN_SHIFT 16 + #define RT3070_EFSROM_MODE_MASK (3 << 6) + #define RT3070_EFUSE_AOUT_MASK 0x0000003f + + /* possible flags for register MAC_SYS_CTRL */ + #define RT2860_RX_TS_EN (1 << 7) + #define RT2860_WLAN_HALT_EN (1 << 6) + #define RT2860_PBF_LOOP_EN (1 << 5) + #define RT2860_CONT_TX_TEST (1 << 4) + #define RT2860_MAC_RX_EN (1 << 3) + #define RT2860_MAC_TX_EN (1 << 2) + #define RT2860_BBP_HRST (1 << 1) + #define RT2860_MAC_SRST (1 << 0) + + /* possible flags for register MAC_BSSID_DW1 */ + #define RT2860_MULTI_BCN_NUM_SHIFT 18 + #define RT2860_MULTI_BSSID_MODE_SHIFT 16 + + /* possible flags for register MAX_LEN_CFG */ + #define RT2860_MIN_MPDU_LEN_SHIFT 16 + #define RT2860_MAX_PSDU_LEN_SHIFT 12 + #define RT2860_MAX_PSDU_LEN8K 0 + #define RT2860_MAX_PSDU_LEN16K 1 + #define RT2860_MAX_PSDU_LEN32K 2 + #define RT2860_MAX_PSDU_LEN64K 3 + #define RT2860_MAX_MPDU_LEN_SHIFT 0 + + /* possible flags for registers BBP_CSR_CFG/H2M_BBPAGENT */ + #define RT2860_BBP_RW_PARALLEL (1 << 19) + #define RT2860_BBP_PAR_DUR_112_5 (1 << 18) + #define RT2860_BBP_CSR_KICK (1 << 17) + #define RT2860_BBP_CSR_READ (1 << 16) + #define RT2860_BBP_ADDR_SHIFT 8 + #define RT2860_BBP_DATA_SHIFT 0 + + /* possible flags for register RF_CSR_CFG0 */ + #define RT2860_RF_REG_CTRL (1 << 31) + #define RT2860_RF_LE_SEL1 (1 << 30) + #define RT2860_RF_LE_STBY (1 << 29) + #define RT2860_RF_REG_WIDTH_SHIFT 24 + #define RT2860_RF_REG_0_SHIFT 0 + + /* possible flags for register RF_CSR_CFG1 */ + #define RT2860_RF_DUR_5 (1 << 24) + #define RT2860_RF_REG_1_SHIFT 0 + + /* possible flags for register LED_CFG */ + #define RT2860_LED_POL (1 << 30) + #define RT2860_Y_LED_MODE_SHIFT 28 + #define RT2860_G_LED_MODE_SHIFT 26 + #define RT2860_R_LED_MODE_SHIFT 24 + #define RT2860_LED_MODE_OFF 0 + #define RT2860_LED_MODE_BLINK_TX 1 + #define RT2860_LED_MODE_SLOW_BLINK 2 + #define RT2860_LED_MODE_ON 3 + #define RT2860_SLOW_BLK_TIME_SHIFT 16 + #define RT2860_LED_OFF_TIME_SHIFT 8 + #define RT2860_LED_ON_TIME_SHIFT 0 + + /* possible flags for register XIFS_TIME_CFG */ + #define RT2860_BB_RXEND_EN (1 << 29) + #define RT2860_EIFS_TIME_SHIFT 20 + #define RT2860_OFDM_XIFS_TIME_SHIFT 16 + #define RT2860_OFDM_SIFS_TIME_SHIFT 8 + #define RT2860_CCK_SIFS_TIME_SHIFT 0 + + /* possible flags for register BKOFF_SLOT_CFG */ + #define RT2860_CC_DELAY_TIME_SHIFT 8 + #define RT2860_SLOT_TIME 0 + + /* possible flags for register NAV_TIME_CFG */ + #define RT2860_NAV_UPD (1 << 31) + #define RT2860_NAV_UPD_VAL_SHIFT 16 + #define RT2860_NAV_CLR_EN (1 << 15) + #define RT2860_NAV_TIMER_SHIFT 0 + + /* possible flags for register CH_TIME_CFG */ + #define RT2860_EIFS_AS_CH_BUSY (1 << 4) + #define RT2860_NAV_AS_CH_BUSY (1 << 3) + #define RT2860_RX_AS_CH_BUSY (1 << 2) + #define RT2860_TX_AS_CH_BUSY (1 << 1) + #define RT2860_CH_STA_TIMER_EN (1 << 0) + + /* possible values for register BCN_TIME_CFG */ + #define RT2860_TSF_INS_COMP_SHIFT 24 + #define RT2860_BCN_TX_EN (1 << 20) + #define RT2860_TBTT_TIMER_EN (1 << 19) + #define RT2860_TSF_SYNC_MODE_SHIFT 17 + #define RT2860_TSF_SYNC_MODE_DIS 0 + #define RT2860_TSF_SYNC_MODE_STA 1 + #define RT2860_TSF_SYNC_MODE_IBSS 2 + #define RT2860_TSF_SYNC_MODE_HOSTAP 3 + #define RT2860_TSF_TIMER_EN (1 << 16) + #define RT2860_BCN_INTVAL_SHIFT 0 + + /* possible flags for register TBTT_SYNC_CFG */ + #define RT2860_BCN_CWMIN_SHIFT 20 + #define RT2860_BCN_AIFSN_SHIFT 16 + #define RT2860_BCN_EXP_WIN_SHIFT 8 + #define RT2860_TBTT_ADJUST_SHIFT 0 + + /* possible flags for register INT_TIMER_CFG */ + #define RT2860_GP_TIMER_SHIFT 16 + #define RT2860_PRE_TBTT_TIMER_SHIFT 0 + + /* possible flags for register INT_TIMER_EN */ + #define RT2860_GP_TIMER_EN (1 << 1) + #define RT2860_PRE_TBTT_INT_EN (1 << 0) + + /* possible flags for register MAC_STATUS_REG */ + #define RT2860_RX_STATUS_BUSY (1 << 1) + #define RT2860_TX_STATUS_BUSY (1 << 0) + + /* possible flags for register PWR_PIN_CFG */ + #define RT2860_IO_ADDA_PD (1 << 3) + #define RT2860_IO_PLL_PD (1 << 2) + #define RT2860_IO_RA_PE (1 << 1) + #define RT2860_IO_RF_PE (1 << 0) + + /* possible flags for register AUTO_WAKEUP_CFG */ + #define RT2860_AUTO_WAKEUP_EN (1 << 15) + #define RT2860_SLEEP_TBTT_NUM_SHIFT 8 + #define RT2860_WAKEUP_LEAD_TIME_SHIFT 0 + + /* possible flags for register TX_PIN_CFG */ + #define RT2860_TRSW_POL (1 << 19) + #define RT2860_TRSW_EN (1 << 18) + #define RT2860_RFTR_POL (1 << 17) + #define RT2860_RFTR_EN (1 << 16) + #define RT2860_LNA_PE_G1_POL (1 << 15) + #define RT2860_LNA_PE_A1_POL (1 << 14) + #define RT2860_LNA_PE_G0_POL (1 << 13) + #define RT2860_LNA_PE_A0_POL (1 << 12) + #define RT2860_LNA_PE_G1_EN (1 << 11) + #define RT2860_LNA_PE_A1_EN (1 << 10) + #define RT2860_LNA_PE_G0_EN (1 << 9) + #define RT2860_LNA_PE_A0_EN (1 << 8) + #define RT2860_PA_PE_G1_POL (1 << 7) + #define RT2860_PA_PE_A1_POL (1 << 6) + #define RT2860_PA_PE_G0_POL (1 << 5) + #define RT2860_PA_PE_A0_POL (1 << 4) + #define RT2860_PA_PE_G1_EN (1 << 3) + #define RT2860_PA_PE_A1_EN (1 << 2) + #define RT2860_PA_PE_G0_EN (1 << 1) + #define RT2860_PA_PE_A0_EN (1 << 0) + + /* possible flags for register TX_BAND_CFG */ + #define RT2860_5G_BAND_SEL_N (1 << 2) + #define RT2860_5G_BAND_SEL_P (1 << 1) + #define RT2860_TX_BAND_SEL (1 << 0) + + /* possible flags for register TX_SW_CFG0 */ + #define RT2860_DLY_RFTR_EN_SHIFT 24 + #define RT2860_DLY_TRSW_EN_SHIFT 16 + #define RT2860_DLY_PAPE_EN_SHIFT 8 + #define RT2860_DLY_TXPE_EN_SHIFT 0 + + /* possible flags for register TX_SW_CFG1 */ + #define RT2860_DLY_RFTR_DIS_SHIFT 16 + #define RT2860_DLY_TRSW_DIS_SHIFT 8 + #define RT2860_DLY_PAPE_DIS SHIFT 0 + + /* possible flags for register TX_SW_CFG2 */ + #define RT2860_DLY_LNA_EN_SHIFT 24 + #define RT2860_DLY_LNA_DIS_SHIFT 16 + #define RT2860_DLY_DAC_EN_SHIFT 8 + #define RT2860_DLY_DAC_DIS_SHIFT 0 + + /* possible flags for register TXOP_THRES_CFG */ + #define RT2860_TXOP_REM_THRES_SHIFT 24 + #define RT2860_CF_END_THRES_SHIFT 16 + #define RT2860_RDG_IN_THRES 8 + #define RT2860_RDG_OUT_THRES 0 + + /* possible flags for register TXOP_CTRL_CFG */ + #define RT2860_EXT_CW_MIN_SHIFT 16 + #define RT2860_EXT_CCA_DLY_SHIFT 8 + #define RT2860_EXT_CCA_EN (1 << 7) + #define RT2860_LSIG_TXOP_EN (1 << 6) + #define RT2860_TXOP_TRUN_EN_MIMOPS (1 << 4) + #define RT2860_TXOP_TRUN_EN_TXOP (1 << 3) + #define RT2860_TXOP_TRUN_EN_RATE (1 << 2) + #define RT2860_TXOP_TRUN_EN_AC (1 << 1) + #define RT2860_TXOP_TRUN_EN_TIMEOUT (1 << 0) + + /* possible flags for register TX_RTS_CFG */ + #define RT2860_RTS_FBK_EN (1 << 24) + #define RT2860_RTS_THRES_SHIFT 8 + #define RT2860_RTS_RTY_LIMIT_SHIFT 0 + + /* possible flags for register TX_TIMEOUT_CFG */ + #define RT2860_TXOP_TIMEOUT_SHIFT 16 + #define RT2860_RX_ACK_TIMEOUT_SHIFT 8 + #define RT2860_MPDU_LIFE_TIME_SHIFT 4 + + /* possible flags for register TX_RTY_CFG */ + #define RT2860_TX_AUTOFB_EN (1 << 30) + #define RT2860_AGG_RTY_MODE_TIMER (1 << 29) + #define RT2860_NAG_RTY_MODE_TIMER (1 << 28) + #define RT2860_LONG_RTY_THRES_SHIFT 16 + #define RT2860_LONG_RTY_LIMIT_SHIFT 8 + #define RT2860_SHORT_RTY_LIMIT_SHIFT 0 + + /* possible flags for register TX_LINK_CFG */ + #define RT2860_REMOTE_MFS_SHIFT 24 + #define RT2860_REMOTE_MFB_SHIFT 16 + #define RT2860_TX_CFACK_EN (1 << 12) + #define RT2860_TX_RDG_EN (1 << 11) + #define RT2860_TX_MRQ_EN (1 << 10) + #define RT2860_REMOTE_UMFS_EN (1 << 9) + #define RT2860_TX_MFB_EN (1 << 8) + #define RT2860_REMOTE_MFB_LT_SHIFT 0 + + /* possible flags for registers *_PROT_CFG */ + #define RT2860_RTSTH_EN (1 << 26) + #define RT2860_TXOP_ALLOW_GF40 (1 << 25) + #define RT2860_TXOP_ALLOW_GF20 (1 << 24) + #define RT2860_TXOP_ALLOW_MM40 (1 << 23) + #define RT2860_TXOP_ALLOW_MM20 (1 << 22) + #define RT2860_TXOP_ALLOW_OFDM (1 << 21) + #define RT2860_TXOP_ALLOW_CCK (1 << 20) + #define RT2860_TXOP_ALLOW_ALL (0x3f << 20) + #define RT2860_PROT_NAV_SHORT (1 << 18) + #define RT2860_PROT_NAV_LONG (2 << 18) + #define RT2860_PROT_CTRL_RTS_CTS (1 << 16) + #define RT2860_PROT_CTRL_CTS (2 << 16) + + /* possible flags for registers EXP_{CTS,ACK}_TIME */ + #define RT2860_EXP_OFDM_TIME_SHIFT 16 + #define RT2860_EXP_CCK_TIME_SHIFT 0 + + /* possible flags for register RX_FILTR_CFG */ + #define RT2860_DROP_CTRL_RSV (1 << 16) + #define RT2860_DROP_BAR (1 << 15) + #define RT2860_DROP_BA (1 << 14) + #define RT2860_DROP_PSPOLL (1 << 13) + #define RT2860_DROP_RTS (1 << 12) + #define RT2860_DROP_CTS (1 << 11) + #define RT2860_DROP_ACK (1 << 10) + #define RT2860_DROP_CFEND (1 << 9) + #define RT2860_DROP_CFACK (1 << 8) + #define RT2860_DROP_DUPL (1 << 7) + #define RT2860_DROP_BC (1 << 6) + #define RT2860_DROP_MC (1 << 5) + #define RT2860_DROP_VER_ERR (1 << 4) + #define RT2860_DROP_NOT_MYBSS (1 << 3) + #define RT2860_DROP_UC_NOME (1 << 2) + #define RT2860_DROP_PHY_ERR (1 << 1) + #define RT2860_DROP_CRC_ERR (1 << 0) + + /* possible flags for register AUTO_RSP_CFG */ + #define RT2860_CTRL_PWR_BIT (1 << 7) + #define RT2860_BAC_ACK_POLICY (1 << 6) + #define RT2860_CCK_SHORT_EN (1 << 4) + #define RT2860_CTS_40M_REF_EN (1 << 3) + #define RT2860_CTS_40M_MODE_EN (1 << 2) + #define RT2860_BAC_ACKPOLICY_EN (1 << 1) + #define RT2860_AUTO_RSP_EN (1 << 0) + + /* possible flags for register SIFS_COST_CFG */ + #define RT2860_OFDM_SIFS_COST_SHIFT 8 + #define RT2860_CCK_SIFS_COST_SHIFT 0 + + /* possible flags for register TXOP_HLDR_ET */ + #define RT2860_TXOP_ETM1_EN (1 << 25) + #define RT2860_TXOP_ETM0_EN (1 << 24) + #define RT2860_TXOP_ETM_THRES_SHIFT 16 + #define RT2860_TXOP_ETO_EN (1 << 8) + #define RT2860_TXOP_ETO_THRES_SHIFT 1 + #define RT2860_PER_RX_RST_EN (1 << 0) + + /* possible flags for register TX_STAT_FIFO */ + #define RT2860_TXQ_MCS_SHIFT 16 + #define RT2860_TXQ_WCID_SHIFT 8 + #define RT2860_TXQ_ACKREQ (1 << 7) + #define RT2860_TXQ_AGG (1 << 6) + #define RT2860_TXQ_OK (1 << 5) + #define RT2860_TXQ_PID_SHIFT 1 + #define RT2860_TXQ_VLD (1 << 0) + + /* possible flags for register WCID_ATTR */ + #define RT2860_MODE_NOSEC 0 + #define RT2860_MODE_WEP40 1 + #define RT2860_MODE_WEP104 2 + #define RT2860_MODE_TKIP 3 + #define RT2860_MODE_AES_CCMP 4 + #define RT2860_MODE_CKIP40 5 + #define RT2860_MODE_CKIP104 6 + #define RT2860_MODE_CKIP128 7 + #define RT2860_RX_PKEY_EN (1 << 0) + + /* possible flags for register H2M_MAILBOX */ + #define RT2860_H2M_BUSY (1 << 24) + #define RT2860_TOKEN_NO_INTR 0xff + + + /* possible flags for MCU command RT2860_MCU_CMD_LEDS */ + #define RT2860_LED_RADIO (1 << 13) + #define RT2860_LED_LINK_2GHZ (1 << 14) + #define RT2860_LED_LINK_5GHZ (1 << 15) + + + /* possible flags for RT3020 RF register 1 */ + #define RT3070_RF_BLOCK (1 << 0) + #define RT3070_RX0_PD (1 << 2) + #define RT3070_TX0_PD (1 << 3) + #define RT3070_RX1_PD (1 << 4) + #define RT3070_TX1_PD (1 << 5) + + /* 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) + + + /* RT2860 TX descriptor */ + struct rt2860_txd { + uint32_t sdp0; /* Segment Data Pointer 0 */ + uint16_t sdl1; /* Segment Data Length 1 */ + #define RT2860_TX_BURST (1 << 15) + #define RT2860_TX_LS1 (1 << 14) /* SDP1 is the last segment */ + + uint16_t sdl0; /* Segment Data Length 0 */ + #define RT2860_TX_DDONE (1 << 15) + #define RT2860_TX_LS0 (1 << 14) /* SDP0 is the last segment */ + + uint32_t sdp1; /* Segment Data Pointer 1 */ + uint8_t reserved[3]; + uint8_t flags; + #define RT2860_TX_QSEL_SHIFT 1 + #define RT2860_TX_QSEL_MGMT (0 << 1) + #define RT2860_TX_QSEL_HCCA (1 << 1) + #define RT2860_TX_QSEL_EDCA (2 << 1) + #define RT2860_TX_WIV (1 << 0) + } __packed; + + /* RT2870 TX descriptor */ + struct rt2870_txd { + uint16_t len; + uint8_t pad; + uint8_t flags; + } __packed; + + /* TX Wireless Information */ + struct rt2860_txwi { + uint8_t flags; + #define RT2860_TX_MPDU_DSITY_SHIFT 5 + #define RT2860_TX_AMPDU (1 << 4) + #define RT2860_TX_TS (1 << 3) + #define RT2860_TX_CFACK (1 << 2) + #define RT2860_TX_MMPS (1 << 1) + #define RT2860_TX_FRAG (1 << 0) + + uint8_t txop; + #define RT2860_TX_TXOP_HT 0 + #define RT2860_TX_TXOP_PIFS 1 + #define RT2860_TX_TXOP_SIFS 2 + #define RT2860_TX_TXOP_BACKOFF 3 + + uint16_t phy; + #define RT2860_PHY_MODE 0xc000 + #define RT2860_PHY_CCK (0 << 14) + #define RT2860_PHY_OFDM (1 << 14) + #define RT2860_PHY_HT (2 << 14) + #define RT2860_PHY_HT_GF (3 << 14) + #define RT2860_PHY_SGI (1 << 8) + #define RT2860_PHY_BW40 (1 << 7) + #define RT2860_PHY_MCS 0x7f + #define RT2860_PHY_SHPRE (1 << 3) + + uint8_t xflags; + #define RT2860_TX_BAWINSIZE_SHIFT 2 + #define RT2860_TX_NSEQ (1 << 1) + #define RT2860_TX_ACK (1 << 0) + + uint8_t wcid; /* Wireless Client ID */ + uint16_t len; + #define RT2860_TX_PID_SHIFT 12 + + uint32_t iv; + uint32_t eiv; + } __packed; + + /* RT2860 RX descriptor */ + struct rt2860_rxd { + uint32_t sdp0; + uint16_t sdl1; /* unused */ + uint16_t sdl0; + #define RT2860_RX_DDONE (1 << 15) + #define RT2860_RX_LS0 (1 << 14) + + uint32_t sdp1; /* unused */ + uint32_t flags; + #define RT2860_RX_DEC (1 << 16) + #define RT2860_RX_AMPDU (1 << 15) + #define RT2860_RX_L2PAD (1 << 14) + #define RT2860_RX_RSSI (1 << 13) + #define RT2860_RX_HTC (1 << 12) + #define RT2860_RX_AMSDU (1 << 11) + #define RT2860_RX_MICERR (1 << 10) + #define RT2860_RX_ICVERR (1 << 9) + #define RT2860_RX_CRCERR (1 << 8) + #define RT2860_RX_MYBSS (1 << 7) + #define RT2860_RX_BC (1 << 6) + #define RT2860_RX_MC (1 << 5) + #define RT2860_RX_UC2ME (1 << 4) + #define RT2860_RX_FRAG (1 << 3) + #define RT2860_RX_NULL (1 << 2) + #define RT2860_RX_DATA (1 << 1) + #define RT2860_RX_BA (1 << 0) + } __packed; + + /* RT2870 RX descriptor */ + struct rt2870_rxd { + /* single 32-bit field */ + uint32_t flags; + } __packed; + + /* RX Wireless Information */ + struct rt2860_rxwi { + uint8_t wcid; + uint8_t keyidx; + #define RT2860_RX_UDF_SHIFT 5 + #define RT2860_RX_BSS_IDX_SHIFT 2 + + uint16_t len; + #define RT2860_RX_TID_SHIFT 12 + + uint16_t seq; + uint16_t phy; + uint8_t rssi[3]; + uint8_t reserved1; + uint8_t snr[2]; + uint16_t reserved2; + } __packed; + + + /* first DMA segment contains TXWI + 802.11 header + 32-bit padding */ + #define RT2860_TXWI_DMASZ \ + (sizeof (struct rt2860_txwi) + \ + /* sizeof (struct ieee80211_htframe) + */ \ + 512 + \ + sizeof (uint16_t)) + + #define RT2860_RF1 0 + #define RT2860_RF2 2 + #define RT2860_RF3 1 + #define RT2860_RF4 3 + + #define RT2860_RF_2820 1 /* 2T3R */ + #define RT2860_RF_2850 2 /* dual-band 2T3R */ + #define RT2860_RF_2720 3 /* 1T2R */ + #define RT2860_RF_2750 4 /* dual-band 1T2R */ + #define RT3070_RF_3020 5 /* 1T1R */ + #define RT3070_RF_2020 6 /* b/g */ + #define RT3070_RF_3021 7 /* 1T2R */ + #define RT3070_RF_3022 8 /* 2T2R */ + + /* USB commands for RT2870 only */ + #define RT2870_RESET 1 + #define RT2870_WRITE_2 2 + #define RT2870_WRITE_REGION_1 6 + #define RT2870_READ_REGION_1 7 + #define RT2870_EEPROM_READ 9 + + #define RT2860_EEPROM_DELAY 1 /* minimum hold time (microsecond) */ + + #define RT2860_EEPROM_VERSION 0x01 + #define RT2860_EEPROM_MAC01 0x02 + #define RT2860_EEPROM_MAC23 0x03 + #define RT2860_EEPROM_MAC45 0x04 + #define RT2860_EEPROM_PCIE_PSLEVEL 0x11 + #define RT2860_EEPROM_REV 0x12 + #define RT2860_EEPROM_ANTENNA 0x1a + #define RT2860_EEPROM_CONFIG 0x1b + #define RT2860_EEPROM_COUNTRY 0x1c + #define RT2860_EEPROM_FREQ_LEDS 0x1d + #define RT2860_EEPROM_LED1 0x1e + #define RT2860_EEPROM_LED2 0x1f + #define RT2860_EEPROM_LED3 0x20 + #define RT2860_EEPROM_LNA 0x22 + #define RT2860_EEPROM_RSSI1_2GHZ 0x23 + #define RT2860_EEPROM_RSSI2_2GHZ 0x24 + #define RT2860_EEPROM_RSSI1_5GHZ 0x25 + #define RT2860_EEPROM_RSSI2_5GHZ 0x26 + #define RT2860_EEPROM_DELTAPWR 0x28 + #define RT2860_EEPROM_PWR2GHZ_BASE1 0x29 + #define RT2860_EEPROM_PWR2GHZ_BASE2 0x30 + #define RT2860_EEPROM_TSSI1_2GHZ 0x37 + #define RT2860_EEPROM_TSSI2_2GHZ 0x38 + #define RT2860_EEPROM_TSSI3_2GHZ 0x39 + #define RT2860_EEPROM_TSSI4_2GHZ 0x3a + #define RT2860_EEPROM_TSSI5_2GHZ 0x3b + #define RT2860_EEPROM_PWR5GHZ_BASE1 0x3c + #define RT2860_EEPROM_PWR5GHZ_BASE2 0x53 + #define RT2860_EEPROM_TSSI1_5GHZ 0x6a + #define RT2860_EEPROM_TSSI2_5GHZ 0x6b + #define RT2860_EEPROM_TSSI3_5GHZ 0x6c + #define RT2860_EEPROM_TSSI4_5GHZ 0x6d + #define RT2860_EEPROM_TSSI5_5GHZ 0x6e + #define RT2860_EEPROM_RPWR 0x6f + #define RT2860_EEPROM_BBP_BASE 0x78 + + #define RT2860_RIDX_CCK1 0 + #define RT2860_RIDX_CCK11 3 + #define RT2860_RIDX_OFDM6 4 + #define RT2860_RIDX_MAX 11 + static const struct rt2860_rate { + uint8_t rate; + uint8_t mcs; + enum ieee80211_phytype phy; + uint8_t ctl_ridx; + uint16_t sp_ack_dur; + uint16_t lp_ack_dur; + } rt2860_rates[] = { + { 2, 0, IEEE80211_T_DS, 0, 304, 304 }, + { 4, 1, IEEE80211_T_DS, 1, 248, 152 }, + { 11, 2, IEEE80211_T_DS, 2, 213, 117 }, + { 22, 3, IEEE80211_T_DS, 3, 203, 107 }, + { 12, 0, IEEE80211_T_OFDM, 4, 50, 50 }, + { 18, 1, IEEE80211_T_OFDM, 4, 42, 42 }, + { 24, 2, IEEE80211_T_OFDM, 6, 38, 38 }, + { 36, 3, IEEE80211_T_OFDM, 6, 34, 34 }, + { 48, 4, IEEE80211_T_OFDM, 8, 34, 34 }, + { 72, 5, IEEE80211_T_OFDM, 8, 30, 30 }, + { 96, 6, IEEE80211_T_OFDM, 8, 30, 30 }, + { 108, 7, IEEE80211_T_OFDM, 8, 30, 30 } + }; + + /* + * Control and status registers access macros. + */ + #define RAL_READ(sc, reg) \ + bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) + + #define RAL_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + + #define RAL_BARRIER_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, 0x1800, \ + BUS_SPACE_BARRIER_WRITE) + + #define RAL_BARRIER_READ_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, 0x1800, \ + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) + + #define RAL_WRITE_REGION_1(sc, offset, datap, count) \ + bus_space_write_region_1((sc)->sc_st, (sc)->sc_sh, (offset), \ + (datap), (count)) + + #define RAL_SET_REGION_4(sc, offset, val, count) \ + bus_space_set_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ + (val), (count)) + + /* + * EEPROM access macro. + */ + #define RT2860_EEPROM_CTL(sc, val) do { \ + RAL_WRITE((sc), RT2860_PCI_EECTRL, (val)); \ + RAL_BARRIER_READ_WRITE((sc)); \ + DELAY(RT2860_EEPROM_DELAY); \ + } while (/* CONSTCOND */0) + + /* + * Default values for MAC registers; values taken from the reference driver. + */ + #define RT2860_DEF_MAC \ + { RT2860_BCN_OFFSET0, 0xf8f0e8e0 }, \ + { RT2860_LEGACY_BASIC_RATE, 0x0000013f }, \ + { RT2860_HT_BASIC_RATE, 0x00008003 }, \ + { RT2860_MAC_SYS_CTRL, 0x00000000 }, \ + { RT2860_BKOFF_SLOT_CFG, 0x00000209 }, \ + { RT2860_TX_SW_CFG0, 0x00000000 }, \ + { RT2860_TX_SW_CFG1, 0x00080606 }, \ + { RT2860_TX_LINK_CFG, 0x00001020 }, \ + { RT2860_TX_TIMEOUT_CFG, 0x000a2090 }, \ + { RT2860_LED_CFG, 0x7f031e46 }, \ + { RT2860_WMM_AIFSN_CFG, 0x00002273 }, \ + { RT2860_WMM_CWMIN_CFG, 0x00002344 }, \ + { RT2860_WMM_CWMAX_CFG, 0x000034aa }, \ + { RT2860_MAX_PCNT, 0x1f3fbf9f }, \ + { RT2860_TX_RTY_CFG, 0x47d01f0f }, \ + { RT2860_AUTO_RSP_CFG, 0x00000013 }, \ + { RT2860_CCK_PROT_CFG, 0x05740003 }, \ + { RT2860_OFDM_PROT_CFG, 0x05740003 }, \ + { RT2860_GF20_PROT_CFG, 0x01744004 }, \ + { RT2860_GF40_PROT_CFG, 0x03f44084 }, \ + { RT2860_MM20_PROT_CFG, 0x01744004 }, \ + { RT2860_MM40_PROT_CFG, 0x03f54084 }, \ + { RT2860_TXOP_CTRL_CFG, 0x0000583f }, \ + { RT2860_TXOP_HLDR_ET, 0x00000002 }, \ + { RT2860_TX_RTS_CFG, 0x00092b20 }, \ + { RT2860_EXP_ACK_TIME, 0x002400ca }, \ + { RT2860_XIFS_TIME_CFG, 0x33a41010 }, \ + { RT2860_PWR_PIN_CFG, 0x00000003 } + + /* XXX only a few registers differ from above, try to merge? */ + #define RT2870_DEF_MAC \ + { RT2860_BCN_OFFSET0, 0xf8f0e8e0 }, \ + { RT2860_LEGACY_BASIC_RATE, 0x0000013f }, \ + { RT2860_HT_BASIC_RATE, 0x00008003 }, \ + { RT2860_MAC_SYS_CTRL, 0x00000000 }, \ + { RT2860_BKOFF_SLOT_CFG, 0x00000209 }, \ + { RT2860_TX_SW_CFG0, 0x00000000 }, \ + { RT2860_TX_SW_CFG1, 0x00080606 }, \ + { RT2860_TX_LINK_CFG, 0x00001020 }, \ + { RT2860_TX_TIMEOUT_CFG, 0x000a2090 }, \ + { RT2860_LED_CFG, 0x7f031e46 }, \ + { RT2860_WMM_AIFSN_CFG, 0x00002273 }, \ + { RT2860_WMM_CWMIN_CFG, 0x00002344 }, \ + { RT2860_WMM_CWMAX_CFG, 0x000034aa }, \ + { RT2860_MAX_PCNT, 0x1f3fbf9f }, \ + { RT2860_TX_RTY_CFG, 0x47d01f0f }, \ + { RT2860_AUTO_RSP_CFG, 0x00000013 }, \ + { RT2860_CCK_PROT_CFG, 0x05740003 }, \ + { RT2860_OFDM_PROT_CFG, 0x05740003 }, \ + { RT2860_PBF_CFG, 0x00f40006 }, \ + { RT2860_WPDMA_GLO_CFG, 0x00000030 }, \ + { RT2860_GF20_PROT_CFG, 0x01744004 }, \ + { RT2860_GF40_PROT_CFG, 0x03f44084 }, \ + { RT2860_MM20_PROT_CFG, 0x01744004 }, \ + { RT2860_MM40_PROT_CFG, 0x03f44084 }, \ + { RT2860_TXOP_CTRL_CFG, 0x0000583f }, \ + { RT2860_TXOP_HLDR_ET, 0x00000002 }, \ + { RT2860_TX_RTS_CFG, 0x00092b20 }, \ + { RT2860_EXP_ACK_TIME, 0x002400ca }, \ + { RT2860_XIFS_TIME_CFG, 0x33a41010 }, \ + { RT2860_PWR_PIN_CFG, 0x00000003 } + + /* + * Default values for BBP registers; values taken from the reference driver. + */ + #define RT2860_DEF_BBP \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 69, 0x12 }, \ + { 70, 0x0a }, \ + { 73, 0x10 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x6a }, \ + { 84, 0x99 }, \ + { 86, 0x00 }, \ + { 91, 0x04 }, \ + { 92, 0x00 }, \ + { 103, 0x00 }, \ + { 105, 0x05 } + + /* + * Default settings for RF registers; values derived from the reference driver. + */ + #define 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 } + + #define RT3070_RF3020 \ + { 241, 2, 2 }, \ + { 241, 2, 7 }, \ + { 242, 2, 2 }, \ + { 242, 2, 7 }, \ + { 243, 2, 2 }, \ + { 243, 2, 7 }, \ + { 244, 2, 2 }, \ + { 244, 2, 7 }, \ + { 245, 2, 2 }, \ + { 245, 2, 7 }, \ + { 246, 2, 2 }, \ + { 246, 2, 7 }, \ + { 247, 2, 2 }, \ + { 248, 2, 4 } + + #define RT3070_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 } Index: sys/dev/cardbus/if_ral_cardbus.c *************** *** 19,25 **** */ /* ! * CardBus front-end for the Ralink RT2560/RT2561/RT2561S/RT2661 driver. */ #include __KERNEL_RCSID(0, "$NetBSD: if_ral_cardbus.c,v 1.14 2008/10/12 02:15:02 dholland Exp $"); --- 19,25 ---- */ /* ! * CardBus front-end for the Ralink RT2560/RT2561/RT2661/RT2860 driver. */ #include __KERNEL_RCSID(0, "$NetBSD: if_ral_cardbus.c,v 1.14 2008/10/12 02:15:02 dholland Exp $"); *************** *** 53,58 **** --- 53,59 ---- #include #include + #include #include #include *************** *** 74,85 **** --- 75,92 ---- rt2661_attach, rt2661_detach, rt2661_intr + + }, ral_rt2860_opns = { + rt2860_attach, + rt2860_detach, + rt2860_intr }; struct ral_cardbus_softc { union { struct rt2560_softc sc_rt2560; struct rt2661_softc sc_rt2661; + struct rt2860_softc sc_rt2860; } u; #define sc_sc u.sc_rt2560 *************** *** 117,127 **** --- 124,146 ---- case PCI_PRODUCT_RALINK_RT2561: case PCI_PRODUCT_RALINK_RT2561S: case PCI_PRODUCT_RALINK_RT2661: + case PCI_PRODUCT_RALINK_RT2860: + case PCI_PRODUCT_RALINK_RT2890: + case PCI_PRODUCT_RALINK_RT2760: + case PCI_PRODUCT_RALINK_RT2790: return 1; default: return 0; } } + #if 0 + if (PCI_VENDOR(ca->ca_id) == PCI_VENDOR_AWT) { + switch (PCI_PRODUCT(ca->ca_id)) { + case PCI_PRODUCT_AWT_RT2890: + return 1; + } + } + #endif return 0; } *************** *** 142,150 **** revision = PCI_REVISION(ca->ca_class); aprint_normal(": %s (rev. 0x%02x)\n", devinfo, revision); ! csc->sc_opns = ! (CARDBUS_PRODUCT(ca->ca_id) == PCI_PRODUCT_RALINK_RT2560) ? ! &ral_rt2560_opns : &ral_rt2661_opns; sc->sc_dmat = ca->ca_dmat; csc->sc_ct = ct; --- 161,183 ---- revision = PCI_REVISION(ca->ca_class); aprint_normal(": %s (rev. 0x%02x)\n", devinfo, revision); ! switch (CARDBUS_PRODUCT(ca->ca_id)) { ! case PCI_PRODUCT_RALINK_RT2560: ! csc->sc_opns = &ral_rt2560_opns; ! break; ! case PCI_PRODUCT_RALINK_RT2561: ! case PCI_PRODUCT_RALINK_RT2561S: ! case PCI_PRODUCT_RALINK_RT2661: ! csc->sc_opns = &ral_rt2661_opns; ! break; ! case PCI_PRODUCT_RALINK_RT2860: ! case PCI_PRODUCT_RALINK_RT2890: ! case PCI_PRODUCT_RALINK_RT2760: ! case PCI_PRODUCT_RALINK_RT2790: ! case PCI_PRODUCT_AWT_RT2890: ! csc->sc_opns = &ral_rt2860_opns; ! break; ! } sc->sc_dmat = ca->ca_dmat; csc->sc_ct = ct; Index: sys/conf/files *************** *** 617,622 **** --- 617,623 ---- device ral: arp, ether, ifnet, wlan, firmload file dev/ic/rt2560.c ral file dev/ic/rt2661.c ral + file dev/ic/rt2860.c ral # 3Com Etherlink-III Ethernet controller #