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