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