summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/rtwn/if_rtwn_tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/dev/rtwn/if_rtwn_tx.c')
-rw-r--r--freebsd/sys/dev/rtwn/if_rtwn_tx.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/freebsd/sys/dev/rtwn/if_rtwn_tx.c b/freebsd/sys/dev/rtwn/if_rtwn_tx.c
new file mode 100644
index 00000000..1ea9a766
--- /dev/null
+++ b/freebsd/sys/dev/rtwn/if_rtwn_tx.c
@@ -0,0 +1,348 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */
+
+/*-
+ * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
+ * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_wlan.h>
+
+#include <rtems/bsd/sys/param.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_ratectl.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/if_rtwn_beacon.h>
+#include <dev/rtwn/if_rtwn_debug.h>
+#include <dev/rtwn/if_rtwn_ridx.h>
+#include <dev/rtwn/if_rtwn_tx.h>
+
+
+void
+rtwn_drain_mbufq(struct rtwn_softc *sc)
+{
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ RTWN_ASSERT_LOCKED(sc);
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ }
+}
+
+#ifdef IEEE80211_SUPPORT_SUPERG
+void
+rtwn_ff_flush_all(struct rtwn_softc *sc, union sec_param *data)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ RTWN_UNLOCK(sc);
+ ieee80211_ff_flush_all(ic);
+ RTWN_LOCK(sc);
+}
+#endif
+
+static uint8_t
+rtwn_get_cipher(u_int ic_cipher)
+{
+ uint8_t cipher;
+
+ switch (ic_cipher) {
+ case IEEE80211_CIPHER_NONE:
+ cipher = RTWN_TXDW1_CIPHER_NONE;
+ break;
+ case IEEE80211_CIPHER_WEP:
+ case IEEE80211_CIPHER_TKIP:
+ cipher = RTWN_TXDW1_CIPHER_RC4;
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ cipher = RTWN_TXDW1_CIPHER_AES;
+ break;
+ default:
+ KASSERT(0, ("%s: unknown cipher %d\n", __func__,
+ ic_cipher));
+ return (RTWN_TXDW1_CIPHER_SM4);
+ }
+
+ return (cipher);
+}
+
+static int
+rtwn_tx_data(struct rtwn_softc *sc, struct ieee80211_node *ni,
+ struct mbuf *m)
+{
+ const struct ieee80211_txparam *tp;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_key *k = NULL;
+ struct ieee80211_channel *chan;
+ struct ieee80211_frame *wh;
+ struct rtwn_tx_desc_common *txd;
+ struct rtwn_tx_buf buf;
+ uint8_t rate, ridx, type;
+ u_int cipher;
+ int ismcast, maxretry;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ wh = mtod(m, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+
+ chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ?
+ ni->ni_chan : ic->ic_curchan;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(chan)];
+ maxretry = tp->maxretry;
+
+ /* Choose a TX rate index. */
+ if (type == IEEE80211_FC0_TYPE_MGT)
+ rate = tp->mgmtrate;
+ else if (ismcast)
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else if (m->m_flags & M_EAPOL)
+ rate = tp->mgmtrate;
+ else {
+ if (sc->sc_ratectl == RTWN_RATECTL_NET80211) {
+ /* XXX pass pktlen */
+ (void) ieee80211_ratectl_rate(ni, NULL, 0);
+ rate = ni->ni_txrate;
+ } else {
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ rate = IEEE80211_RATE_MCS | 0x4; /* MCS4 */
+ else if (ic->ic_curmode != IEEE80211_MODE_11B)
+ rate = ridx2rate[RTWN_RIDX_OFDM36];
+ else
+ rate = ridx2rate[RTWN_RIDX_CCK55];
+ }
+ }
+
+ ridx = rate2ridx(rate);
+
+ cipher = IEEE80211_CIPHER_NONE;
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ k = ieee80211_crypto_encap(ni, m);
+ if (k == NULL) {
+ device_printf(sc->sc_dev,
+ "ieee80211_crypto_encap returns NULL.\n");
+ return (ENOBUFS);
+ }
+ if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT))
+ cipher = k->wk_cipher->ic_cipher;
+
+ /* in case packet header moved, reset pointer */
+ wh = mtod(m, struct ieee80211_frame *);
+ }
+
+ /* Fill Tx descriptor. */
+ txd = (struct rtwn_tx_desc_common *)&buf;
+ memset(txd, 0, sc->txdesc_len);
+ txd->txdw1 = htole32(SM(RTWN_TXDW1_CIPHER, rtwn_get_cipher(cipher)));
+
+ rtwn_fill_tx_desc(sc, ni, m, txd, ridx, maxretry);
+
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct rtwn_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = rtwn_tx_radiotap_flags(sc, txd);
+ if (k != NULL)
+ tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+ ieee80211_radiotap_tx(vap, m);
+ }
+
+ return (rtwn_tx_start(sc, ni, m, (uint8_t *)txd, type, 0));
+}
+
+static int
+rtwn_tx_raw(struct rtwn_softc *sc, struct ieee80211_node *ni,
+ struct mbuf *m, const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_key *k = NULL;
+ struct ieee80211_frame *wh;
+ struct rtwn_tx_desc_common *txd;
+ struct rtwn_tx_buf buf;
+ uint8_t type;
+ u_int cipher;
+
+ /* Encrypt the frame if need be. */
+ cipher = IEEE80211_CIPHER_NONE;
+ if (params->ibp_flags & IEEE80211_BPF_CRYPTO) {
+ /* Retrieve key for TX. */
+ k = ieee80211_crypto_encap(ni, m);
+ if (k == NULL) {
+ device_printf(sc->sc_dev,
+ "ieee80211_crypto_encap returns NULL.\n");
+ return (ENOBUFS);
+ }
+ if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT))
+ cipher = k->wk_cipher->ic_cipher;
+ }
+
+ wh = mtod(m, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+ /* Fill Tx descriptor. */
+ txd = (struct rtwn_tx_desc_common *)&buf;
+ memset(txd, 0, sc->txdesc_len);
+ txd->txdw1 = htole32(SM(RTWN_TXDW1_CIPHER, rtwn_get_cipher(cipher)));
+
+ rtwn_fill_tx_desc_raw(sc, ni, m, txd, params);
+
+ if (ieee80211_radiotap_active_vap(vap)) {
+ struct rtwn_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = rtwn_tx_radiotap_flags(sc, txd);
+ if (k != NULL)
+ tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+ ieee80211_radiotap_tx(vap, m);
+ }
+
+ return (rtwn_tx_start(sc, ni, m, (uint8_t *)txd, type, 0));
+}
+
+int
+rtwn_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct rtwn_softc *sc = ic->ic_softc;
+ int error;
+
+ RTWN_LOCK(sc);
+ if ((sc->sc_flags & RTWN_RUNNING) == 0) {
+ RTWN_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ RTWN_UNLOCK(sc);
+ return (error);
+ }
+ rtwn_start(sc);
+ RTWN_UNLOCK(sc);
+
+ return (0);
+}
+
+void
+rtwn_start(struct rtwn_softc *sc)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ RTWN_ASSERT_LOCKED(sc);
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ if (sc->qfullmsk != 0) {
+ mbufq_prepend(&sc->sc_snd, m);
+ break;
+ }
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
+ "%s: called; m %p, ni %p\n", __func__, m, ni);
+
+ if (rtwn_tx_data(sc, ni, m) != 0) {
+ if_inc_counter(ni->ni_vap->iv_ifp,
+ IFCOUNTER_OERRORS, 1);
+ m_freem(m);
+#ifdef D4054
+ ieee80211_tx_watchdog_refresh(ni->ni_ic, -1, 0);
+#endif
+ ieee80211_free_node(ni);
+ break;
+ }
+ }
+}
+
+int
+rtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct rtwn_softc *sc = ic->ic_softc;
+ int error;
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT, "%s: called; m %p, ni %p\n",
+ __func__, m, ni);
+
+ /* prevent management frames from being sent if we're not ready */
+ RTWN_LOCK(sc);
+ if (!(sc->sc_flags & RTWN_RUNNING)) {
+ error = ENETDOWN;
+ goto end;
+ }
+
+ if (sc->qfullmsk != 0) {
+ error = ENOBUFS;
+ goto end;
+ }
+
+ if (params == NULL) {
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ */
+ error = rtwn_tx_data(sc, ni, m);
+ } else {
+ /*
+ * Caller supplied explicit parameters to use in
+ * sending the frame.
+ */
+ error = rtwn_tx_raw(sc, ni, m, params);
+ }
+
+end:
+ if (error != 0) {
+ if (m->m_flags & M_TXCB)
+ ieee80211_process_callback(ni, m, 1);
+ m_freem(m);
+ }
+
+ RTWN_UNLOCK(sc);
+
+ return (error);
+}