xref: /freebsd/sys/dev/rtwn/rtl8192c/r92c_tx.c (revision ce7fca19287cb218794da6fcbe320946485cd67a)
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