summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/rtwn/if_rtwn_rx.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/dev/rtwn/if_rtwn_rx.c')
-rw-r--r--freebsd/sys/dev/rtwn/if_rtwn_rx.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/freebsd/sys/dev/rtwn/if_rtwn_rx.c b/freebsd/sys/dev/rtwn/if_rtwn_rx.c
new file mode 100644
index 00000000..8d103dc7
--- /dev/null
+++ b/freebsd/sys/dev/rtwn/if_rtwn_rx.c
@@ -0,0 +1,466 @@
+#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_dl.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rtwn/if_rtwnreg.h>
+#include <dev/rtwn/if_rtwnvar.h>
+
+#include <dev/rtwn/if_rtwn_debug.h>
+#include <dev/rtwn/if_rtwn_ridx.h>
+#include <dev/rtwn/if_rtwn_rx.h>
+
+#include <dev/rtwn/rtl8192c/r92c_reg.h>
+#include <dev/rtwn/rtl8192c/r92c_rx_desc.h>
+
+
+void
+rtwn_get_rates(struct rtwn_softc *sc, const struct ieee80211_rateset *rs,
+ const struct ieee80211_htrateset *rs_ht, uint32_t *rates_p,
+ int *maxrate_p, int basic_rates)
+{
+ uint32_t rates;
+ uint8_t ridx;
+ int i, maxrate;
+
+ /* Get rates mask. */
+ rates = 0;
+ maxrate = 0;
+
+ /* This is for 11bg */
+ for (i = 0; i < rs->rs_nrates; i++) {
+ /* Convert 802.11 rate to HW rate index. */
+ ridx = rate2ridx(IEEE80211_RV(rs->rs_rates[i]));
+ if (ridx == RTWN_RIDX_UNKNOWN) /* Unknown rate, skip. */
+ continue;
+ if (((rs->rs_rates[i] & IEEE80211_RATE_BASIC) != 0) ||
+ !basic_rates) {
+ rates |= 1 << ridx;
+ if (ridx > maxrate)
+ maxrate = ridx;
+ }
+ }
+
+ /* If we're doing 11n, enable 11n rates */
+ if (rs_ht != NULL && !basic_rates) {
+ for (i = 0; i < rs_ht->rs_nrates; i++) {
+ if ((rs_ht->rs_rates[i] & 0x7f) > 0xf)
+ continue;
+ /* 11n rates start at index 12 */
+ ridx = RTWN_RIDX_MCS((rs_ht->rs_rates[i]) & 0xf);
+ rates |= (1 << ridx);
+
+ /* Guard against the rate table being oddly ordered */
+ if (ridx > maxrate)
+ maxrate = ridx;
+ }
+ }
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_RA,
+ "%s: rates 0x%08X, maxrate %d\n", __func__, rates, maxrate);
+
+ if (rates_p != NULL)
+ *rates_p = rates;
+ if (maxrate_p != NULL)
+ *maxrate_p = maxrate;
+}
+
+void
+rtwn_set_basicrates(struct rtwn_softc *sc, uint32_t rates)
+{
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_RA, "%s: rates 0x%08X\n", __func__, rates);
+
+ rtwn_setbits_4(sc, R92C_RRSR, R92C_RRSR_RATE_BITMAP_M, rates);
+}
+
+static void
+rtwn_update_avgrssi(struct rtwn_softc *sc, struct rtwn_node *un, int rate)
+{
+ int pwdb;
+
+ /* Convert antenna signal to percentage. */
+ if (un->last_rssi <= -100 || un->last_rssi >= 20)
+ pwdb = 0;
+ else if (un->last_rssi >= 0)
+ pwdb = 100;
+ else
+ pwdb = 100 + un->last_rssi;
+ if (RTWN_RATE_IS_CCK(rate)) {
+ /* CCK gain is smaller than OFDM/MCS gain. */
+ pwdb += 6;
+ if (pwdb > 100)
+ pwdb = 100;
+ if (pwdb <= 14)
+ pwdb -= 4;
+ else if (pwdb <= 26)
+ pwdb -= 8;
+ else if (pwdb <= 34)
+ pwdb -= 6;
+ else if (pwdb <= 42)
+ pwdb -= 2;
+ }
+
+ if (un->avg_pwdb == -1) /* Init. */
+ un->avg_pwdb = pwdb;
+ else if (un->avg_pwdb < pwdb)
+ un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20) + 1;
+ else
+ un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20);
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI,
+ "MACID %d, PWDB %d, EMA %d\n", un->id, pwdb, un->avg_pwdb);
+}
+
+static int8_t
+rtwn_get_rssi(struct rtwn_softc *sc, int rate, void *physt)
+{
+ int8_t rssi;
+
+ if (RTWN_RATE_IS_CCK(rate))
+ rssi = rtwn_get_rssi_cck(sc, physt);
+ else /* OFDM/HT. */
+ rssi = rtwn_get_rssi_ofdm(sc, physt);
+
+ return (rssi);
+}
+
+static uint32_t
+rtwn_get_tsf_low(struct rtwn_softc *sc, int id)
+{
+ return (rtwn_read_4(sc, R92C_TSFTR(id)));
+}
+
+static uint32_t
+rtwn_get_tsf_high(struct rtwn_softc *sc, int id)
+{
+ return (rtwn_read_4(sc, R92C_TSFTR(id) + 4));
+}
+
+static void
+rtwn_get_tsf(struct rtwn_softc *sc, uint64_t *buf, int id)
+{
+ /* NB: we cannot read it at once. */
+ *buf = rtwn_get_tsf_high(sc, id);
+ *buf <<= 32;
+ *buf += rtwn_get_tsf_low(sc, id);
+}
+
+struct ieee80211_node *
+rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc,
+ int8_t *rssi)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct ieee80211_frame_min *wh;
+ struct rtwn_node *un;
+ struct r92c_rx_stat *stat;
+ uint32_t rxdw0, rxdw3;
+ int cipher, infosz, pktlen, rate, shift;
+
+ stat = desc;
+ rxdw0 = le32toh(stat->rxdw0);
+ rxdw3 = le32toh(stat->rxdw3);
+
+ cipher = MS(rxdw0, R92C_RXDW0_CIPHER);
+ infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8;
+ pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN);
+ shift = MS(rxdw0, R92C_RXDW0_SHIFT);
+ rate = MS(rxdw3, R92C_RXDW3_RATE);
+
+ wh = (struct ieee80211_frame_min *)(mtodo(m, shift + infosz));
+ if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
+ cipher != R92C_CAM_ALGO_NONE)
+ m->m_flags |= M_WEP;
+
+ if (pktlen >= sizeof(*wh))
+ ni = ieee80211_find_rxnode(ic, wh);
+ else
+ ni = NULL;
+ un = RTWN_NODE(ni);
+
+ /* Get RSSI from PHY status descriptor if present. */
+ if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) {
+ *rssi = rtwn_get_rssi(sc, rate, mtod(m, void *));
+ RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, "%s: rssi %d, ridx %d\n",
+ __func__, *rssi, rate);
+
+ sc->last_rssi = *rssi;
+ if (un != NULL) {
+ un->last_rssi = *rssi;
+
+ /* Update our average RSSI. */
+ rtwn_update_avgrssi(sc, un, rate);
+ }
+ } else
+ *rssi = (un != NULL) ? un->last_rssi : sc->last_rssi;
+
+ if (ieee80211_radiotap_active(ic)) {
+ struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap;
+ int id = RTWN_VAP_ID_INVALID;
+
+ if (ni != NULL)
+ id = RTWN_VAP(ni->ni_vap)->id;
+ if (id == RTWN_VAP_ID_INVALID)
+ id = 0;
+
+ tap->wr_flags = rtwn_rx_radiotap_flags(sc, desc);
+ tap->wr_tsft = rtwn_get_tsf_high(sc, id);
+ if (le32toh(stat->tsf_low) > rtwn_get_tsf_low(sc, id))
+ tap->wr_tsft--;
+ tap->wr_tsft = (uint64_t)htole32(tap->wr_tsft) << 32;
+ tap->wr_tsft += stat->tsf_low;
+
+ /* XXX 20/40? */
+
+ /* Map HW rate index to 802.11 rate. */
+ if (rate < RTWN_RIDX_MCS(0))
+ tap->wr_rate = ridx2rate[rate];
+ else /* MCS0~15. */
+ tap->wr_rate = IEEE80211_RATE_MCS | (rate - 12);
+
+ tap->wr_dbm_antsignal = *rssi;
+ tap->wr_dbm_antnoise = RTWN_NOISE_FLOOR;
+ }
+
+ /* Drop PHY descriptor. */
+ m_adj(m, infosz + shift);
+
+ return (ni);
+}
+
+void
+rtwn_adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
+ const struct ieee80211_rx_stats *rxs,
+ int rssi, int nf)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct rtwn_softc *sc = vap->iv_ic->ic_softc;
+ struct rtwn_vap *uvp = RTWN_VAP(vap);
+ uint64_t ni_tstamp, curr_tstamp;
+
+ uvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);
+
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
+ subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
+ ni_tstamp = le64toh(ni->ni_tstamp.tsf);
+ RTWN_LOCK(sc);
+ rtwn_get_tsf(sc, &curr_tstamp, uvp->id);
+ RTWN_UNLOCK(sc);
+
+ if (ni_tstamp >= curr_tstamp)
+ (void) ieee80211_ibss_merge(ni);
+ }
+}
+
+static uint8_t
+rtwn_get_multi_pos(const uint8_t maddr[])
+{
+ uint64_t mask = 0x00004d101df481b4;
+ uint8_t pos = 0x27; /* initial value */
+ int i, j;
+
+ for (i = 0; i < IEEE80211_ADDR_LEN; i++)
+ for (j = (i == 0) ? 1 : 0; j < 8; j++)
+ if ((maddr[i] >> j) & 1)
+ pos ^= (mask >> (i * 8 + j - 1));
+
+ pos &= 0x3f;
+
+ return (pos);
+}
+
+void
+rtwn_set_multi(struct rtwn_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t mfilt[2];
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ /* general structure was copied from ath(4). */
+ if (ic->ic_allmulti == 0) {
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+
+ /*
+ * Merge multicast addresses to form the hardware filter.
+ */
+ mfilt[0] = mfilt[1] = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ ifp = vap->iv_ifp;
+ if_maddr_rlock(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ caddr_t dl;
+ uint8_t pos;
+
+ dl = LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr);
+ pos = rtwn_get_multi_pos(dl);
+
+ mfilt[pos / 32] |= (1 << (pos % 32));
+ }
+ if_maddr_runlock(ifp);
+ }
+ } else
+ mfilt[0] = mfilt[1] = ~0;
+
+
+ rtwn_write_4(sc, R92C_MAR + 0, mfilt[0]);
+ rtwn_write_4(sc, R92C_MAR + 4, mfilt[1]);
+
+ RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s: MC filter %08x:%08x\n",
+ __func__, mfilt[0], mfilt[1]);
+}
+
+static void
+rtwn_rxfilter_update_mgt(struct rtwn_softc *sc)
+{
+ uint16_t filter;
+
+ filter = 0x7f7f;
+ if (sc->bcn_vaps == 0) { /* STA and/or MONITOR mode vaps */
+ filter &= ~(
+ R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_REQ) |
+ R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_REQ) |
+ R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_PROBE_REQ));
+ }
+ if (sc->ap_vaps == sc->nvaps - sc->mon_vaps) { /* AP vaps only */
+ filter &= ~(
+ R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_RESP) |
+ R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_RESP));
+ }
+ rtwn_write_2(sc, R92C_RXFLTMAP0, filter);
+}
+
+void
+rtwn_rxfilter_update(struct rtwn_softc *sc)
+{
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ /* Filter for management frames. */
+ rtwn_rxfilter_update_mgt(sc);
+
+ /* Update Rx filter. */
+ rtwn_set_promisc(sc);
+}
+
+void
+rtwn_rxfilter_init(struct rtwn_softc *sc)
+{
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ /* Setup multicast filter. */
+ rtwn_set_multi(sc);
+
+ /* Reject all control frames. */
+ rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000);
+
+ /* Reject all data frames. */
+ rtwn_write_2(sc, R92C_RXFLTMAP2, 0x0000);
+
+ /* Append generic Rx filter bits. */
+ sc->rcr |= R92C_RCR_AM | R92C_RCR_AB | R92C_RCR_APM |
+ R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS |
+ R92C_RCR_APP_ICV | R92C_RCR_APP_MIC;
+
+ /* Update dynamic Rx filter parts. */
+ rtwn_rxfilter_update(sc);
+}
+
+void
+rtwn_rxfilter_set(struct rtwn_softc *sc)
+{
+ if (!(sc->sc_flags & RTWN_RCR_LOCKED))
+ rtwn_write_4(sc, R92C_RCR, sc->rcr);
+}
+
+void
+rtwn_set_rx_bssid_all(struct rtwn_softc *sc, int enable)
+{
+
+ if (enable)
+ sc->rcr &= ~R92C_RCR_CBSSID_BCN;
+ else
+ sc->rcr |= R92C_RCR_CBSSID_BCN;
+ rtwn_rxfilter_set(sc);
+}
+
+void
+rtwn_set_promisc(struct rtwn_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t mask_all, mask_min;
+
+ RTWN_ASSERT_LOCKED(sc);
+
+ mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP;
+ mask_min = R92C_RCR_APM;
+
+ if (sc->bcn_vaps == 0)
+ mask_min |= R92C_RCR_CBSSID_BCN;
+ if (sc->ap_vaps == 0)
+ mask_min |= R92C_RCR_CBSSID_DATA;
+
+ if (ic->ic_promisc == 0 && sc->mon_vaps == 0) {
+ if (sc->bcn_vaps != 0)
+ mask_all |= R92C_RCR_CBSSID_BCN;
+ if (sc->ap_vaps != 0) /* for Null data frames */
+ mask_all |= R92C_RCR_CBSSID_DATA;
+
+ sc->rcr &= ~mask_all;
+ sc->rcr |= mask_min;
+ } else {
+ sc->rcr &= ~mask_min;
+ sc->rcr |= mask_all;
+ }
+ rtwn_rxfilter_set(sc);
+}