xref: /freebsd-src/sys/dev/rtwn/if_rtwn_tx.c (revision 64ecfc27dbd486ed764f9696b6e56f88e34ee8a1)
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 
377453645fSAndriy Voskoboinyk #include <net/if.h>
387453645fSAndriy Voskoboinyk #include <net/if_var.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 #include <net80211/ieee80211_ratectl.h>
457453645fSAndriy Voskoboinyk #ifdef	IEEE80211_SUPPORT_SUPERG
467453645fSAndriy Voskoboinyk #include <net80211/ieee80211_superg.h>
477453645fSAndriy Voskoboinyk #endif
487453645fSAndriy Voskoboinyk 
497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
507453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
517453645fSAndriy Voskoboinyk 
527453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_beacon.h>
537453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
547453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_ridx.h>
557453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_tx.h>
567453645fSAndriy Voskoboinyk 
577453645fSAndriy Voskoboinyk void
587453645fSAndriy Voskoboinyk rtwn_drain_mbufq(struct rtwn_softc *sc)
597453645fSAndriy Voskoboinyk {
607453645fSAndriy Voskoboinyk 	struct mbuf *m;
617453645fSAndriy Voskoboinyk 	struct ieee80211_node *ni;
627453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
637453645fSAndriy Voskoboinyk 	while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
647453645fSAndriy Voskoboinyk 		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
657453645fSAndriy Voskoboinyk 		m->m_pkthdr.rcvif = NULL;
667453645fSAndriy Voskoboinyk 		ieee80211_free_node(ni);
677453645fSAndriy Voskoboinyk 		m_freem(m);
687453645fSAndriy Voskoboinyk 	}
697453645fSAndriy Voskoboinyk }
707453645fSAndriy Voskoboinyk 
717453645fSAndriy Voskoboinyk #ifdef IEEE80211_SUPPORT_SUPERG
727453645fSAndriy Voskoboinyk void
737453645fSAndriy Voskoboinyk rtwn_ff_flush_all(struct rtwn_softc *sc, union sec_param *data)
747453645fSAndriy Voskoboinyk {
757453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
767453645fSAndriy Voskoboinyk 
777453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
787453645fSAndriy Voskoboinyk 	ieee80211_ff_flush_all(ic);
797453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
807453645fSAndriy Voskoboinyk }
817453645fSAndriy Voskoboinyk #endif
827453645fSAndriy Voskoboinyk 
837453645fSAndriy Voskoboinyk static uint8_t
847453645fSAndriy Voskoboinyk rtwn_get_cipher(u_int ic_cipher)
857453645fSAndriy Voskoboinyk {
867453645fSAndriy Voskoboinyk 	uint8_t cipher;
877453645fSAndriy Voskoboinyk 
887453645fSAndriy Voskoboinyk 	switch (ic_cipher) {
897453645fSAndriy Voskoboinyk 	case IEEE80211_CIPHER_NONE:
907453645fSAndriy Voskoboinyk 		cipher = RTWN_TXDW1_CIPHER_NONE;
917453645fSAndriy Voskoboinyk 		break;
927453645fSAndriy Voskoboinyk 	case IEEE80211_CIPHER_WEP:
937453645fSAndriy Voskoboinyk 	case IEEE80211_CIPHER_TKIP:
947453645fSAndriy Voskoboinyk 		cipher = RTWN_TXDW1_CIPHER_RC4;
957453645fSAndriy Voskoboinyk 		break;
967453645fSAndriy Voskoboinyk 	case IEEE80211_CIPHER_AES_CCM:
977453645fSAndriy Voskoboinyk 		cipher = RTWN_TXDW1_CIPHER_AES;
987453645fSAndriy Voskoboinyk 		break;
997453645fSAndriy Voskoboinyk 	default:
1007453645fSAndriy Voskoboinyk 		KASSERT(0, ("%s: unknown cipher %d\n", __func__,
1017453645fSAndriy Voskoboinyk 		    ic_cipher));
1027453645fSAndriy Voskoboinyk 		return (RTWN_TXDW1_CIPHER_SM4);
1037453645fSAndriy Voskoboinyk 	}
1047453645fSAndriy Voskoboinyk 
1057453645fSAndriy Voskoboinyk 	return (cipher);
1067453645fSAndriy Voskoboinyk }
1077453645fSAndriy Voskoboinyk 
1087453645fSAndriy Voskoboinyk static int
1097453645fSAndriy Voskoboinyk rtwn_tx_data(struct rtwn_softc *sc, struct ieee80211_node *ni,
1107453645fSAndriy Voskoboinyk     struct mbuf *m)
1117453645fSAndriy Voskoboinyk {
112f6313575SAndriy Voskoboinyk 	const struct ieee80211_txparam *tp = ni->ni_txparms;
1137453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
1147453645fSAndriy Voskoboinyk 	struct ieee80211vap *vap = ni->ni_vap;
1157453645fSAndriy Voskoboinyk 	struct ieee80211_key *k = NULL;
1167453645fSAndriy Voskoboinyk 	struct ieee80211_frame *wh;
1177453645fSAndriy Voskoboinyk 	struct rtwn_tx_desc_common *txd;
1187453645fSAndriy Voskoboinyk 	struct rtwn_tx_buf buf;
1197453645fSAndriy Voskoboinyk 	uint8_t rate, ridx, type;
120*64ecfc27SAdrian Chadd 	bool force_rate = false;
1217453645fSAndriy Voskoboinyk 	u_int cipher;
122f6313575SAndriy Voskoboinyk 	int ismcast;
1237453645fSAndriy Voskoboinyk 
1247453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
1257453645fSAndriy Voskoboinyk 
1267453645fSAndriy Voskoboinyk 	wh = mtod(m, struct ieee80211_frame *);
1277453645fSAndriy Voskoboinyk 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
1287453645fSAndriy Voskoboinyk 	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
1297453645fSAndriy Voskoboinyk 
1307453645fSAndriy Voskoboinyk 	/* Choose a TX rate index. */
131f6313575SAndriy Voskoboinyk 	if (type == IEEE80211_FC0_TYPE_MGT ||
132f6313575SAndriy Voskoboinyk 	    type == IEEE80211_FC0_TYPE_CTL ||
133*64ecfc27SAdrian Chadd 	    (m->m_flags & M_EAPOL) != 0) {
1347453645fSAndriy Voskoboinyk 		rate = tp->mgmtrate;
135*64ecfc27SAdrian Chadd 		force_rate = true;
136*64ecfc27SAdrian Chadd 	} else if (ismcast) {
137*64ecfc27SAdrian Chadd 		force_rate = true;
1387453645fSAndriy Voskoboinyk 		rate = tp->mcastrate;
139*64ecfc27SAdrian Chadd 	} else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
140*64ecfc27SAdrian Chadd 		force_rate = true;
1417453645fSAndriy Voskoboinyk 		rate = tp->ucastrate;
142*64ecfc27SAdrian Chadd 	} else {
1437453645fSAndriy Voskoboinyk 		if (sc->sc_ratectl == RTWN_RATECTL_NET80211) {
1447453645fSAndriy Voskoboinyk 			/* XXX pass pktlen */
1457453645fSAndriy Voskoboinyk 			(void) ieee80211_ratectl_rate(ni, NULL, 0);
1467453645fSAndriy Voskoboinyk 			rate = ni->ni_txrate;
1477453645fSAndriy Voskoboinyk 		} else {
1487453645fSAndriy Voskoboinyk 			if (ni->ni_flags & IEEE80211_NODE_HT)
1497453645fSAndriy Voskoboinyk 				rate = IEEE80211_RATE_MCS | 0x4; /* MCS4 */
1507453645fSAndriy Voskoboinyk 			else if (ic->ic_curmode != IEEE80211_MODE_11B)
1517453645fSAndriy Voskoboinyk 				rate = ridx2rate[RTWN_RIDX_OFDM36];
1527453645fSAndriy Voskoboinyk 			else
1537453645fSAndriy Voskoboinyk 				rate = ridx2rate[RTWN_RIDX_CCK55];
1547453645fSAndriy Voskoboinyk 		}
1557453645fSAndriy Voskoboinyk 	}
1567453645fSAndriy Voskoboinyk 
1577453645fSAndriy Voskoboinyk 	ridx = rate2ridx(rate);
1587453645fSAndriy Voskoboinyk 
1597453645fSAndriy Voskoboinyk 	cipher = IEEE80211_CIPHER_NONE;
1607453645fSAndriy Voskoboinyk 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
1617453645fSAndriy Voskoboinyk 		k = ieee80211_crypto_encap(ni, m);
1627453645fSAndriy Voskoboinyk 		if (k == NULL) {
1637453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
1647453645fSAndriy Voskoboinyk 			    "ieee80211_crypto_encap returns NULL.\n");
1657453645fSAndriy Voskoboinyk 			return (ENOBUFS);
1667453645fSAndriy Voskoboinyk 		}
1677453645fSAndriy Voskoboinyk 		if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT))
1687453645fSAndriy Voskoboinyk 			cipher = k->wk_cipher->ic_cipher;
1697453645fSAndriy Voskoboinyk 
1707453645fSAndriy Voskoboinyk 		/* in case packet header moved, reset pointer */
1717453645fSAndriy Voskoboinyk 		wh = mtod(m, struct ieee80211_frame *);
1727453645fSAndriy Voskoboinyk 	}
1737453645fSAndriy Voskoboinyk 
1747453645fSAndriy Voskoboinyk 	/* Fill Tx descriptor. */
1757453645fSAndriy Voskoboinyk 	txd = (struct rtwn_tx_desc_common *)&buf;
1767453645fSAndriy Voskoboinyk 	memset(txd, 0, sc->txdesc_len);
1777453645fSAndriy Voskoboinyk 	txd->txdw1 = htole32(SM(RTWN_TXDW1_CIPHER, rtwn_get_cipher(cipher)));
1787453645fSAndriy Voskoboinyk 
179*64ecfc27SAdrian Chadd 	rtwn_fill_tx_desc(sc, ni, m, txd, ridx, force_rate, tp->maxretry);
1807453645fSAndriy Voskoboinyk 
1817453645fSAndriy Voskoboinyk 	if (ieee80211_radiotap_active_vap(vap)) {
1827453645fSAndriy Voskoboinyk 		struct rtwn_tx_radiotap_header *tap = &sc->sc_txtap;
1837453645fSAndriy Voskoboinyk 
1847453645fSAndriy Voskoboinyk 		tap->wt_flags = rtwn_tx_radiotap_flags(sc, txd);
1857453645fSAndriy Voskoboinyk 		if (k != NULL)
1867453645fSAndriy Voskoboinyk 			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
1877453645fSAndriy Voskoboinyk 		ieee80211_radiotap_tx(vap, m);
1887453645fSAndriy Voskoboinyk 	}
1897453645fSAndriy Voskoboinyk 
1907453645fSAndriy Voskoboinyk 	return (rtwn_tx_start(sc, ni, m, (uint8_t *)txd, type, 0));
1917453645fSAndriy Voskoboinyk }
1927453645fSAndriy Voskoboinyk 
1937453645fSAndriy Voskoboinyk static int
1947453645fSAndriy Voskoboinyk rtwn_tx_raw(struct rtwn_softc *sc, struct ieee80211_node *ni,
1957453645fSAndriy Voskoboinyk     struct mbuf *m, const struct ieee80211_bpf_params *params)
1967453645fSAndriy Voskoboinyk {
1977453645fSAndriy Voskoboinyk 	struct ieee80211vap *vap = ni->ni_vap;
1987453645fSAndriy Voskoboinyk 	struct ieee80211_key *k = NULL;
1997453645fSAndriy Voskoboinyk 	struct ieee80211_frame *wh;
2007453645fSAndriy Voskoboinyk 	struct rtwn_tx_desc_common *txd;
2017453645fSAndriy Voskoboinyk 	struct rtwn_tx_buf buf;
2027453645fSAndriy Voskoboinyk 	uint8_t type;
2037453645fSAndriy Voskoboinyk 	u_int cipher;
2047453645fSAndriy Voskoboinyk 
2057453645fSAndriy Voskoboinyk 	/* Encrypt the frame if need be. */
2067453645fSAndriy Voskoboinyk 	cipher = IEEE80211_CIPHER_NONE;
2077453645fSAndriy Voskoboinyk 	if (params->ibp_flags & IEEE80211_BPF_CRYPTO) {
2087453645fSAndriy Voskoboinyk 		/* Retrieve key for TX. */
2097453645fSAndriy Voskoboinyk 		k = ieee80211_crypto_encap(ni, m);
2107453645fSAndriy Voskoboinyk 		if (k == NULL) {
2117453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
2127453645fSAndriy Voskoboinyk 			    "ieee80211_crypto_encap returns NULL.\n");
2137453645fSAndriy Voskoboinyk 			return (ENOBUFS);
2147453645fSAndriy Voskoboinyk 		}
2157453645fSAndriy Voskoboinyk 		if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT))
2167453645fSAndriy Voskoboinyk 			cipher = k->wk_cipher->ic_cipher;
2177453645fSAndriy Voskoboinyk 	}
2187453645fSAndriy Voskoboinyk 
2197453645fSAndriy Voskoboinyk 	wh = mtod(m, struct ieee80211_frame *);
2207453645fSAndriy Voskoboinyk 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
2217453645fSAndriy Voskoboinyk 
2227453645fSAndriy Voskoboinyk 	/* Fill Tx descriptor. */
2237453645fSAndriy Voskoboinyk 	txd = (struct rtwn_tx_desc_common *)&buf;
2247453645fSAndriy Voskoboinyk 	memset(txd, 0, sc->txdesc_len);
2257453645fSAndriy Voskoboinyk 	txd->txdw1 = htole32(SM(RTWN_TXDW1_CIPHER, rtwn_get_cipher(cipher)));
2267453645fSAndriy Voskoboinyk 
2277453645fSAndriy Voskoboinyk 	rtwn_fill_tx_desc_raw(sc, ni, m, txd, params);
2287453645fSAndriy Voskoboinyk 
2297453645fSAndriy Voskoboinyk 	if (ieee80211_radiotap_active_vap(vap)) {
2307453645fSAndriy Voskoboinyk 		struct rtwn_tx_radiotap_header *tap = &sc->sc_txtap;
2317453645fSAndriy Voskoboinyk 
2327453645fSAndriy Voskoboinyk 		tap->wt_flags = rtwn_tx_radiotap_flags(sc, txd);
2337453645fSAndriy Voskoboinyk 		if (k != NULL)
2347453645fSAndriy Voskoboinyk 			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
2357453645fSAndriy Voskoboinyk 		ieee80211_radiotap_tx(vap, m);
2367453645fSAndriy Voskoboinyk 	}
2377453645fSAndriy Voskoboinyk 
2387453645fSAndriy Voskoboinyk 	return (rtwn_tx_start(sc, ni, m, (uint8_t *)txd, type, 0));
2397453645fSAndriy Voskoboinyk }
2407453645fSAndriy Voskoboinyk 
2417453645fSAndriy Voskoboinyk int
2427453645fSAndriy Voskoboinyk rtwn_transmit(struct ieee80211com *ic, struct mbuf *m)
2437453645fSAndriy Voskoboinyk {
2447453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
2457453645fSAndriy Voskoboinyk 	int error;
2467453645fSAndriy Voskoboinyk 
2477453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
2487453645fSAndriy Voskoboinyk 	if ((sc->sc_flags & RTWN_RUNNING) == 0) {
2497453645fSAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
2507453645fSAndriy Voskoboinyk 		return (ENXIO);
2517453645fSAndriy Voskoboinyk 	}
2527453645fSAndriy Voskoboinyk 	error = mbufq_enqueue(&sc->sc_snd, m);
2537453645fSAndriy Voskoboinyk 	if (error) {
2547453645fSAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
2557453645fSAndriy Voskoboinyk 		return (error);
2567453645fSAndriy Voskoboinyk 	}
2577453645fSAndriy Voskoboinyk 	rtwn_start(sc);
2587453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
2597453645fSAndriy Voskoboinyk 
2607453645fSAndriy Voskoboinyk 	return (0);
2617453645fSAndriy Voskoboinyk }
2627453645fSAndriy Voskoboinyk 
2637453645fSAndriy Voskoboinyk void
2647453645fSAndriy Voskoboinyk rtwn_start(struct rtwn_softc *sc)
2657453645fSAndriy Voskoboinyk {
2667453645fSAndriy Voskoboinyk 	struct ieee80211_node *ni;
2677453645fSAndriy Voskoboinyk 	struct mbuf *m;
2687453645fSAndriy Voskoboinyk 
2697453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
2708838f3c3SAdrian Chadd 
2718838f3c3SAdrian Chadd 	/* Ensure no work is scheduled during reset/teardown */
2728838f3c3SAdrian Chadd 	if ((sc->sc_flags & RTWN_RUNNING) == 0)
2738838f3c3SAdrian Chadd 		return;
2748838f3c3SAdrian Chadd 
2757453645fSAndriy Voskoboinyk 	while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
2767453645fSAndriy Voskoboinyk 		if (sc->qfullmsk != 0) {
2777453645fSAndriy Voskoboinyk 			mbufq_prepend(&sc->sc_snd, m);
2787453645fSAndriy Voskoboinyk 			break;
2797453645fSAndriy Voskoboinyk 		}
2807453645fSAndriy Voskoboinyk 		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
2817453645fSAndriy Voskoboinyk 		m->m_pkthdr.rcvif = NULL;
2827453645fSAndriy Voskoboinyk 
2837453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
2847453645fSAndriy Voskoboinyk 		    "%s: called; m %p, ni %p\n", __func__, m, ni);
2857453645fSAndriy Voskoboinyk 
2867453645fSAndriy Voskoboinyk 		if (rtwn_tx_data(sc, ni, m) != 0) {
2877453645fSAndriy Voskoboinyk 			if_inc_counter(ni->ni_vap->iv_ifp,
2887453645fSAndriy Voskoboinyk 			    IFCOUNTER_OERRORS, 1);
2897453645fSAndriy Voskoboinyk 			m_freem(m);
2907453645fSAndriy Voskoboinyk #ifdef D4054
2917453645fSAndriy Voskoboinyk 			ieee80211_tx_watchdog_refresh(ni->ni_ic, -1, 0);
2927453645fSAndriy Voskoboinyk #endif
2937453645fSAndriy Voskoboinyk 			ieee80211_free_node(ni);
2947453645fSAndriy Voskoboinyk 			break;
2957453645fSAndriy Voskoboinyk 		}
2967453645fSAndriy Voskoboinyk 	}
2977453645fSAndriy Voskoboinyk }
2987453645fSAndriy Voskoboinyk 
2997453645fSAndriy Voskoboinyk int
3007453645fSAndriy Voskoboinyk rtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3017453645fSAndriy Voskoboinyk     const struct ieee80211_bpf_params *params)
3027453645fSAndriy Voskoboinyk {
3037453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = ni->ni_ic;
3047453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
3057453645fSAndriy Voskoboinyk 	int error;
3067453645fSAndriy Voskoboinyk 
3077453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT, "%s: called; m %p, ni %p\n",
3087453645fSAndriy Voskoboinyk 	    __func__, m, ni);
3097453645fSAndriy Voskoboinyk 
3107453645fSAndriy Voskoboinyk 	/* prevent management frames from being sent if we're not ready */
3117453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
3127453645fSAndriy Voskoboinyk 	if (!(sc->sc_flags & RTWN_RUNNING)) {
3137453645fSAndriy Voskoboinyk 		error = ENETDOWN;
3147453645fSAndriy Voskoboinyk 		goto end;
3157453645fSAndriy Voskoboinyk 	}
3167453645fSAndriy Voskoboinyk 
3177453645fSAndriy Voskoboinyk 	if (sc->qfullmsk != 0) {
3187453645fSAndriy Voskoboinyk 		error = ENOBUFS;
3197453645fSAndriy Voskoboinyk 		goto end;
3207453645fSAndriy Voskoboinyk 	}
3217453645fSAndriy Voskoboinyk 
3227453645fSAndriy Voskoboinyk 	if (params == NULL) {
3237453645fSAndriy Voskoboinyk 		/*
3247453645fSAndriy Voskoboinyk 		 * Legacy path; interpret frame contents to decide
3257453645fSAndriy Voskoboinyk 		 * precisely how to send the frame.
3267453645fSAndriy Voskoboinyk 		 */
3277453645fSAndriy Voskoboinyk 		error = rtwn_tx_data(sc, ni, m);
3287453645fSAndriy Voskoboinyk 	} else {
3297453645fSAndriy Voskoboinyk 		/*
3307453645fSAndriy Voskoboinyk 		 * Caller supplied explicit parameters to use in
3317453645fSAndriy Voskoboinyk 		 * sending the frame.
3327453645fSAndriy Voskoboinyk 		 */
3337453645fSAndriy Voskoboinyk 		error = rtwn_tx_raw(sc, ni, m, params);
3347453645fSAndriy Voskoboinyk 	}
3357453645fSAndriy Voskoboinyk 
3367453645fSAndriy Voskoboinyk end:
3377453645fSAndriy Voskoboinyk 	if (error != 0) {
3387453645fSAndriy Voskoboinyk 		if (m->m_flags & M_TXCB)
3397453645fSAndriy Voskoboinyk 			ieee80211_process_callback(ni, m, 1);
3407453645fSAndriy Voskoboinyk 		m_freem(m);
3417453645fSAndriy Voskoboinyk 	}
3427453645fSAndriy Voskoboinyk 
3437453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
3447453645fSAndriy Voskoboinyk 
3457453645fSAndriy Voskoboinyk 	return (error);
3467453645fSAndriy Voskoboinyk }
347