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
r92c_tx_get_sco(struct rtwn_softc * sc,struct ieee80211_channel * c)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
r92c_tx_set_ht40(struct rtwn_softc * sc,void * buf,struct ieee80211_node * ni)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
r92c_tx_protection(struct rtwn_softc * sc,struct r92c_tx_desc * txd,enum ieee80211_protmode mode,uint8_t ridx,bool force_rate)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
r92c_tx_raid(struct rtwn_softc * sc,struct r92c_tx_desc * txd,struct ieee80211_node * ni,int ismcast)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
r92c_tx_set_sgi(struct rtwn_softc * sc,void * buf,struct ieee80211_node * ni)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
r92c_tx_enable_ampdu(void * buf,int enable)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
r92c_tx_setup_hwseq(void * buf)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
r92c_tx_setup_macid(void * buf,int id)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
r92c_calculate_tx_agg_window(struct rtwn_softc * sc,const struct ieee80211_node * ni,int tid)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
r92c_check_enable_ccx_report(struct rtwn_softc * sc,int macid)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
r92c_fill_tx_desc_datarate(struct rtwn_softc * sc,struct r92c_tx_desc * txd,uint8_t ridx,bool force_rate)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
r92c_fill_tx_desc_shpreamble(struct rtwn_softc * sc,struct r92c_tx_desc * txd,uint8_t ridx,bool force_rate)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
r92c_tx_get_protmode(struct rtwn_softc * sc,const struct ieee80211vap * vap,const struct ieee80211_node * ni,const struct mbuf * m,uint8_t ridx,bool force_rate)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
r92c_fill_tx_desc(struct rtwn_softc * sc,struct ieee80211_node * ni,struct mbuf * m,void * buf,uint8_t ridx,bool force_rate,int maxretry)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
r92c_fill_tx_desc_raw(struct rtwn_softc * sc,struct ieee80211_node * ni,struct mbuf * m,void * buf,const struct ieee80211_bpf_params * params)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
r92c_fill_tx_desc_null(struct rtwn_softc * sc,void * buf,int is11b,int qos,int id)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
r92c_tx_radiotap_flags(const void * buf)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