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