17453645fSAndriy Voskoboinyk /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ 27453645fSAndriy Voskoboinyk 37453645fSAndriy Voskoboinyk /*- 47453645fSAndriy Voskoboinyk * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> 57453645fSAndriy Voskoboinyk * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org> 67453645fSAndriy Voskoboinyk * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org> 77453645fSAndriy Voskoboinyk * 87453645fSAndriy Voskoboinyk * Permission to use, copy, modify, and distribute this software for any 97453645fSAndriy Voskoboinyk * purpose with or without fee is hereby granted, provided that the above 107453645fSAndriy Voskoboinyk * copyright notice and this permission notice appear in all copies. 117453645fSAndriy Voskoboinyk * 127453645fSAndriy Voskoboinyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 137453645fSAndriy Voskoboinyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 147453645fSAndriy Voskoboinyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 157453645fSAndriy Voskoboinyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 167453645fSAndriy Voskoboinyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 177453645fSAndriy Voskoboinyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 187453645fSAndriy Voskoboinyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 197453645fSAndriy Voskoboinyk */ 207453645fSAndriy Voskoboinyk 217453645fSAndriy Voskoboinyk #include <sys/cdefs.h> 227453645fSAndriy Voskoboinyk #include "opt_wlan.h" 237453645fSAndriy Voskoboinyk 247453645fSAndriy Voskoboinyk #include <sys/param.h> 257453645fSAndriy Voskoboinyk #include <sys/lock.h> 267453645fSAndriy Voskoboinyk #include <sys/mutex.h> 277453645fSAndriy Voskoboinyk #include <sys/mbuf.h> 287453645fSAndriy Voskoboinyk #include <sys/kernel.h> 297453645fSAndriy Voskoboinyk #include <sys/socket.h> 307453645fSAndriy Voskoboinyk #include <sys/systm.h> 317453645fSAndriy Voskoboinyk #include <sys/malloc.h> 327453645fSAndriy Voskoboinyk #include <sys/queue.h> 337453645fSAndriy Voskoboinyk #include <sys/taskqueue.h> 347453645fSAndriy Voskoboinyk #include <sys/bus.h> 357453645fSAndriy Voskoboinyk #include <sys/endian.h> 367453645fSAndriy Voskoboinyk #include <sys/linker.h> 377453645fSAndriy Voskoboinyk 387453645fSAndriy Voskoboinyk #include <net/if.h> 397453645fSAndriy Voskoboinyk #include <net/ethernet.h> 407453645fSAndriy Voskoboinyk #include <net/if_media.h> 417453645fSAndriy Voskoboinyk 427453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h> 437453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h> 447453645fSAndriy Voskoboinyk 457453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h> 467453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h> 477453645fSAndriy Voskoboinyk 487453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_ridx.h> 497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_tx.h> 507453645fSAndriy Voskoboinyk 517453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c.h> 527453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_var.h> 537453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_tx_desc.h> 547453645fSAndriy Voskoboinyk 557453645fSAndriy Voskoboinyk static int 567453645fSAndriy Voskoboinyk r92c_tx_get_sco(struct rtwn_softc *sc, struct ieee80211_channel *c) 577453645fSAndriy Voskoboinyk { 587453645fSAndriy Voskoboinyk if (IEEE80211_IS_CHAN_HT40U(c)) 597453645fSAndriy Voskoboinyk return (R92C_TXDW4_SCO_SCA); 607453645fSAndriy Voskoboinyk else 617453645fSAndriy Voskoboinyk return (R92C_TXDW4_SCO_SCB); 627453645fSAndriy Voskoboinyk } 637453645fSAndriy Voskoboinyk 647453645fSAndriy Voskoboinyk static void 657453645fSAndriy Voskoboinyk r92c_tx_set_ht40(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) 667453645fSAndriy Voskoboinyk { 677453645fSAndriy Voskoboinyk struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; 687453645fSAndriy Voskoboinyk 6935135609SAdrian Chadd if (ieee80211_ht_check_tx_ht40(ni)) { 707453645fSAndriy Voskoboinyk int extc_offset; 717453645fSAndriy Voskoboinyk 727453645fSAndriy Voskoboinyk extc_offset = r92c_tx_get_sco(sc, ni->ni_chan); 737453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(R92C_TXDW4_DATA_BW40); 747453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R92C_TXDW4_DATA_SCO, extc_offset)); 757453645fSAndriy Voskoboinyk } 767453645fSAndriy Voskoboinyk } 777453645fSAndriy Voskoboinyk 787453645fSAndriy Voskoboinyk static void 797453645fSAndriy Voskoboinyk r92c_tx_protection(struct rtwn_softc *sc, struct r92c_tx_desc *txd, 80*ce7fca19SAdrian Chadd enum ieee80211_protmode mode, uint8_t ridx, bool force_rate) 817453645fSAndriy Voskoboinyk { 827453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 837453645fSAndriy Voskoboinyk uint8_t rate; 84*ce7fca19SAdrian Chadd bool use_fw_ratectl; 85*ce7fca19SAdrian Chadd 86*ce7fca19SAdrian Chadd use_fw_ratectl = 87*ce7fca19SAdrian Chadd (sc->sc_ratectl == RTWN_RATECTL_FW && !force_rate); 887453645fSAndriy Voskoboinyk 897453645fSAndriy Voskoboinyk switch (mode) { 907453645fSAndriy Voskoboinyk case IEEE80211_PROT_CTSONLY: 917453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF); 927453645fSAndriy Voskoboinyk break; 937453645fSAndriy Voskoboinyk case IEEE80211_PROT_RTSCTS: 947453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(R92C_TXDW4_RTSEN); 957453645fSAndriy Voskoboinyk break; 967453645fSAndriy Voskoboinyk default: 977453645fSAndriy Voskoboinyk break; 987453645fSAndriy Voskoboinyk } 997453645fSAndriy Voskoboinyk 1007453645fSAndriy Voskoboinyk if (mode == IEEE80211_PROT_CTSONLY || 1017453645fSAndriy Voskoboinyk mode == IEEE80211_PROT_RTSCTS) { 102*ce7fca19SAdrian Chadd if (use_fw_ratectl) { 103*ce7fca19SAdrian Chadd /* 104*ce7fca19SAdrian Chadd * If we're not forcing the driver rate then this 105*ce7fca19SAdrian Chadd * field actually doesn't matter; what matters is 106*ce7fca19SAdrian Chadd * the RRSR and INIRTS configuration. 107*ce7fca19SAdrian Chadd */ 108*ce7fca19SAdrian Chadd ridx = RTWN_RIDX_OFDM24; 109*ce7fca19SAdrian Chadd } else if (RTWN_RATE_IS_HT(ridx)) { 1107453645fSAndriy Voskoboinyk rate = rtwn_ctl_mcsrate(ic->ic_rt, ridx); 111*ce7fca19SAdrian Chadd ridx = rate2ridx(IEEE80211_RV(rate)); 112*ce7fca19SAdrian Chadd } else { 1137453645fSAndriy Voskoboinyk rate = ieee80211_ctl_rate(ic->ic_rt, ridx2rate[ridx]); 11487339626SAndriy Voskoboinyk ridx = rate2ridx(IEEE80211_RV(rate)); 115*ce7fca19SAdrian Chadd } 1167453645fSAndriy Voskoboinyk 1177453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, ridx)); 1187453645fSAndriy Voskoboinyk /* RTS rate fallback limit (max). */ 1197453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(SM(R92C_TXDW5_RTSRATE_FB_LMT, 0xf)); 1207453645fSAndriy Voskoboinyk 121*ce7fca19SAdrian Chadd if (!use_fw_ratectl && RTWN_RATE_IS_CCK(ridx) && 122*ce7fca19SAdrian Chadd ridx != RTWN_RIDX_CCK1 && 1237453645fSAndriy Voskoboinyk (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) 1247453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(R92C_TXDW4_RTS_SHORT); 1257453645fSAndriy Voskoboinyk } 1267453645fSAndriy Voskoboinyk } 1277453645fSAndriy Voskoboinyk 1287453645fSAndriy Voskoboinyk static void 1297453645fSAndriy Voskoboinyk r92c_tx_raid(struct rtwn_softc *sc, struct r92c_tx_desc *txd, 1307453645fSAndriy Voskoboinyk struct ieee80211_node *ni, int ismcast) 1317453645fSAndriy Voskoboinyk { 1327453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 1337453645fSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 1347453645fSAndriy Voskoboinyk struct ieee80211_channel *chan; 1357453645fSAndriy Voskoboinyk enum ieee80211_phymode mode; 1367453645fSAndriy Voskoboinyk uint8_t raid; 1377453645fSAndriy Voskoboinyk 1387453645fSAndriy Voskoboinyk chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? 1397453645fSAndriy Voskoboinyk ni->ni_chan : ic->ic_curchan; 1407453645fSAndriy Voskoboinyk mode = ieee80211_chan2mode(chan); 1417453645fSAndriy Voskoboinyk 1427453645fSAndriy Voskoboinyk /* NB: group addressed frames are done at 11bg rates for now */ 1437453645fSAndriy Voskoboinyk if (ismcast || !(ni->ni_flags & IEEE80211_NODE_HT)) { 1447453645fSAndriy Voskoboinyk switch (mode) { 1457453645fSAndriy Voskoboinyk case IEEE80211_MODE_11B: 1467453645fSAndriy Voskoboinyk case IEEE80211_MODE_11G: 1477453645fSAndriy Voskoboinyk break; 1487453645fSAndriy Voskoboinyk case IEEE80211_MODE_11NG: 1497453645fSAndriy Voskoboinyk mode = IEEE80211_MODE_11G; 1507453645fSAndriy Voskoboinyk break; 1517453645fSAndriy Voskoboinyk default: 1527453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "unknown mode(1) %d!\n", 1537453645fSAndriy Voskoboinyk ic->ic_curmode); 1547453645fSAndriy Voskoboinyk return; 1557453645fSAndriy Voskoboinyk } 1567453645fSAndriy Voskoboinyk } 1577453645fSAndriy Voskoboinyk 1587453645fSAndriy Voskoboinyk switch (mode) { 1597453645fSAndriy Voskoboinyk case IEEE80211_MODE_11B: 1607453645fSAndriy Voskoboinyk raid = R92C_RAID_11B; 1617453645fSAndriy Voskoboinyk break; 1627453645fSAndriy Voskoboinyk case IEEE80211_MODE_11G: 1637453645fSAndriy Voskoboinyk if (vap->iv_flags & IEEE80211_F_PUREG) 1647453645fSAndriy Voskoboinyk raid = R92C_RAID_11G; 1657453645fSAndriy Voskoboinyk else 1667453645fSAndriy Voskoboinyk raid = R92C_RAID_11BG; 1677453645fSAndriy Voskoboinyk break; 1687453645fSAndriy Voskoboinyk case IEEE80211_MODE_11NG: 1697453645fSAndriy Voskoboinyk if (vap->iv_flags_ht & IEEE80211_FHT_PUREN) 1707453645fSAndriy Voskoboinyk raid = R92C_RAID_11N; 1717453645fSAndriy Voskoboinyk else 1727453645fSAndriy Voskoboinyk raid = R92C_RAID_11BGN; 1737453645fSAndriy Voskoboinyk break; 1747453645fSAndriy Voskoboinyk default: 1757453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "unknown mode(2) %d!\n", mode); 1767453645fSAndriy Voskoboinyk return; 1777453645fSAndriy Voskoboinyk } 1787453645fSAndriy Voskoboinyk 1797453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R92C_TXDW1_RAID, raid)); 1807453645fSAndriy Voskoboinyk } 1817453645fSAndriy Voskoboinyk 1827453645fSAndriy Voskoboinyk /* XXX move to device-independent layer */ 1837453645fSAndriy Voskoboinyk static void 1847453645fSAndriy Voskoboinyk r92c_tx_set_sgi(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) 1857453645fSAndriy Voskoboinyk { 1867453645fSAndriy Voskoboinyk struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; 1877453645fSAndriy Voskoboinyk 1886749f059SAdrian Chadd /* 1896749f059SAdrian Chadd * Only enable short-GI if we're transmitting in that 1906749f059SAdrian Chadd * width to that node. 1916749f059SAdrian Chadd * 1926749f059SAdrian Chadd * Specifically, do not enable shortgi for 20MHz if 1936749f059SAdrian Chadd * we're attempting to transmit at 40MHz. 1946749f059SAdrian Chadd */ 1956749f059SAdrian Chadd if (ieee80211_ht_check_tx_ht40(ni)) { 1966749f059SAdrian Chadd if (ieee80211_ht_check_tx_shortgi_40(ni)) 1977453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(R92C_TXDW5_SGI); 1986749f059SAdrian Chadd } else if (ieee80211_ht_check_tx_ht(ni)) { 1996749f059SAdrian Chadd if (ieee80211_ht_check_tx_shortgi_20(ni)) 2007453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(R92C_TXDW5_SGI); 2017453645fSAndriy Voskoboinyk } 2026749f059SAdrian Chadd } 2037453645fSAndriy Voskoboinyk 2047453645fSAndriy Voskoboinyk void 2057453645fSAndriy Voskoboinyk r92c_tx_enable_ampdu(void *buf, int enable) 2067453645fSAndriy Voskoboinyk { 2077453645fSAndriy Voskoboinyk struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; 2087453645fSAndriy Voskoboinyk 2097453645fSAndriy Voskoboinyk if (enable) 2107453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(R92C_TXDW1_AGGEN); 2117453645fSAndriy Voskoboinyk else 2127453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(R92C_TXDW1_AGGBK); 2137453645fSAndriy Voskoboinyk } 2147453645fSAndriy Voskoboinyk 2157453645fSAndriy Voskoboinyk void 2167453645fSAndriy Voskoboinyk r92c_tx_setup_hwseq(void *buf) 2177453645fSAndriy Voskoboinyk { 2187453645fSAndriy Voskoboinyk struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; 2197453645fSAndriy Voskoboinyk 2207453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ_EN); 2217453645fSAndriy Voskoboinyk } 2227453645fSAndriy Voskoboinyk 2237453645fSAndriy Voskoboinyk void 2247453645fSAndriy Voskoboinyk r92c_tx_setup_macid(void *buf, int id) 2257453645fSAndriy Voskoboinyk { 2267453645fSAndriy Voskoboinyk struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; 2277453645fSAndriy Voskoboinyk 2287453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R92C_TXDW1_MACID, id)); 2297453645fSAndriy Voskoboinyk } 2307453645fSAndriy Voskoboinyk 23177e64f45SAdrian Chadd static int 23277e64f45SAdrian Chadd r92c_calculate_tx_agg_window(struct rtwn_softc *sc, 23377e64f45SAdrian Chadd const struct ieee80211_node *ni, int tid) 23477e64f45SAdrian Chadd { 23577e64f45SAdrian Chadd const struct ieee80211_tx_ampdu *tap; 23677e64f45SAdrian Chadd int wnd; 23777e64f45SAdrian Chadd 23877e64f45SAdrian Chadd tap = &ni->ni_tx_ampdu[tid]; 23977e64f45SAdrian Chadd 24077e64f45SAdrian Chadd /* 24177e64f45SAdrian Chadd * BAW is (MAX_AGG * 2) + 1, hence the /2 here. 24277e64f45SAdrian Chadd * Ensure we don't send 0 or more than 64. 24377e64f45SAdrian Chadd */ 24477e64f45SAdrian Chadd wnd = tap->txa_wnd / 2; 24577e64f45SAdrian Chadd if (wnd == 0) 24677e64f45SAdrian Chadd wnd = 1; 24777e64f45SAdrian Chadd else if (wnd > 0x1f) 24877e64f45SAdrian Chadd wnd = 0x1f; 24977e64f45SAdrian Chadd 25077e64f45SAdrian Chadd return (wnd); 25177e64f45SAdrian Chadd } 25277e64f45SAdrian Chadd 253af2e102cSAdrian Chadd /* 254af2e102cSAdrian Chadd * Check whether to enable the per-packet TX CCX report. 255af2e102cSAdrian Chadd * 256af2e102cSAdrian Chadd * For chipsets that do the RPT2 reports, enabling the TX 257af2e102cSAdrian Chadd * CCX report results in the packet not being counted in 258af2e102cSAdrian Chadd * the RPT2 counts. 259af2e102cSAdrian Chadd */ 260af2e102cSAdrian Chadd static bool 261af2e102cSAdrian Chadd r92c_check_enable_ccx_report(struct rtwn_softc *sc, int macid) 262af2e102cSAdrian Chadd { 263af2e102cSAdrian Chadd if (sc->sc_ratectl != RTWN_RATECTL_NET80211) 264af2e102cSAdrian Chadd return false; 265af2e102cSAdrian Chadd 266af2e102cSAdrian Chadd #ifndef RTWN_WITHOUT_UCODE 267af2e102cSAdrian Chadd if ((sc->macid_rpt2_max_num != 0) && 268af2e102cSAdrian Chadd (macid < sc->macid_rpt2_max_num)) 269af2e102cSAdrian Chadd return false; 270af2e102cSAdrian Chadd #endif 271af2e102cSAdrian Chadd return true; 272af2e102cSAdrian Chadd } 273af2e102cSAdrian Chadd 274ea347b7fSAdrian Chadd static void 275ea347b7fSAdrian Chadd r92c_fill_tx_desc_datarate(struct rtwn_softc *sc, struct r92c_tx_desc *txd, 276ea347b7fSAdrian Chadd uint8_t ridx, bool force_rate) 277ea347b7fSAdrian Chadd { 278ea347b7fSAdrian Chadd 279ea347b7fSAdrian Chadd /* Force this rate if needed. */ 280ea347b7fSAdrian Chadd if (sc->sc_ratectl == RTWN_RATECTL_FW && !force_rate) { 281ea347b7fSAdrian Chadd txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 0)); 282ea347b7fSAdrian Chadd } else { 283ea347b7fSAdrian Chadd txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, ridx)); 284ea347b7fSAdrian Chadd txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); 285ea347b7fSAdrian Chadd } 286ea347b7fSAdrian Chadd 287ea347b7fSAdrian Chadd /* Data rate fallback limit (max). */ 288ea347b7fSAdrian Chadd txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE_FB_LMT, 0x1f)); 289ea347b7fSAdrian Chadd } 290ea347b7fSAdrian Chadd 291ea347b7fSAdrian Chadd static void 292ea347b7fSAdrian Chadd r92c_fill_tx_desc_shpreamble(struct rtwn_softc *sc, struct r92c_tx_desc *txd, 293ea347b7fSAdrian Chadd uint8_t ridx, bool force_rate) 294ea347b7fSAdrian Chadd { 295ea347b7fSAdrian Chadd const struct ieee80211com *ic = &sc->sc_ic; 296ea347b7fSAdrian Chadd 297ea347b7fSAdrian Chadd if (RTWN_RATE_IS_CCK(ridx) && ridx != RTWN_RIDX_CCK1 && 298ea347b7fSAdrian Chadd (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) 299ea347b7fSAdrian Chadd txd->txdw4 |= htole32(R92C_TXDW4_DATA_SHPRE); 300ea347b7fSAdrian Chadd } 301ea347b7fSAdrian Chadd 302*ce7fca19SAdrian Chadd static enum ieee80211_protmode 303*ce7fca19SAdrian Chadd r92c_tx_get_protmode(struct rtwn_softc *sc, const struct ieee80211vap *vap, 304*ce7fca19SAdrian Chadd const struct ieee80211_node *ni, const struct mbuf *m, 305*ce7fca19SAdrian Chadd uint8_t ridx, bool force_rate) 306*ce7fca19SAdrian Chadd { 307*ce7fca19SAdrian Chadd const struct ieee80211com *ic = &sc->sc_ic; 308*ce7fca19SAdrian Chadd enum ieee80211_protmode prot; 309*ce7fca19SAdrian Chadd 310*ce7fca19SAdrian Chadd prot = IEEE80211_PROT_NONE; 311*ce7fca19SAdrian Chadd 312*ce7fca19SAdrian Chadd /* 313*ce7fca19SAdrian Chadd * If doing firmware rate control, base it the configured channel. 314*ce7fca19SAdrian Chadd * This ensures that for HT operation the RTS/CTS or CTS-to-self 315*ce7fca19SAdrian Chadd * configuration is obeyed. 316*ce7fca19SAdrian Chadd */ 317*ce7fca19SAdrian Chadd if (sc->sc_ratectl == RTWN_RATECTL_FW && !force_rate) { 318*ce7fca19SAdrian Chadd struct ieee80211_channel *chan; 319*ce7fca19SAdrian Chadd enum ieee80211_phymode mode; 320*ce7fca19SAdrian Chadd 321*ce7fca19SAdrian Chadd chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? 322*ce7fca19SAdrian Chadd ni->ni_chan : ic->ic_curchan; 323*ce7fca19SAdrian Chadd mode = ieee80211_chan2mode(chan); 324*ce7fca19SAdrian Chadd if (mode == IEEE80211_MODE_11NG) 325*ce7fca19SAdrian Chadd prot = ic->ic_htprotmode; 326*ce7fca19SAdrian Chadd else if (ic->ic_flags & IEEE80211_F_USEPROT) 327*ce7fca19SAdrian Chadd prot = ic->ic_protmode; 328*ce7fca19SAdrian Chadd } else { 329*ce7fca19SAdrian Chadd if (RTWN_RATE_IS_HT(ridx)) 330*ce7fca19SAdrian Chadd prot = ic->ic_htprotmode; 331*ce7fca19SAdrian Chadd else if (ic->ic_flags & IEEE80211_F_USEPROT) 332*ce7fca19SAdrian Chadd prot = ic->ic_protmode; 333*ce7fca19SAdrian Chadd } 334*ce7fca19SAdrian Chadd 335*ce7fca19SAdrian Chadd /* XXX fix last comparison for A-MSDU (in net80211) */ 336*ce7fca19SAdrian Chadd /* XXX A-MPDU? */ 337*ce7fca19SAdrian Chadd if (m->m_pkthdr.len + IEEE80211_CRC_LEN > 338*ce7fca19SAdrian Chadd vap->iv_rtsthreshold && 339*ce7fca19SAdrian Chadd vap->iv_rtsthreshold != IEEE80211_RTS_MAX) 340*ce7fca19SAdrian Chadd prot = IEEE80211_PROT_RTSCTS; 341*ce7fca19SAdrian Chadd 342*ce7fca19SAdrian Chadd return (prot); 343*ce7fca19SAdrian Chadd } 344*ce7fca19SAdrian Chadd 3457453645fSAndriy Voskoboinyk void 3467453645fSAndriy Voskoboinyk r92c_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, 34764ecfc27SAdrian Chadd struct mbuf *m, void *buf, uint8_t ridx, bool force_rate, int maxretry) 3487453645fSAndriy Voskoboinyk { 3497453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 3507453645fSAndriy Voskoboinyk struct r92c_softc *rs = sc->sc_priv; 3517453645fSAndriy Voskoboinyk #endif 3527453645fSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 3537453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 3547453645fSAndriy Voskoboinyk struct ieee80211_frame *wh; 3557453645fSAndriy Voskoboinyk struct r92c_tx_desc *txd; 3567453645fSAndriy Voskoboinyk enum ieee80211_protmode prot; 3577453645fSAndriy Voskoboinyk uint8_t type, tid, qos, qsel; 3587453645fSAndriy Voskoboinyk int hasqos, ismcast, macid; 3597453645fSAndriy Voskoboinyk 3607453645fSAndriy Voskoboinyk wh = mtod(m, struct ieee80211_frame *); 3617453645fSAndriy Voskoboinyk type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3627453645fSAndriy Voskoboinyk hasqos = IEEE80211_QOS_HAS_SEQ(wh); 3637453645fSAndriy Voskoboinyk ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 3647453645fSAndriy Voskoboinyk 3657453645fSAndriy Voskoboinyk /* Select TX ring for this frame. */ 3667453645fSAndriy Voskoboinyk if (hasqos) { 3677453645fSAndriy Voskoboinyk qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; 3687453645fSAndriy Voskoboinyk tid = qos & IEEE80211_QOS_TID; 3697453645fSAndriy Voskoboinyk } else { 3707453645fSAndriy Voskoboinyk qos = 0; 3717453645fSAndriy Voskoboinyk tid = 0; 3727453645fSAndriy Voskoboinyk } 3737453645fSAndriy Voskoboinyk 3747453645fSAndriy Voskoboinyk /* Fill Tx descriptor. */ 3757453645fSAndriy Voskoboinyk txd = (struct r92c_tx_desc *)buf; 3767453645fSAndriy Voskoboinyk txd->flags0 |= R92C_FLAGS0_LSG | R92C_FLAGS0_FSG; 3777453645fSAndriy Voskoboinyk if (ismcast) 3787453645fSAndriy Voskoboinyk txd->flags0 |= R92C_FLAGS0_BMCAST; 3797453645fSAndriy Voskoboinyk 3804fad98b5SAdrian Chadd if (IEEE80211_IS_QOSDATA(wh)) 3814fad98b5SAdrian Chadd txd->txdw4 |= htole32(R92C_TXDW4_QOS); 3824fad98b5SAdrian Chadd 3837453645fSAndriy Voskoboinyk if (!ismcast) { 3844fad98b5SAdrian Chadd struct rtwn_node *un = RTWN_NODE(ni); 3854fad98b5SAdrian Chadd macid = un->id; 3864fad98b5SAdrian Chadd 3877453645fSAndriy Voskoboinyk /* Unicast frame, check if an ACK is expected. */ 3887453645fSAndriy Voskoboinyk if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != 3897453645fSAndriy Voskoboinyk IEEE80211_QOS_ACKPOLICY_NOACK) { 3907453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(R92C_TXDW5_RTY_LMT_ENA); 3917453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(SM(R92C_TXDW5_RTY_LMT, 3927453645fSAndriy Voskoboinyk maxretry)); 3937453645fSAndriy Voskoboinyk } 3947453645fSAndriy Voskoboinyk 3957453645fSAndriy Voskoboinyk if (type == IEEE80211_FC0_TYPE_DATA) { 3967453645fSAndriy Voskoboinyk qsel = tid % RTWN_MAX_TID; 3977453645fSAndriy Voskoboinyk 3987453645fSAndriy Voskoboinyk rtwn_r92c_tx_enable_ampdu(sc, buf, 3997453645fSAndriy Voskoboinyk (m->m_flags & M_AMPDU_MPDU) != 0); 4007453645fSAndriy Voskoboinyk if (m->m_flags & M_AMPDU_MPDU) { 4017453645fSAndriy Voskoboinyk txd->txdw2 |= htole32(SM(R92C_TXDW2_AMPDU_DEN, 40277e64f45SAdrian Chadd ieee80211_ht_get_node_ampdu_density(ni))); 4037453645fSAndriy Voskoboinyk txd->txdw6 |= htole32(SM(R92C_TXDW6_MAX_AGG, 40477e64f45SAdrian Chadd r92c_calculate_tx_agg_window(sc, ni, tid))); 4057453645fSAndriy Voskoboinyk } 406af2e102cSAdrian Chadd if (r92c_check_enable_ccx_report(sc, macid)) { 4077453645fSAndriy Voskoboinyk txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT); 4087453645fSAndriy Voskoboinyk sc->sc_tx_n_active++; 4097453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 4107453645fSAndriy Voskoboinyk rs->rs_c2h_pending++; 4117453645fSAndriy Voskoboinyk #endif 4127453645fSAndriy Voskoboinyk } 4137453645fSAndriy Voskoboinyk 414ea347b7fSAdrian Chadd r92c_fill_tx_desc_shpreamble(sc, txd, ridx, force_rate); 4157453645fSAndriy Voskoboinyk 416*ce7fca19SAdrian Chadd prot = r92c_tx_get_protmode(sc, vap, ni, m, ridx, 417*ce7fca19SAdrian Chadd force_rate); 418*ce7fca19SAdrian Chadd 419*ce7fca19SAdrian Chadd /* 420*ce7fca19SAdrian Chadd * Note: Firmware rate control will enable short-GI 421*ce7fca19SAdrian Chadd * based on the configured rate mask, however HT40 422*ce7fca19SAdrian Chadd * may not be enabled. 423*ce7fca19SAdrian Chadd */ 424*ce7fca19SAdrian Chadd if (sc->sc_ratectl != RTWN_RATECTL_FW && 425*ce7fca19SAdrian Chadd RTWN_RATE_IS_HT(ridx)) { 4267453645fSAndriy Voskoboinyk r92c_tx_set_ht40(sc, txd, ni); 4277453645fSAndriy Voskoboinyk r92c_tx_set_sgi(sc, txd, ni); 428*ce7fca19SAdrian Chadd } 4297453645fSAndriy Voskoboinyk 4307453645fSAndriy Voskoboinyk /* NB: checks for ht40 / short bits (set above). */ 431*ce7fca19SAdrian Chadd r92c_tx_protection(sc, txd, prot, ridx, force_rate); 4327453645fSAndriy Voskoboinyk } else /* IEEE80211_FC0_TYPE_MGT */ 4337453645fSAndriy Voskoboinyk qsel = R92C_TXDW1_QSEL_MGNT; 4347453645fSAndriy Voskoboinyk } else { 4357453645fSAndriy Voskoboinyk macid = RTWN_MACID_BC; 4367453645fSAndriy Voskoboinyk qsel = R92C_TXDW1_QSEL_MGNT; 4377453645fSAndriy Voskoboinyk } 4387453645fSAndriy Voskoboinyk 4397453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R92C_TXDW1_QSEL, qsel)); 4407453645fSAndriy Voskoboinyk 4417453645fSAndriy Voskoboinyk rtwn_r92c_tx_setup_macid(sc, txd, macid); 442ea347b7fSAdrian Chadd 443ea347b7fSAdrian Chadd /* Fill in data rate, data retry */ 444ea347b7fSAdrian Chadd r92c_fill_tx_desc_datarate(sc, txd, ridx, force_rate); 445ea347b7fSAdrian Chadd 4467453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R92C_TXDW4_PORT_ID, uvp->id)); 4477453645fSAndriy Voskoboinyk r92c_tx_raid(sc, txd, ni, ismcast); 4487453645fSAndriy Voskoboinyk 4497453645fSAndriy Voskoboinyk if (!hasqos) { 4507453645fSAndriy Voskoboinyk /* Use HW sequence numbering for non-QoS frames. */ 4517453645fSAndriy Voskoboinyk rtwn_r92c_tx_setup_hwseq(sc, txd); 4527453645fSAndriy Voskoboinyk } else { 4537453645fSAndriy Voskoboinyk uint16_t seqno; 4547453645fSAndriy Voskoboinyk 4557453645fSAndriy Voskoboinyk if (m->m_flags & M_AMPDU_MPDU) { 4567544c1d2SAndriy Gapon seqno = ni->ni_txseqs[tid] % IEEE80211_SEQ_RANGE; 4577453645fSAndriy Voskoboinyk ni->ni_txseqs[tid]++; 4587453645fSAndriy Voskoboinyk } else 4597453645fSAndriy Voskoboinyk seqno = M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE; 4607453645fSAndriy Voskoboinyk 4617453645fSAndriy Voskoboinyk /* Set sequence number. */ 4627453645fSAndriy Voskoboinyk txd->txdseq = htole16(seqno); 4637453645fSAndriy Voskoboinyk } 4647453645fSAndriy Voskoboinyk } 4657453645fSAndriy Voskoboinyk 4667453645fSAndriy Voskoboinyk void 4677453645fSAndriy Voskoboinyk r92c_fill_tx_desc_raw(struct rtwn_softc *sc, struct ieee80211_node *ni, 4687453645fSAndriy Voskoboinyk struct mbuf *m, void *buf, const struct ieee80211_bpf_params *params) 4697453645fSAndriy Voskoboinyk { 4707453645fSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 4717453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 4727453645fSAndriy Voskoboinyk struct ieee80211_frame *wh; 4737453645fSAndriy Voskoboinyk struct r92c_tx_desc *txd; 4747453645fSAndriy Voskoboinyk uint8_t ridx; 4757453645fSAndriy Voskoboinyk int ismcast; 4767453645fSAndriy Voskoboinyk 4777453645fSAndriy Voskoboinyk /* XXX TODO: 11n checks, matching r92c_fill_tx_desc() */ 4787453645fSAndriy Voskoboinyk 4797453645fSAndriy Voskoboinyk wh = mtod(m, struct ieee80211_frame *); 4807453645fSAndriy Voskoboinyk ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 4817453645fSAndriy Voskoboinyk ridx = rate2ridx(params->ibp_rate0); 4827453645fSAndriy Voskoboinyk 4837453645fSAndriy Voskoboinyk /* Fill Tx descriptor. */ 4847453645fSAndriy Voskoboinyk txd = (struct r92c_tx_desc *)buf; 4857453645fSAndriy Voskoboinyk txd->flags0 |= R92C_FLAGS0_LSG | R92C_FLAGS0_FSG; 4867453645fSAndriy Voskoboinyk if (ismcast) 4877453645fSAndriy Voskoboinyk txd->flags0 |= R92C_FLAGS0_BMCAST; 4887453645fSAndriy Voskoboinyk 4897453645fSAndriy Voskoboinyk if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { 4907453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(R92C_TXDW5_RTY_LMT_ENA); 4917453645fSAndriy Voskoboinyk txd->txdw5 |= htole32(SM(R92C_TXDW5_RTY_LMT, 4927453645fSAndriy Voskoboinyk params->ibp_try0)); 4937453645fSAndriy Voskoboinyk } 4947453645fSAndriy Voskoboinyk if (params->ibp_flags & IEEE80211_BPF_RTS) 495*ce7fca19SAdrian Chadd r92c_tx_protection(sc, txd, IEEE80211_PROT_RTSCTS, ridx, 496*ce7fca19SAdrian Chadd true); 4977453645fSAndriy Voskoboinyk if (params->ibp_flags & IEEE80211_BPF_CTS) 498*ce7fca19SAdrian Chadd r92c_tx_protection(sc, txd, IEEE80211_PROT_CTSONLY, ridx, 499*ce7fca19SAdrian Chadd true); 5007453645fSAndriy Voskoboinyk 5017453645fSAndriy Voskoboinyk rtwn_r92c_tx_setup_macid(sc, txd, RTWN_MACID_BC); 5027453645fSAndriy Voskoboinyk txd->txdw1 |= htole32(SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT)); 5037453645fSAndriy Voskoboinyk 5047453645fSAndriy Voskoboinyk /* Set TX rate index. */ 505ea347b7fSAdrian Chadd r92c_fill_tx_desc_datarate(sc, txd, ridx, true); /* force rate */ 5067453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R92C_TXDW4_PORT_ID, uvp->id)); 5077453645fSAndriy Voskoboinyk r92c_tx_raid(sc, txd, ni, ismcast); 5087453645fSAndriy Voskoboinyk 5097453645fSAndriy Voskoboinyk if (!IEEE80211_QOS_HAS_SEQ(wh)) { 5107453645fSAndriy Voskoboinyk /* Use HW sequence numbering for non-QoS frames. */ 5117453645fSAndriy Voskoboinyk rtwn_r92c_tx_setup_hwseq(sc, txd); 5127453645fSAndriy Voskoboinyk } else { 5137453645fSAndriy Voskoboinyk /* Set sequence number. */ 5147453645fSAndriy Voskoboinyk txd->txdseq |= htole16(M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE); 5157453645fSAndriy Voskoboinyk } 5167453645fSAndriy Voskoboinyk } 5177453645fSAndriy Voskoboinyk 5187453645fSAndriy Voskoboinyk void 5197453645fSAndriy Voskoboinyk r92c_fill_tx_desc_null(struct rtwn_softc *sc, void *buf, int is11b, 5207453645fSAndriy Voskoboinyk int qos, int id) 5217453645fSAndriy Voskoboinyk { 5227453645fSAndriy Voskoboinyk struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; 5237453645fSAndriy Voskoboinyk 5247453645fSAndriy Voskoboinyk txd->flags0 = R92C_FLAGS0_FSG | R92C_FLAGS0_LSG | R92C_FLAGS0_OWN; 5257453645fSAndriy Voskoboinyk txd->txdw1 = htole32( 5267453645fSAndriy Voskoboinyk SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT)); 5277453645fSAndriy Voskoboinyk 5287453645fSAndriy Voskoboinyk txd->txdw4 = htole32(R92C_TXDW4_DRVRATE); 5297453645fSAndriy Voskoboinyk txd->txdw4 |= htole32(SM(R92C_TXDW4_PORT_ID, id)); 5307453645fSAndriy Voskoboinyk if (is11b) { 5317453645fSAndriy Voskoboinyk txd->txdw5 = htole32(SM(R92C_TXDW5_DATARATE, 5327453645fSAndriy Voskoboinyk RTWN_RIDX_CCK1)); 5337453645fSAndriy Voskoboinyk } else { 5347453645fSAndriy Voskoboinyk txd->txdw5 = htole32(SM(R92C_TXDW5_DATARATE, 5357453645fSAndriy Voskoboinyk RTWN_RIDX_OFDM6)); 5367453645fSAndriy Voskoboinyk } 5377453645fSAndriy Voskoboinyk 5387453645fSAndriy Voskoboinyk if (!qos) { 5397453645fSAndriy Voskoboinyk rtwn_r92c_tx_setup_hwseq(sc, txd); 5407453645fSAndriy Voskoboinyk } 5417453645fSAndriy Voskoboinyk } 5427453645fSAndriy Voskoboinyk 5437453645fSAndriy Voskoboinyk uint8_t 5447453645fSAndriy Voskoboinyk r92c_tx_radiotap_flags(const void *buf) 5457453645fSAndriy Voskoboinyk { 5467453645fSAndriy Voskoboinyk const struct r92c_tx_desc *txd = buf; 5477453645fSAndriy Voskoboinyk uint8_t flags; 5487453645fSAndriy Voskoboinyk 5497453645fSAndriy Voskoboinyk flags = 0; 5507453645fSAndriy Voskoboinyk if (txd->txdw4 & htole32(R92C_TXDW4_DATA_SHPRE)) 5517453645fSAndriy Voskoboinyk flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 5527453645fSAndriy Voskoboinyk if (txd->txdw5 & htole32(R92C_TXDW5_SGI)) 5537453645fSAndriy Voskoboinyk flags |= IEEE80211_RADIOTAP_F_SHORTGI; 5547453645fSAndriy Voskoboinyk return (flags); 5557453645fSAndriy Voskoboinyk } 556