17453645fSAndriy Voskoboinyk /*- 27453645fSAndriy Voskoboinyk * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> 37453645fSAndriy Voskoboinyk * All rights reserved. 47453645fSAndriy Voskoboinyk * 57453645fSAndriy Voskoboinyk * Redistribution and use in source and binary forms, with or without 67453645fSAndriy Voskoboinyk * modification, are permitted provided that the following conditions 77453645fSAndriy Voskoboinyk * are met: 87453645fSAndriy Voskoboinyk * 1. Redistributions of source code must retain the above copyright 97453645fSAndriy Voskoboinyk * notice, this list of conditions and the following disclaimer. 107453645fSAndriy Voskoboinyk * 2. Redistributions in binary form must reproduce the above copyright 117453645fSAndriy Voskoboinyk * notice, this list of conditions and the following disclaimer in the 127453645fSAndriy Voskoboinyk * documentation and/or other materials provided with the distribution. 137453645fSAndriy Voskoboinyk * 147453645fSAndriy Voskoboinyk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 157453645fSAndriy Voskoboinyk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 167453645fSAndriy Voskoboinyk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 177453645fSAndriy Voskoboinyk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 187453645fSAndriy Voskoboinyk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 197453645fSAndriy Voskoboinyk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 207453645fSAndriy Voskoboinyk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 217453645fSAndriy Voskoboinyk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 227453645fSAndriy Voskoboinyk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 237453645fSAndriy Voskoboinyk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 247453645fSAndriy Voskoboinyk * SUCH DAMAGE. 257453645fSAndriy Voskoboinyk */ 267453645fSAndriy Voskoboinyk 277453645fSAndriy Voskoboinyk #include <sys/cdefs.h> 287453645fSAndriy Voskoboinyk #include "opt_wlan.h" 297453645fSAndriy Voskoboinyk 307453645fSAndriy Voskoboinyk #include <sys/param.h> 317453645fSAndriy Voskoboinyk #include <sys/lock.h> 327453645fSAndriy Voskoboinyk #include <sys/mutex.h> 337453645fSAndriy Voskoboinyk #include <sys/mbuf.h> 347453645fSAndriy Voskoboinyk #include <sys/kernel.h> 357453645fSAndriy Voskoboinyk #include <sys/socket.h> 367453645fSAndriy Voskoboinyk #include <sys/systm.h> 377453645fSAndriy Voskoboinyk #include <sys/malloc.h> 387453645fSAndriy Voskoboinyk #include <sys/queue.h> 397453645fSAndriy Voskoboinyk #include <sys/taskqueue.h> 407453645fSAndriy Voskoboinyk #include <sys/bus.h> 417453645fSAndriy Voskoboinyk #include <sys/endian.h> 427453645fSAndriy Voskoboinyk #include <sys/linker.h> 437453645fSAndriy Voskoboinyk 447453645fSAndriy Voskoboinyk #include <net/if.h> 457453645fSAndriy Voskoboinyk #include <net/ethernet.h> 467453645fSAndriy Voskoboinyk #include <net/if_media.h> 477453645fSAndriy Voskoboinyk 487453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h> 497453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h> 50*82182587SAdrian Chadd #include <net80211/ieee80211_vht.h> 517453645fSAndriy Voskoboinyk 527453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h> 537453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h> 547453645fSAndriy Voskoboinyk 557453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_ridx.h> 567453645fSAndriy Voskoboinyk 577453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8812a/r12a.h> 587453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8812a/r12a_tx_desc.h> 597453645fSAndriy Voskoboinyk 60d82bfe73SAdrian Chadd /* 61d82bfe73SAdrian Chadd * This function actually handles the secondary channel mapping, 62d82bfe73SAdrian Chadd * not the primary channel mapping. It hints to the MAC where 63d82bfe73SAdrian Chadd * to handle duplicate transmission of the RTS/CTS and payload 64d82bfe73SAdrian Chadd * frames when the requested transmit channel width is less than 65d82bfe73SAdrian Chadd * the configured channel width. 66d82bfe73SAdrian Chadd * 67d82bfe73SAdrian Chadd * Note: the vendor driver and linux rtw88 driver both leave this 68d82bfe73SAdrian Chadd * field currently set to 0. 69d82bfe73SAdrian Chadd * 70d82bfe73SAdrian Chadd * See the rtl8812au vendor driver, hal/rtl8812a_xmit.c:SCMapping_8812() 71d82bfe73SAdrian Chadd * and where it's used (and ignored.) 72d82bfe73SAdrian Chadd */ 737453645fSAndriy Voskoboinyk static int 747453645fSAndriy Voskoboinyk r12a_get_primary_channel(struct rtwn_softc *sc, struct ieee80211_channel *c) 757453645fSAndriy Voskoboinyk { 76d82bfe73SAdrian Chadd #if 0 7735135609SAdrian Chadd /* XXX VHT80; VHT40 */ 787453645fSAndriy Voskoboinyk if (IEEE80211_IS_CHAN_HT40U(c)) 797453645fSAndriy Voskoboinyk return (R12A_TXDW5_PRIM_CHAN_20_80_2); 807453645fSAndriy Voskoboinyk else 817453645fSAndriy Voskoboinyk return (R12A_TXDW5_PRIM_CHAN_20_80_3); 82d82bfe73SAdrian Chadd #endif 83d82bfe73SAdrian Chadd 84d82bfe73SAdrian Chadd /* 85d82bfe73SAdrian Chadd * For now just return the VHT_DATA_SC_DONOT_CARE value 86d82bfe73SAdrian Chadd * from the reference driver. 87d82bfe73SAdrian Chadd */ 88d82bfe73SAdrian Chadd return (0); 897453645fSAndriy Voskoboinyk } 907453645fSAndriy Voskoboinyk 91*82182587SAdrian Chadd /* 92*82182587SAdrian Chadd * Configure VHT20/VHT40/VHT80 as appropriate. 93*82182587SAdrian Chadd * 94*82182587SAdrian Chadd * This is only called for VHT, not for HT. 95*82182587SAdrian Chadd */ 96*82182587SAdrian Chadd static void 97*82182587SAdrian Chadd r12a_tx_set_vht_bw(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) 98*82182587SAdrian Chadd { 99*82182587SAdrian Chadd struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf; 100*82182587SAdrian Chadd int prim_chan; 101*82182587SAdrian Chadd 102*82182587SAdrian Chadd prim_chan = r12a_get_primary_channel(sc, ni->ni_chan); 103*82182587SAdrian Chadd 104*82182587SAdrian Chadd if (ieee80211_vht_check_tx_bw(ni, IEEE80211_STA_RX_BW_80)) { 105*82182587SAdrian Chadd txd->txdw5 |= htole32(SM(R12A_TXDW5_DATA_BW, 106*82182587SAdrian Chadd R12A_TXDW5_DATA_BW80)); 107*82182587SAdrian Chadd txd->txdw5 |= htole32(SM(R12A_TXDW5_DATA_PRIM_CHAN, 108*82182587SAdrian Chadd prim_chan)); 109*82182587SAdrian Chadd } else if (ieee80211_vht_check_tx_bw(ni, IEEE80211_STA_RX_BW_40)) { 110*82182587SAdrian Chadd txd->txdw5 |= htole32(SM(R12A_TXDW5_DATA_BW, 111*82182587SAdrian Chadd R12A_TXDW5_DATA_BW40)); 112*82182587SAdrian Chadd txd->txdw5 |= htole32(SM(R12A_TXDW5_DATA_PRIM_CHAN, 113*82182587SAdrian Chadd prim_chan)); 114*82182587SAdrian Chadd } 115*82182587SAdrian Chadd } 116*82182587SAdrian Chadd 117*82182587SAdrian Chadd /* 118*82182587SAdrian Chadd * Configure HT20/HT40 as appropriate. 119*82182587SAdrian Chadd * 120*82182587SAdrian Chadd * This is only called for HT, not for VHT. 121*82182587SAdrian Chadd */ 1227453645fSAndriy Voskoboinyk static void 1237453645fSAndriy Voskoboinyk r12a_tx_set_ht40(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) 1247453645fSAndriy Voskoboinyk { 1257453645fSAndriy Voskoboinyk struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf; 1267453645fSAndriy Voskoboinyk 12735135609SAdrian Chadd if (ieee80211_ht_check_tx_ht40(ni)) { 1287453645fSAndriy Voskoboinyk int prim_chan; 1297453645fSAndriy Voskoboinyk 1307453645fSAndriy Voskoboinyk prim_chan = r12a_get_primary_channel(sc, ni->ni_chan); 1317453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(SM(R12A_TXDW5_DATA_BW, 1327453645fSAndriy Voskoboinyk R12A_TXDW5_DATA_BW40)); 1337453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(SM(R12A_TXDW5_DATA_PRIM_CHAN, 1347453645fSAndriy Voskoboinyk prim_chan)); 1357453645fSAndriy Voskoboinyk } 1367453645fSAndriy Voskoboinyk } 1377453645fSAndriy Voskoboinyk 1387453645fSAndriy Voskoboinyk static void 1397453645fSAndriy Voskoboinyk r12a_tx_protection(struct rtwn_softc *sc, struct r12a_tx_desc *txd, 1407453645fSAndriy Voskoboinyk enum ieee80211_protmode mode, uint8_t ridx) 1417453645fSAndriy Voskoboinyk { 1427453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 1437453645fSAndriy Voskoboinyk uint8_t rate; 1447453645fSAndriy Voskoboinyk 1457453645fSAndriy Voskoboinyk switch (mode) { 1467453645fSAndriy Voskoboinyk case IEEE80211_PROT_CTSONLY: 1477453645fSAndriy Voskoboinyk txd->txdw3 |= htole32(R12A_TXDW3_CTS2SELF); 1487453645fSAndriy Voskoboinyk break; 1497453645fSAndriy Voskoboinyk case IEEE80211_PROT_RTSCTS: 1507453645fSAndriy Voskoboinyk txd->txdw3 |= htole32(R12A_TXDW3_RTSEN); 1517453645fSAndriy Voskoboinyk break; 1527453645fSAndriy Voskoboinyk default: 1537453645fSAndriy Voskoboinyk break; 1547453645fSAndriy Voskoboinyk } 1557453645fSAndriy Voskoboinyk 1567453645fSAndriy Voskoboinyk if (mode == IEEE80211_PROT_CTSONLY || 1577453645fSAndriy Voskoboinyk mode == IEEE80211_PROT_RTSCTS) { 15893411b39SAdrian Chadd /* 15993411b39SAdrian Chadd * Note: this code assumes basic rates for protection for 16093411b39SAdrian Chadd * both 802.11abg and 802.11n rates. 16193411b39SAdrian Chadd */ 16293411b39SAdrian Chadd if (RTWN_RATE_IS_VHT(ridx)) 16393411b39SAdrian Chadd rate = rtwn_ctl_vhtrate(ic->ic_rt, ridx); 16493411b39SAdrian Chadd else if (RTWN_RATE_IS_HT(ridx)) 1657453645fSAndriy Voskoboinyk rate = rtwn_ctl_mcsrate(ic->ic_rt, ridx); 1667453645fSAndriy Voskoboinyk else 1677453645fSAndriy Voskoboinyk rate = ieee80211_ctl_rate(ic->ic_rt, ridx2rate[ridx]); 16893411b39SAdrian Chadd /* Map basic rate back to ridx */ 16987339626SAndriy Voskoboinyk ridx = rate2ridx(IEEE80211_RV(rate)); 1707453645fSAndriy Voskoboinyk 1717453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R12A_TXDW4_RTSRATE, ridx)); 1727453645fSAndriy Voskoboinyk /* RTS rate fallback limit (max). */ 1737453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R12A_TXDW4_RTSRATE_FB_LMT, 0xf)); 1747453645fSAndriy Voskoboinyk 1757453645fSAndriy Voskoboinyk if (RTWN_RATE_IS_CCK(ridx) && ridx != RTWN_RIDX_CCK1 && 1767453645fSAndriy Voskoboinyk (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) 1777453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(R12A_TXDW5_RTS_SHORT); 1787453645fSAndriy Voskoboinyk } 1797453645fSAndriy Voskoboinyk } 1807453645fSAndriy Voskoboinyk 1817453645fSAndriy Voskoboinyk static void 1827453645fSAndriy Voskoboinyk r12a_tx_raid(struct rtwn_softc *sc, struct r12a_tx_desc *txd, 1837453645fSAndriy Voskoboinyk struct ieee80211_node *ni, int ismcast) 1847453645fSAndriy Voskoboinyk { 1857453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 1867453645fSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 1877453645fSAndriy Voskoboinyk struct ieee80211_channel *chan; 1887453645fSAndriy Voskoboinyk enum ieee80211_phymode mode; 1897453645fSAndriy Voskoboinyk uint8_t raid; 1907453645fSAndriy Voskoboinyk 1917453645fSAndriy Voskoboinyk chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? 1927453645fSAndriy Voskoboinyk ni->ni_chan : ic->ic_curchan; 1937453645fSAndriy Voskoboinyk mode = ieee80211_chan2mode(chan); 1947453645fSAndriy Voskoboinyk 1957453645fSAndriy Voskoboinyk /* NB: group addressed frames are done at 11bg rates for now */ 1967453645fSAndriy Voskoboinyk if (ismcast || !(ni->ni_flags & IEEE80211_NODE_HT)) { 1977453645fSAndriy Voskoboinyk switch (mode) { 1987453645fSAndriy Voskoboinyk case IEEE80211_MODE_11A: 1997453645fSAndriy Voskoboinyk case IEEE80211_MODE_11B: 2007453645fSAndriy Voskoboinyk case IEEE80211_MODE_11G: 2017453645fSAndriy Voskoboinyk break; 2027453645fSAndriy Voskoboinyk case IEEE80211_MODE_11NA: 2037453645fSAndriy Voskoboinyk mode = IEEE80211_MODE_11A; 2047453645fSAndriy Voskoboinyk break; 2057453645fSAndriy Voskoboinyk case IEEE80211_MODE_11NG: 2067453645fSAndriy Voskoboinyk mode = IEEE80211_MODE_11G; 2077453645fSAndriy Voskoboinyk break; 2084fa68495SAdrian Chadd case IEEE80211_MODE_VHT_5GHZ: 2094fa68495SAdrian Chadd mode = IEEE80211_MODE_VHT_5GHZ; 2104fa68495SAdrian Chadd break; 2117453645fSAndriy Voskoboinyk default: 2127453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "unknown mode(1) %d!\n", 2137453645fSAndriy Voskoboinyk ic->ic_curmode); 2147453645fSAndriy Voskoboinyk return; 2157453645fSAndriy Voskoboinyk } 2167453645fSAndriy Voskoboinyk } 2177453645fSAndriy Voskoboinyk 2187453645fSAndriy Voskoboinyk switch (mode) { 2197453645fSAndriy Voskoboinyk case IEEE80211_MODE_11A: 2207453645fSAndriy Voskoboinyk raid = R12A_RAID_11G; 2217453645fSAndriy Voskoboinyk break; 2227453645fSAndriy Voskoboinyk case IEEE80211_MODE_11B: 2237453645fSAndriy Voskoboinyk raid = R12A_RAID_11B; 2247453645fSAndriy Voskoboinyk break; 2257453645fSAndriy Voskoboinyk case IEEE80211_MODE_11G: 2267453645fSAndriy Voskoboinyk if (vap->iv_flags & IEEE80211_F_PUREG) 2277453645fSAndriy Voskoboinyk raid = R12A_RAID_11G; 2287453645fSAndriy Voskoboinyk else 2297453645fSAndriy Voskoboinyk raid = R12A_RAID_11BG; 2307453645fSAndriy Voskoboinyk break; 2317453645fSAndriy Voskoboinyk case IEEE80211_MODE_11NA: 2327453645fSAndriy Voskoboinyk if (sc->ntxchains == 1) 2337453645fSAndriy Voskoboinyk raid = R12A_RAID_11GN_1; 2347453645fSAndriy Voskoboinyk else 2357453645fSAndriy Voskoboinyk raid = R12A_RAID_11GN_2; 2367453645fSAndriy Voskoboinyk break; 2377453645fSAndriy Voskoboinyk case IEEE80211_MODE_11NG: 2387453645fSAndriy Voskoboinyk if (sc->ntxchains == 1) { 2397453645fSAndriy Voskoboinyk if (IEEE80211_IS_CHAN_HT40(chan)) 2407453645fSAndriy Voskoboinyk raid = R12A_RAID_11BGN_1_40; 2417453645fSAndriy Voskoboinyk else 2427453645fSAndriy Voskoboinyk raid = R12A_RAID_11BGN_1; 2437453645fSAndriy Voskoboinyk } else { 2447453645fSAndriy Voskoboinyk if (IEEE80211_IS_CHAN_HT40(chan)) 2457453645fSAndriy Voskoboinyk raid = R12A_RAID_11BGN_2_40; 2467453645fSAndriy Voskoboinyk else 2477453645fSAndriy Voskoboinyk raid = R12A_RAID_11BGN_2; 2487453645fSAndriy Voskoboinyk } 2497453645fSAndriy Voskoboinyk break; 2504fa68495SAdrian Chadd case IEEE80211_MODE_VHT_5GHZ: 2514fa68495SAdrian Chadd if (sc->ntxchains == 1) 2524fa68495SAdrian Chadd raid = R12A_RAID_11AC_1; 2534fa68495SAdrian Chadd else 2544fa68495SAdrian Chadd raid = R12A_RAID_11AC_2; 2554fa68495SAdrian Chadd break; 2567453645fSAndriy Voskoboinyk default: 2577453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "unknown mode(2) %d!\n", mode); 2587453645fSAndriy Voskoboinyk return; 2597453645fSAndriy Voskoboinyk } 2607453645fSAndriy Voskoboinyk 2617453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R12A_TXDW1_RAID, raid)); 2627453645fSAndriy Voskoboinyk } 2637453645fSAndriy Voskoboinyk 2647453645fSAndriy Voskoboinyk static void 2657453645fSAndriy Voskoboinyk r12a_tx_set_sgi(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) 2667453645fSAndriy Voskoboinyk { 2677453645fSAndriy Voskoboinyk struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf; 2687453645fSAndriy Voskoboinyk 269e1eff81eSAdrian Chadd /* TODO: VHT 20/40/80 checks */ 270e1eff81eSAdrian Chadd 2716749f059SAdrian Chadd /* 2726749f059SAdrian Chadd * Only enable short-GI if we're transmitting in that 2736749f059SAdrian Chadd * width to that node. 2746749f059SAdrian Chadd * 2756749f059SAdrian Chadd * Specifically, do not enable shortgi for 20MHz if 2766749f059SAdrian Chadd * we're attempting to transmit at 40MHz. 2776749f059SAdrian Chadd */ 2786749f059SAdrian Chadd if (ieee80211_ht_check_tx_ht40(ni)) { 2796749f059SAdrian Chadd if (ieee80211_ht_check_tx_shortgi_40(ni)) 2807453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); 2816749f059SAdrian Chadd } else if (ieee80211_ht_check_tx_ht(ni)) { 2826749f059SAdrian Chadd if (ieee80211_ht_check_tx_shortgi_20(ni)) 2837453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); 2847453645fSAndriy Voskoboinyk } 2856749f059SAdrian Chadd } 2867453645fSAndriy Voskoboinyk 2873111723cSAndriy Voskoboinyk static void 2883111723cSAndriy Voskoboinyk r12a_tx_set_ldpc(struct rtwn_softc *sc, struct r12a_tx_desc *txd, 2893111723cSAndriy Voskoboinyk struct ieee80211_node *ni) 2903111723cSAndriy Voskoboinyk { 2913111723cSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 2923111723cSAndriy Voskoboinyk 2933111723cSAndriy Voskoboinyk if ((vap->iv_flags_ht & IEEE80211_FHT_LDPC_TX) && 2943111723cSAndriy Voskoboinyk (ni->ni_htcap & IEEE80211_HTCAP_LDPC)) 2953111723cSAndriy Voskoboinyk txd->txdw5 |= htole32(R12A_TXDW5_DATA_LDPC); 2963111723cSAndriy Voskoboinyk } 2973111723cSAndriy Voskoboinyk 29877e64f45SAdrian Chadd static int 29977e64f45SAdrian Chadd r12a_calculate_tx_agg_window(struct rtwn_softc *sc, 30077e64f45SAdrian Chadd const struct ieee80211_node *ni, int tid) 30177e64f45SAdrian Chadd { 30277e64f45SAdrian Chadd const struct ieee80211_tx_ampdu *tap; 30377e64f45SAdrian Chadd int wnd; 30477e64f45SAdrian Chadd 30577e64f45SAdrian Chadd tap = &ni->ni_tx_ampdu[tid]; 30677e64f45SAdrian Chadd 30777e64f45SAdrian Chadd /* 30877e64f45SAdrian Chadd * BAW is (MAX_AGG * 2) + 1, hence the /2 here. 30977e64f45SAdrian Chadd * Ensure we don't send 0 or more than 64. 31077e64f45SAdrian Chadd */ 31177e64f45SAdrian Chadd wnd = tap->txa_wnd / 2; 31277e64f45SAdrian Chadd if (wnd == 0) 31377e64f45SAdrian Chadd wnd = 1; 31477e64f45SAdrian Chadd else if (wnd > 0x1f) 31577e64f45SAdrian Chadd wnd = 0x1f; 31677e64f45SAdrian Chadd 31777e64f45SAdrian Chadd return (wnd); 31877e64f45SAdrian Chadd } 31977e64f45SAdrian Chadd 3207453645fSAndriy Voskoboinyk void 3217453645fSAndriy Voskoboinyk r12a_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, 32264ecfc27SAdrian Chadd struct mbuf *m, void *buf, uint8_t ridx, bool force_rate, int maxretry) 3237453645fSAndriy Voskoboinyk { 3247453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 3257453645fSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 3267453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 3277453645fSAndriy Voskoboinyk struct ieee80211_frame *wh; 3287453645fSAndriy Voskoboinyk struct r12a_tx_desc *txd; 3297453645fSAndriy Voskoboinyk enum ieee80211_protmode prot; 3307453645fSAndriy Voskoboinyk uint8_t type, tid, qos, qsel; 3317453645fSAndriy Voskoboinyk int hasqos, ismcast, macid; 3327453645fSAndriy Voskoboinyk 3337453645fSAndriy Voskoboinyk wh = mtod(m, struct ieee80211_frame *); 3347453645fSAndriy Voskoboinyk type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3357453645fSAndriy Voskoboinyk hasqos = IEEE80211_QOS_HAS_SEQ(wh); 3367453645fSAndriy Voskoboinyk ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 3377453645fSAndriy Voskoboinyk 3387453645fSAndriy Voskoboinyk /* Select TX ring for this frame. */ 3397453645fSAndriy Voskoboinyk if (hasqos) { 3407453645fSAndriy Voskoboinyk qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; 3417453645fSAndriy Voskoboinyk tid = qos & IEEE80211_QOS_TID; 3427453645fSAndriy Voskoboinyk } else { 3437453645fSAndriy Voskoboinyk qos = 0; 3447453645fSAndriy Voskoboinyk tid = 0; 3457453645fSAndriy Voskoboinyk } 3467453645fSAndriy Voskoboinyk 3477453645fSAndriy Voskoboinyk /* Fill Tx descriptor. */ 3487453645fSAndriy Voskoboinyk txd = (struct r12a_tx_desc *)buf; 3497453645fSAndriy Voskoboinyk txd->flags0 |= R12A_FLAGS0_LSG | R12A_FLAGS0_FSG; 3507453645fSAndriy Voskoboinyk if (ismcast) 3517453645fSAndriy Voskoboinyk txd->flags0 |= R12A_FLAGS0_BMCAST; 3527453645fSAndriy Voskoboinyk 3537453645fSAndriy Voskoboinyk if (!ismcast) { 3547453645fSAndriy Voskoboinyk /* Unicast frame, check if an ACK is expected. */ 3557453645fSAndriy Voskoboinyk if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != 3567453645fSAndriy Voskoboinyk IEEE80211_QOS_ACKPOLICY_NOACK) { 3577453645fSAndriy Voskoboinyk txd->txdw4 = htole32(R12A_TXDW4_RETRY_LMT_ENA); 3587453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R12A_TXDW4_RETRY_LMT, 3597453645fSAndriy Voskoboinyk maxretry)); 3607453645fSAndriy Voskoboinyk } 3617453645fSAndriy Voskoboinyk 3627453645fSAndriy Voskoboinyk struct rtwn_node *un = RTWN_NODE(ni); 3637453645fSAndriy Voskoboinyk macid = un->id; 3647453645fSAndriy Voskoboinyk 3657453645fSAndriy Voskoboinyk if (type == IEEE80211_FC0_TYPE_DATA) { 3667453645fSAndriy Voskoboinyk qsel = tid % RTWN_MAX_TID; 3677453645fSAndriy Voskoboinyk 3687453645fSAndriy Voskoboinyk if (m->m_flags & M_AMPDU_MPDU) { 3697453645fSAndriy Voskoboinyk txd->txdw2 |= htole32(R12A_TXDW2_AGGEN); 3707453645fSAndriy Voskoboinyk txd->txdw2 |= htole32(SM(R12A_TXDW2_AMPDU_DEN, 37177e64f45SAdrian Chadd ieee80211_ht_get_node_ampdu_density(ni))); 3727453645fSAndriy Voskoboinyk txd->txdw3 |= htole32(SM(R12A_TXDW3_MAX_AGG, 37377e64f45SAdrian Chadd r12a_calculate_tx_agg_window(sc, ni, tid))); 3747453645fSAndriy Voskoboinyk } else 3757453645fSAndriy Voskoboinyk txd->txdw2 |= htole32(R12A_TXDW2_AGGBK); 3767453645fSAndriy Voskoboinyk 3777453645fSAndriy Voskoboinyk if (sc->sc_ratectl == RTWN_RATECTL_NET80211) { 3787453645fSAndriy Voskoboinyk txd->txdw2 |= htole32(R12A_TXDW2_SPE_RPT); 3797453645fSAndriy Voskoboinyk sc->sc_tx_n_active++; 3807453645fSAndriy Voskoboinyk } 3817453645fSAndriy Voskoboinyk 3827453645fSAndriy Voskoboinyk if (RTWN_RATE_IS_CCK(ridx) && ridx != RTWN_RIDX_CCK1 && 3837453645fSAndriy Voskoboinyk (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) 3847453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); 3857453645fSAndriy Voskoboinyk 3867453645fSAndriy Voskoboinyk prot = IEEE80211_PROT_NONE; 387*82182587SAdrian Chadd if (RTWN_RATE_IS_VHT(ridx)) { 388*82182587SAdrian Chadd r12a_tx_set_vht_bw(sc, txd, ni); 389*82182587SAdrian Chadd /* XXX TODO: sgi */ 390*82182587SAdrian Chadd /* XXX TODO: ldpc */ 391*82182587SAdrian Chadd prot = ic->ic_htprotmode; 392*82182587SAdrian Chadd } else if (RTWN_RATE_IS_HT(ridx)) { 3937453645fSAndriy Voskoboinyk r12a_tx_set_ht40(sc, txd, ni); 3947453645fSAndriy Voskoboinyk r12a_tx_set_sgi(sc, txd, ni); 3953111723cSAndriy Voskoboinyk r12a_tx_set_ldpc(sc, txd, ni); 3967453645fSAndriy Voskoboinyk prot = ic->ic_htprotmode; 3977453645fSAndriy Voskoboinyk } else if (ic->ic_flags & IEEE80211_F_USEPROT) 3987453645fSAndriy Voskoboinyk prot = ic->ic_protmode; 3997453645fSAndriy Voskoboinyk 4007453645fSAndriy Voskoboinyk /* XXX fix last comparison for A-MSDU (in net80211) */ 4017453645fSAndriy Voskoboinyk /* XXX A-MPDU? */ 4027453645fSAndriy Voskoboinyk if (m->m_pkthdr.len + IEEE80211_CRC_LEN > 4037453645fSAndriy Voskoboinyk vap->iv_rtsthreshold && 4047453645fSAndriy Voskoboinyk vap->iv_rtsthreshold != IEEE80211_RTS_MAX) 4057453645fSAndriy Voskoboinyk prot = IEEE80211_PROT_RTSCTS; 4067453645fSAndriy Voskoboinyk 4077453645fSAndriy Voskoboinyk if (prot != IEEE80211_PROT_NONE) 4087453645fSAndriy Voskoboinyk r12a_tx_protection(sc, txd, prot, ridx); 4097453645fSAndriy Voskoboinyk } else /* IEEE80211_FC0_TYPE_MGT */ 4107453645fSAndriy Voskoboinyk qsel = R12A_TXDW1_QSEL_MGNT; 4117453645fSAndriy Voskoboinyk } else { 4127453645fSAndriy Voskoboinyk macid = RTWN_MACID_BC; 4137453645fSAndriy Voskoboinyk qsel = R12A_TXDW1_QSEL_MGNT; 4147453645fSAndriy Voskoboinyk } 4157453645fSAndriy Voskoboinyk 4167453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R12A_TXDW1_QSEL, qsel)); 4177453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R12A_TXDW1_MACID, macid)); 4187453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE, ridx)); 4197453645fSAndriy Voskoboinyk /* Data rate fallback limit (max). */ 4207453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE_FB_LMT, 0x1f)); 4217453645fSAndriy Voskoboinyk /* XXX recheck for non-21au */ 4227453645fSAndriy Voskoboinyk txd->txdw6 |= htole32(SM(R21A_TXDW6_MBSSID, uvp->id)); 4237453645fSAndriy Voskoboinyk r12a_tx_raid(sc, txd, ni, ismcast); 4247453645fSAndriy Voskoboinyk 4257453645fSAndriy Voskoboinyk /* Force this rate if needed. */ 4267453645fSAndriy Voskoboinyk if (sc->sc_ratectl != RTWN_RATECTL_FW) 4277453645fSAndriy Voskoboinyk txd->txdw3 |= htole32(R12A_TXDW3_DRVRATE); 4287453645fSAndriy Voskoboinyk 4297453645fSAndriy Voskoboinyk if (!hasqos) { 4307453645fSAndriy Voskoboinyk /* Use HW sequence numbering for non-QoS frames. */ 4317453645fSAndriy Voskoboinyk txd->txdw8 |= htole32(R12A_TXDW8_HWSEQ_EN); 4327453645fSAndriy Voskoboinyk txd->txdw3 |= htole32(SM(R12A_TXDW3_SEQ_SEL, uvp->id)); 4337453645fSAndriy Voskoboinyk } else { 4347453645fSAndriy Voskoboinyk uint16_t seqno; 4357453645fSAndriy Voskoboinyk 4367453645fSAndriy Voskoboinyk if (m->m_flags & M_AMPDU_MPDU) { 4377453645fSAndriy Voskoboinyk seqno = ni->ni_txseqs[tid]; 4387453645fSAndriy Voskoboinyk ni->ni_txseqs[tid]++; 4397453645fSAndriy Voskoboinyk } else 4407453645fSAndriy Voskoboinyk seqno = M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE; 4417453645fSAndriy Voskoboinyk 4427453645fSAndriy Voskoboinyk /* Set sequence number. */ 4437453645fSAndriy Voskoboinyk txd->txdw9 |= htole32(SM(R12A_TXDW9_SEQ, seqno)); 4447453645fSAndriy Voskoboinyk } 4457453645fSAndriy Voskoboinyk } 4467453645fSAndriy Voskoboinyk 4477453645fSAndriy Voskoboinyk void 4487453645fSAndriy Voskoboinyk r12a_fill_tx_desc_raw(struct rtwn_softc *sc, struct ieee80211_node *ni, 4497453645fSAndriy Voskoboinyk struct mbuf *m, void *buf, const struct ieee80211_bpf_params *params) 4507453645fSAndriy Voskoboinyk { 4517453645fSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 4527453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 4537453645fSAndriy Voskoboinyk struct ieee80211_frame *wh; 4547453645fSAndriy Voskoboinyk struct r12a_tx_desc *txd; 4557453645fSAndriy Voskoboinyk uint8_t ridx; 4567453645fSAndriy Voskoboinyk int ismcast; 4577453645fSAndriy Voskoboinyk 4587453645fSAndriy Voskoboinyk /* XXX TODO: 11n checks, matching rtwn_fill_tx_desc() */ 4597453645fSAndriy Voskoboinyk 4607453645fSAndriy Voskoboinyk wh = mtod(m, struct ieee80211_frame *); 4617453645fSAndriy Voskoboinyk ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 4627453645fSAndriy Voskoboinyk ridx = rate2ridx(params->ibp_rate0); 4637453645fSAndriy Voskoboinyk 4647453645fSAndriy Voskoboinyk /* Fill Tx descriptor. */ 4657453645fSAndriy Voskoboinyk txd = (struct r12a_tx_desc *)buf; 4667453645fSAndriy Voskoboinyk txd->flags0 |= R12A_FLAGS0_LSG | R12A_FLAGS0_FSG; 4677453645fSAndriy Voskoboinyk if (ismcast) 4687453645fSAndriy Voskoboinyk txd->flags0 |= R12A_FLAGS0_BMCAST; 4697453645fSAndriy Voskoboinyk 4707453645fSAndriy Voskoboinyk if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { 4717453645fSAndriy Voskoboinyk txd->txdw4 = htole32(R12A_TXDW4_RETRY_LMT_ENA); 4727453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R12A_TXDW4_RETRY_LMT, 4737453645fSAndriy Voskoboinyk params->ibp_try0)); 4747453645fSAndriy Voskoboinyk } 4757453645fSAndriy Voskoboinyk if (params->ibp_flags & IEEE80211_BPF_RTS) 4767453645fSAndriy Voskoboinyk r12a_tx_protection(sc, txd, IEEE80211_PROT_RTSCTS, ridx); 4777453645fSAndriy Voskoboinyk if (params->ibp_flags & IEEE80211_BPF_CTS) 4787453645fSAndriy Voskoboinyk r12a_tx_protection(sc, txd, IEEE80211_PROT_CTSONLY, ridx); 4797453645fSAndriy Voskoboinyk 4807453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R12A_TXDW1_MACID, RTWN_MACID_BC)); 4817453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R12A_TXDW1_QSEL, R12A_TXDW1_QSEL_MGNT)); 4827453645fSAndriy Voskoboinyk 4837453645fSAndriy Voskoboinyk /* Set TX rate index. */ 4847453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE, ridx)); 4857453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE_FB_LMT, 0x1f)); 4867453645fSAndriy Voskoboinyk txd->txdw6 |= htole32(SM(R21A_TXDW6_MBSSID, uvp->id)); 4877453645fSAndriy Voskoboinyk txd->txdw3 |= htole32(R12A_TXDW3_DRVRATE); 4887453645fSAndriy Voskoboinyk r12a_tx_raid(sc, txd, ni, ismcast); 4897453645fSAndriy Voskoboinyk 4907453645fSAndriy Voskoboinyk if (!IEEE80211_QOS_HAS_SEQ(wh)) { 4917453645fSAndriy Voskoboinyk /* Use HW sequence numbering for non-QoS frames. */ 4927453645fSAndriy Voskoboinyk txd->txdw8 |= htole32(R12A_TXDW8_HWSEQ_EN); 4937453645fSAndriy Voskoboinyk txd->txdw3 |= htole32(SM(R12A_TXDW3_SEQ_SEL, uvp->id)); 4947453645fSAndriy Voskoboinyk } else { 4957453645fSAndriy Voskoboinyk /* Set sequence number. */ 4967453645fSAndriy Voskoboinyk txd->txdw9 |= htole32(SM(R12A_TXDW9_SEQ, 4977453645fSAndriy Voskoboinyk M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE)); 4987453645fSAndriy Voskoboinyk } 4997453645fSAndriy Voskoboinyk } 5007453645fSAndriy Voskoboinyk 5017453645fSAndriy Voskoboinyk void 5027453645fSAndriy Voskoboinyk r12a_fill_tx_desc_null(struct rtwn_softc *sc, void *buf, int is11b, int qos, 5037453645fSAndriy Voskoboinyk int id) 5047453645fSAndriy Voskoboinyk { 5057453645fSAndriy Voskoboinyk struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf; 5067453645fSAndriy Voskoboinyk 5077453645fSAndriy Voskoboinyk txd->flags0 = R12A_FLAGS0_FSG | R12A_FLAGS0_LSG | R12A_FLAGS0_OWN; 5087453645fSAndriy Voskoboinyk txd->txdw1 = htole32( 5097453645fSAndriy Voskoboinyk SM(R12A_TXDW1_QSEL, R12A_TXDW1_QSEL_MGNT)); 5107453645fSAndriy Voskoboinyk 5117453645fSAndriy Voskoboinyk txd->txdw3 = htole32(R12A_TXDW3_DRVRATE); 5127453645fSAndriy Voskoboinyk txd->txdw6 = htole32(SM(R21A_TXDW6_MBSSID, id)); 5137453645fSAndriy Voskoboinyk if (is11b) { 5147453645fSAndriy Voskoboinyk txd->txdw4 = htole32(SM(R12A_TXDW4_DATARATE, 5157453645fSAndriy Voskoboinyk RTWN_RIDX_CCK1)); 5167453645fSAndriy Voskoboinyk } else { 5177453645fSAndriy Voskoboinyk txd->txdw4 = htole32(SM(R12A_TXDW4_DATARATE, 5187453645fSAndriy Voskoboinyk RTWN_RIDX_OFDM6)); 5197453645fSAndriy Voskoboinyk } 5207453645fSAndriy Voskoboinyk 5217453645fSAndriy Voskoboinyk if (!qos) { 5227453645fSAndriy Voskoboinyk txd->txdw8 = htole32(R12A_TXDW8_HWSEQ_EN); 5237453645fSAndriy Voskoboinyk txd->txdw3 |= htole32(SM(R12A_TXDW3_SEQ_SEL, id)); 5247453645fSAndriy Voskoboinyk } 5257453645fSAndriy Voskoboinyk } 5267453645fSAndriy Voskoboinyk 5277453645fSAndriy Voskoboinyk uint8_t 5287453645fSAndriy Voskoboinyk r12a_tx_radiotap_flags(const void *buf) 5297453645fSAndriy Voskoboinyk { 5307453645fSAndriy Voskoboinyk const struct r12a_tx_desc *txd = buf; 5317453645fSAndriy Voskoboinyk uint8_t flags, rate; 5327453645fSAndriy Voskoboinyk 5337453645fSAndriy Voskoboinyk if (!(txd->txdw5 & htole32(R12A_TXDW5_DATA_SHORT))) 5347453645fSAndriy Voskoboinyk return (0); 5357453645fSAndriy Voskoboinyk 5367453645fSAndriy Voskoboinyk rate = MS(le32toh(txd->txdw4), R12A_TXDW4_DATARATE); 5377453645fSAndriy Voskoboinyk if (RTWN_RATE_IS_CCK(rate)) 5387453645fSAndriy Voskoboinyk flags = IEEE80211_RADIOTAP_F_SHORTPRE; 5397453645fSAndriy Voskoboinyk else 5407453645fSAndriy Voskoboinyk flags = IEEE80211_RADIOTAP_F_SHORTGI; 5417453645fSAndriy Voskoboinyk return (flags); 5427453645fSAndriy Voskoboinyk } 543