xref: /freebsd/sys/dev/rtwn/if_rtwn_rx.c (revision c15d869233cec56dfa0b54fd5287120f609d9577)
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_dl.h>
437453645fSAndriy Voskoboinyk #include <net/if_media.h>
447453645fSAndriy Voskoboinyk 
457453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
467453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h>
477453645fSAndriy Voskoboinyk 
487453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
507453645fSAndriy Voskoboinyk 
517453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
527453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_ridx.h>
537453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_rx.h>
547453645fSAndriy Voskoboinyk 
557453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_reg.h>
567453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_rx_desc.h>
577453645fSAndriy Voskoboinyk 
587453645fSAndriy Voskoboinyk 
597453645fSAndriy Voskoboinyk void
607453645fSAndriy Voskoboinyk rtwn_get_rates(struct rtwn_softc *sc, const struct ieee80211_rateset *rs,
617453645fSAndriy Voskoboinyk     const struct ieee80211_htrateset *rs_ht, uint32_t *rates_p,
627453645fSAndriy Voskoboinyk     int *maxrate_p, int basic_rates)
637453645fSAndriy Voskoboinyk {
647453645fSAndriy Voskoboinyk 	uint32_t rates;
657453645fSAndriy Voskoboinyk 	uint8_t ridx;
667453645fSAndriy Voskoboinyk 	int i, maxrate;
677453645fSAndriy Voskoboinyk 
687453645fSAndriy Voskoboinyk 	/* Get rates mask. */
697453645fSAndriy Voskoboinyk 	rates = 0;
707453645fSAndriy Voskoboinyk 	maxrate = 0;
717453645fSAndriy Voskoboinyk 
727453645fSAndriy Voskoboinyk 	/* This is for 11bg */
737453645fSAndriy Voskoboinyk 	for (i = 0; i < rs->rs_nrates; i++) {
747453645fSAndriy Voskoboinyk 		/* Convert 802.11 rate to HW rate index. */
757453645fSAndriy Voskoboinyk 		ridx = rate2ridx(IEEE80211_RV(rs->rs_rates[i]));
767453645fSAndriy Voskoboinyk 		if (ridx == RTWN_RIDX_UNKNOWN)	/* Unknown rate, skip. */
777453645fSAndriy Voskoboinyk 			continue;
787453645fSAndriy Voskoboinyk 		if (((rs->rs_rates[i] & IEEE80211_RATE_BASIC) != 0) ||
797453645fSAndriy Voskoboinyk 		    !basic_rates) {
807453645fSAndriy Voskoboinyk 			rates |= 1 << ridx;
817453645fSAndriy Voskoboinyk 			if (ridx > maxrate)
827453645fSAndriy Voskoboinyk 				maxrate = ridx;
837453645fSAndriy Voskoboinyk 		}
847453645fSAndriy Voskoboinyk 	}
857453645fSAndriy Voskoboinyk 
867453645fSAndriy Voskoboinyk 	/* If we're doing 11n, enable 11n rates */
877453645fSAndriy Voskoboinyk 	if (rs_ht != NULL && !basic_rates) {
887453645fSAndriy Voskoboinyk 		for (i = 0; i < rs_ht->rs_nrates; i++) {
897453645fSAndriy Voskoboinyk 			if ((rs_ht->rs_rates[i] & 0x7f) > 0xf)
907453645fSAndriy Voskoboinyk 				continue;
917453645fSAndriy Voskoboinyk 			/* 11n rates start at index 12 */
927453645fSAndriy Voskoboinyk 			ridx = RTWN_RIDX_MCS((rs_ht->rs_rates[i]) & 0xf);
937453645fSAndriy Voskoboinyk 			rates |= (1 << ridx);
947453645fSAndriy Voskoboinyk 
957453645fSAndriy Voskoboinyk 			/* Guard against the rate table being oddly ordered */
967453645fSAndriy Voskoboinyk 			if (ridx > maxrate)
977453645fSAndriy Voskoboinyk 				maxrate = ridx;
987453645fSAndriy Voskoboinyk 		}
997453645fSAndriy Voskoboinyk 	}
1007453645fSAndriy Voskoboinyk 
1017453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RA,
1027453645fSAndriy Voskoboinyk 	    "%s: rates 0x%08X, maxrate %d\n", __func__, rates, maxrate);
1037453645fSAndriy Voskoboinyk 
1047453645fSAndriy Voskoboinyk 	if (rates_p != NULL)
1057453645fSAndriy Voskoboinyk 		*rates_p = rates;
1067453645fSAndriy Voskoboinyk 	if (maxrate_p != NULL)
1077453645fSAndriy Voskoboinyk 		*maxrate_p = maxrate;
1087453645fSAndriy Voskoboinyk }
1097453645fSAndriy Voskoboinyk 
1107453645fSAndriy Voskoboinyk void
1117453645fSAndriy Voskoboinyk rtwn_set_basicrates(struct rtwn_softc *sc, uint32_t rates)
1127453645fSAndriy Voskoboinyk {
1137453645fSAndriy Voskoboinyk 
1147453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RA, "%s: rates 0x%08X\n", __func__, rates);
1157453645fSAndriy Voskoboinyk 
1167453645fSAndriy Voskoboinyk 	rtwn_setbits_4(sc, R92C_RRSR, R92C_RRSR_RATE_BITMAP_M, rates);
1177453645fSAndriy Voskoboinyk }
1187453645fSAndriy Voskoboinyk 
1197453645fSAndriy Voskoboinyk static void
1207453645fSAndriy Voskoboinyk rtwn_update_avgrssi(struct rtwn_softc *sc, struct rtwn_node *un, int rate)
1217453645fSAndriy Voskoboinyk {
1227453645fSAndriy Voskoboinyk 	int pwdb;
1237453645fSAndriy Voskoboinyk 
1247453645fSAndriy Voskoboinyk 	/* Convert antenna signal to percentage. */
1257453645fSAndriy Voskoboinyk 	if (un->last_rssi <= -100 || un->last_rssi >= 20)
1267453645fSAndriy Voskoboinyk 		pwdb = 0;
1277453645fSAndriy Voskoboinyk 	else if (un->last_rssi >= 0)
1287453645fSAndriy Voskoboinyk 		pwdb = 100;
1297453645fSAndriy Voskoboinyk 	else
1307453645fSAndriy Voskoboinyk 		pwdb = 100 + un->last_rssi;
1317453645fSAndriy Voskoboinyk 	if (RTWN_RATE_IS_CCK(rate)) {
1327453645fSAndriy Voskoboinyk 		/* CCK gain is smaller than OFDM/MCS gain. */
1337453645fSAndriy Voskoboinyk 		pwdb += 6;
1347453645fSAndriy Voskoboinyk 		if (pwdb > 100)
1357453645fSAndriy Voskoboinyk 			pwdb = 100;
1367453645fSAndriy Voskoboinyk 		if (pwdb <= 14)
1377453645fSAndriy Voskoboinyk 			pwdb -= 4;
1387453645fSAndriy Voskoboinyk 		else if (pwdb <= 26)
1397453645fSAndriy Voskoboinyk 			pwdb -= 8;
1407453645fSAndriy Voskoboinyk 		else if (pwdb <= 34)
1417453645fSAndriy Voskoboinyk 			pwdb -= 6;
1427453645fSAndriy Voskoboinyk 		else if (pwdb <= 42)
1437453645fSAndriy Voskoboinyk 			pwdb -= 2;
1447453645fSAndriy Voskoboinyk 	}
1457453645fSAndriy Voskoboinyk 
1467453645fSAndriy Voskoboinyk 	if (un->avg_pwdb == -1)	/* Init. */
1477453645fSAndriy Voskoboinyk 		un->avg_pwdb = pwdb;
1487453645fSAndriy Voskoboinyk 	else if (un->avg_pwdb < pwdb)
1497453645fSAndriy Voskoboinyk 		un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20) + 1;
1507453645fSAndriy Voskoboinyk 	else
1517453645fSAndriy Voskoboinyk 		un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20);
1527453645fSAndriy Voskoboinyk 
1537453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI,
1547453645fSAndriy Voskoboinyk 	    "MACID %d, PWDB %d, EMA %d\n", un->id, pwdb, un->avg_pwdb);
1557453645fSAndriy Voskoboinyk }
1567453645fSAndriy Voskoboinyk 
1577453645fSAndriy Voskoboinyk static int8_t
1587453645fSAndriy Voskoboinyk rtwn_get_rssi(struct rtwn_softc *sc, int rate, void *physt)
1597453645fSAndriy Voskoboinyk {
1607453645fSAndriy Voskoboinyk 	int8_t rssi;
1617453645fSAndriy Voskoboinyk 
1627453645fSAndriy Voskoboinyk 	if (RTWN_RATE_IS_CCK(rate))
1637453645fSAndriy Voskoboinyk 		rssi = rtwn_get_rssi_cck(sc, physt);
1647453645fSAndriy Voskoboinyk 	else	/* OFDM/HT. */
1657453645fSAndriy Voskoboinyk 		rssi = rtwn_get_rssi_ofdm(sc, physt);
1667453645fSAndriy Voskoboinyk 
1677453645fSAndriy Voskoboinyk 	return (rssi);
1687453645fSAndriy Voskoboinyk }
1697453645fSAndriy Voskoboinyk 
1707453645fSAndriy Voskoboinyk static uint32_t
1717453645fSAndriy Voskoboinyk rtwn_get_tsf_low(struct rtwn_softc *sc, int id)
1727453645fSAndriy Voskoboinyk {
1737453645fSAndriy Voskoboinyk 	return (rtwn_read_4(sc, R92C_TSFTR(id)));
1747453645fSAndriy Voskoboinyk }
1757453645fSAndriy Voskoboinyk 
1767453645fSAndriy Voskoboinyk static uint32_t
1777453645fSAndriy Voskoboinyk rtwn_get_tsf_high(struct rtwn_softc *sc, int id)
1787453645fSAndriy Voskoboinyk {
1797453645fSAndriy Voskoboinyk 	return (rtwn_read_4(sc, R92C_TSFTR(id) + 4));
1807453645fSAndriy Voskoboinyk }
1817453645fSAndriy Voskoboinyk 
1827453645fSAndriy Voskoboinyk static void
1837453645fSAndriy Voskoboinyk rtwn_get_tsf(struct rtwn_softc *sc, uint64_t *buf, int id)
1847453645fSAndriy Voskoboinyk {
1857453645fSAndriy Voskoboinyk 	/* NB: we cannot read it at once. */
1867453645fSAndriy Voskoboinyk 	*buf = rtwn_get_tsf_high(sc, id);
1877453645fSAndriy Voskoboinyk 	*buf <<= 32;
1887453645fSAndriy Voskoboinyk 	*buf += rtwn_get_tsf_low(sc, id);
1897453645fSAndriy Voskoboinyk }
1907453645fSAndriy Voskoboinyk 
1917453645fSAndriy Voskoboinyk struct ieee80211_node *
1927453645fSAndriy Voskoboinyk rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc,
1937453645fSAndriy Voskoboinyk     int8_t *rssi)
1947453645fSAndriy Voskoboinyk {
1957453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
1967453645fSAndriy Voskoboinyk 	struct ieee80211_node *ni;
1977453645fSAndriy Voskoboinyk 	struct ieee80211_frame_min *wh;
1987453645fSAndriy Voskoboinyk 	struct rtwn_node *un;
1997453645fSAndriy Voskoboinyk 	struct r92c_rx_stat *stat;
2007453645fSAndriy Voskoboinyk 	uint32_t rxdw0, rxdw3;
2017453645fSAndriy Voskoboinyk 	int cipher, infosz, pktlen, rate, shift;
2027453645fSAndriy Voskoboinyk 
2037453645fSAndriy Voskoboinyk 	stat = desc;
2047453645fSAndriy Voskoboinyk 	rxdw0 = le32toh(stat->rxdw0);
2057453645fSAndriy Voskoboinyk 	rxdw3 = le32toh(stat->rxdw3);
2067453645fSAndriy Voskoboinyk 
2077453645fSAndriy Voskoboinyk 	cipher = MS(rxdw0, R92C_RXDW0_CIPHER);
2087453645fSAndriy Voskoboinyk 	infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8;
2097453645fSAndriy Voskoboinyk 	pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN);
2107453645fSAndriy Voskoboinyk 	shift = MS(rxdw0, R92C_RXDW0_SHIFT);
2117453645fSAndriy Voskoboinyk 	rate = MS(rxdw3, R92C_RXDW3_RATE);
2127453645fSAndriy Voskoboinyk 
2137453645fSAndriy Voskoboinyk 	wh = (struct ieee80211_frame_min *)(mtodo(m, shift + infosz));
2147453645fSAndriy Voskoboinyk 	if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
2157453645fSAndriy Voskoboinyk 	    cipher != R92C_CAM_ALGO_NONE)
2167453645fSAndriy Voskoboinyk 		m->m_flags |= M_WEP;
2177453645fSAndriy Voskoboinyk 
2187453645fSAndriy Voskoboinyk 	if (pktlen >= sizeof(*wh))
2197453645fSAndriy Voskoboinyk 		ni = ieee80211_find_rxnode(ic, wh);
2207453645fSAndriy Voskoboinyk 	else
2217453645fSAndriy Voskoboinyk 		ni = NULL;
2227453645fSAndriy Voskoboinyk 	un = RTWN_NODE(ni);
2237453645fSAndriy Voskoboinyk 
2247453645fSAndriy Voskoboinyk 	/* Get RSSI from PHY status descriptor if present. */
2257453645fSAndriy Voskoboinyk 	if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) {
2267453645fSAndriy Voskoboinyk 		*rssi = rtwn_get_rssi(sc, rate, mtod(m, void *));
2277453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, "%s: rssi %d, ridx %d\n",
2287453645fSAndriy Voskoboinyk 		    __func__, *rssi, rate);
2297453645fSAndriy Voskoboinyk 
2307453645fSAndriy Voskoboinyk 		sc->last_rssi = *rssi;
2317453645fSAndriy Voskoboinyk 		if (un != NULL) {
2327453645fSAndriy Voskoboinyk 			un->last_rssi = *rssi;
2337453645fSAndriy Voskoboinyk 
2347453645fSAndriy Voskoboinyk 			/* Update our average RSSI. */
2357453645fSAndriy Voskoboinyk 			rtwn_update_avgrssi(sc, un, rate);
2367453645fSAndriy Voskoboinyk 		}
2377453645fSAndriy Voskoboinyk 	} else
2387453645fSAndriy Voskoboinyk 		*rssi = (un != NULL) ? un->last_rssi : sc->last_rssi;
2397453645fSAndriy Voskoboinyk 
2407453645fSAndriy Voskoboinyk 	if (ieee80211_radiotap_active(ic)) {
2417453645fSAndriy Voskoboinyk 		struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap;
2427453645fSAndriy Voskoboinyk 		int id = RTWN_VAP_ID_INVALID;
2437453645fSAndriy Voskoboinyk 
2447453645fSAndriy Voskoboinyk 		if (ni != NULL)
2457453645fSAndriy Voskoboinyk 			id = RTWN_VAP(ni->ni_vap)->id;
2467453645fSAndriy Voskoboinyk 		if (id == RTWN_VAP_ID_INVALID)
2477453645fSAndriy Voskoboinyk 			id = 0;
2487453645fSAndriy Voskoboinyk 
2497453645fSAndriy Voskoboinyk 		tap->wr_flags = rtwn_rx_radiotap_flags(sc, desc);
2507453645fSAndriy Voskoboinyk 		tap->wr_tsft = rtwn_get_tsf_high(sc, id);
2517453645fSAndriy Voskoboinyk 		if (le32toh(stat->tsf_low) > rtwn_get_tsf_low(sc, id))
2527453645fSAndriy Voskoboinyk 			tap->wr_tsft--;
2537453645fSAndriy Voskoboinyk 		tap->wr_tsft = (uint64_t)htole32(tap->wr_tsft) << 32;
2547453645fSAndriy Voskoboinyk 		tap->wr_tsft += stat->tsf_low;
2557453645fSAndriy Voskoboinyk 
2567453645fSAndriy Voskoboinyk 		/* XXX 20/40? */
2577453645fSAndriy Voskoboinyk 
2587453645fSAndriy Voskoboinyk 		/* Map HW rate index to 802.11 rate. */
2597453645fSAndriy Voskoboinyk 		if (rate < RTWN_RIDX_MCS(0))
2607453645fSAndriy Voskoboinyk 			tap->wr_rate = ridx2rate[rate];
2617453645fSAndriy Voskoboinyk 		else	/* MCS0~15. */
2627453645fSAndriy Voskoboinyk 			tap->wr_rate = IEEE80211_RATE_MCS | (rate - 12);
2637453645fSAndriy Voskoboinyk 
2647453645fSAndriy Voskoboinyk 		tap->wr_dbm_antsignal = *rssi;
2657453645fSAndriy Voskoboinyk 		tap->wr_dbm_antnoise = RTWN_NOISE_FLOOR;
2667453645fSAndriy Voskoboinyk 	}
2677453645fSAndriy Voskoboinyk 
2687453645fSAndriy Voskoboinyk 	/* Drop PHY descriptor. */
2697453645fSAndriy Voskoboinyk 	m_adj(m, infosz + shift);
2707453645fSAndriy Voskoboinyk 
2717453645fSAndriy Voskoboinyk 	return (ni);
2727453645fSAndriy Voskoboinyk }
2737453645fSAndriy Voskoboinyk 
2747453645fSAndriy Voskoboinyk void
2757453645fSAndriy Voskoboinyk rtwn_adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
2767453645fSAndriy Voskoboinyk     const struct ieee80211_rx_stats *rxs,
2777453645fSAndriy Voskoboinyk     int rssi, int nf)
2787453645fSAndriy Voskoboinyk {
2797453645fSAndriy Voskoboinyk 	struct ieee80211vap *vap = ni->ni_vap;
2807453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = vap->iv_ic->ic_softc;
2817453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
2827453645fSAndriy Voskoboinyk 	uint64_t ni_tstamp, curr_tstamp;
2837453645fSAndriy Voskoboinyk 
2847453645fSAndriy Voskoboinyk 	uvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);
2857453645fSAndriy Voskoboinyk 
2867453645fSAndriy Voskoboinyk 	if (vap->iv_state == IEEE80211_S_RUN &&
2877453645fSAndriy Voskoboinyk 	    (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
2887453645fSAndriy Voskoboinyk 	    subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
2897453645fSAndriy Voskoboinyk 		ni_tstamp = le64toh(ni->ni_tstamp.tsf);
2907453645fSAndriy Voskoboinyk 		RTWN_LOCK(sc);
2917453645fSAndriy Voskoboinyk 		rtwn_get_tsf(sc, &curr_tstamp, uvp->id);
2927453645fSAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
2937453645fSAndriy Voskoboinyk 
2947453645fSAndriy Voskoboinyk 		if (ni_tstamp >= curr_tstamp)
2957453645fSAndriy Voskoboinyk 			(void) ieee80211_ibss_merge(ni);
2967453645fSAndriy Voskoboinyk 	}
2977453645fSAndriy Voskoboinyk }
2987453645fSAndriy Voskoboinyk 
2997453645fSAndriy Voskoboinyk static uint8_t
3007453645fSAndriy Voskoboinyk rtwn_get_multi_pos(const uint8_t maddr[])
3017453645fSAndriy Voskoboinyk {
3027453645fSAndriy Voskoboinyk 	uint64_t mask = 0x00004d101df481b4;
3037453645fSAndriy Voskoboinyk 	uint8_t pos = 0x27;	/* initial value */
3047453645fSAndriy Voskoboinyk 	int i, j;
3057453645fSAndriy Voskoboinyk 
3067453645fSAndriy Voskoboinyk 	for (i = 0; i < IEEE80211_ADDR_LEN; i++)
3077453645fSAndriy Voskoboinyk 		for (j = (i == 0) ? 1 : 0; j < 8; j++)
3087453645fSAndriy Voskoboinyk 			if ((maddr[i] >> j) & 1)
3097453645fSAndriy Voskoboinyk 				pos ^= (mask >> (i * 8 + j - 1));
3107453645fSAndriy Voskoboinyk 
3117453645fSAndriy Voskoboinyk 	pos &= 0x3f;
3127453645fSAndriy Voskoboinyk 
3137453645fSAndriy Voskoboinyk 	return (pos);
3147453645fSAndriy Voskoboinyk }
3157453645fSAndriy Voskoboinyk 
3167453645fSAndriy Voskoboinyk void
3177453645fSAndriy Voskoboinyk rtwn_set_multi(struct rtwn_softc *sc)
3187453645fSAndriy Voskoboinyk {
3197453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
3207453645fSAndriy Voskoboinyk 	uint32_t mfilt[2];
3217453645fSAndriy Voskoboinyk 
3227453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
3237453645fSAndriy Voskoboinyk 
3247453645fSAndriy Voskoboinyk 	/* general structure was copied from ath(4). */
3257453645fSAndriy Voskoboinyk 	if (ic->ic_allmulti == 0) {
3267453645fSAndriy Voskoboinyk 		struct ieee80211vap *vap;
3277453645fSAndriy Voskoboinyk 		struct ifnet *ifp;
3287453645fSAndriy Voskoboinyk 		struct ifmultiaddr *ifma;
3297453645fSAndriy Voskoboinyk 
3307453645fSAndriy Voskoboinyk 		/*
3317453645fSAndriy Voskoboinyk 		 * Merge multicast addresses to form the hardware filter.
3327453645fSAndriy Voskoboinyk 		 */
3337453645fSAndriy Voskoboinyk 		mfilt[0] = mfilt[1] = 0;
3347453645fSAndriy Voskoboinyk 		TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
3357453645fSAndriy Voskoboinyk 			ifp = vap->iv_ifp;
3367453645fSAndriy Voskoboinyk 			if_maddr_rlock(ifp);
3377453645fSAndriy Voskoboinyk 			TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
3387453645fSAndriy Voskoboinyk 				caddr_t dl;
3397453645fSAndriy Voskoboinyk 				uint8_t pos;
3407453645fSAndriy Voskoboinyk 
3417453645fSAndriy Voskoboinyk 				dl = LLADDR((struct sockaddr_dl *)
3427453645fSAndriy Voskoboinyk 				    ifma->ifma_addr);
3437453645fSAndriy Voskoboinyk 				pos = rtwn_get_multi_pos(dl);
3447453645fSAndriy Voskoboinyk 
3457453645fSAndriy Voskoboinyk 				mfilt[pos / 32] |= (1 << (pos % 32));
3467453645fSAndriy Voskoboinyk 			}
3477453645fSAndriy Voskoboinyk 			if_maddr_runlock(ifp);
3487453645fSAndriy Voskoboinyk 		}
3497453645fSAndriy Voskoboinyk 	} else
3507453645fSAndriy Voskoboinyk 		mfilt[0] = mfilt[1] = ~0;
3517453645fSAndriy Voskoboinyk 
3527453645fSAndriy Voskoboinyk 
3537453645fSAndriy Voskoboinyk 	rtwn_write_4(sc, R92C_MAR + 0, mfilt[0]);
3547453645fSAndriy Voskoboinyk 	rtwn_write_4(sc, R92C_MAR + 4, mfilt[1]);
3557453645fSAndriy Voskoboinyk 
3567453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s: MC filter %08x:%08x\n",
3577453645fSAndriy Voskoboinyk 	    __func__, mfilt[0], mfilt[1]);
3587453645fSAndriy Voskoboinyk }
3597453645fSAndriy Voskoboinyk 
3607453645fSAndriy Voskoboinyk static void
3617453645fSAndriy Voskoboinyk rtwn_rxfilter_update_mgt(struct rtwn_softc *sc)
3627453645fSAndriy Voskoboinyk {
3637453645fSAndriy Voskoboinyk 	uint16_t filter;
3647453645fSAndriy Voskoboinyk 
365*c15d8692SAndriy Voskoboinyk 	filter = 0x7f7f;
3667453645fSAndriy Voskoboinyk 	if (sc->bcn_vaps == 0) {	/* STA and/or MONITOR mode vaps */
3677453645fSAndriy Voskoboinyk 		filter &= ~(
3687453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_REQ) |
3697453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_REQ) |
3707453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_PROBE_REQ));
3717453645fSAndriy Voskoboinyk 	}
3727453645fSAndriy Voskoboinyk 	if (sc->ap_vaps == sc->nvaps - sc->mon_vaps) {	/* AP vaps only */
3737453645fSAndriy Voskoboinyk 		filter &= ~(
3747453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_RESP) |
3757453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_RESP));
3767453645fSAndriy Voskoboinyk 	}
3777453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_RXFLTMAP0, filter);
3787453645fSAndriy Voskoboinyk }
3797453645fSAndriy Voskoboinyk 
3807453645fSAndriy Voskoboinyk void
3817453645fSAndriy Voskoboinyk rtwn_rxfilter_update(struct rtwn_softc *sc)
3827453645fSAndriy Voskoboinyk {
3837453645fSAndriy Voskoboinyk 
3847453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
3857453645fSAndriy Voskoboinyk 
3867453645fSAndriy Voskoboinyk 	/* Filter for management frames. */
3877453645fSAndriy Voskoboinyk 	rtwn_rxfilter_update_mgt(sc);
3887453645fSAndriy Voskoboinyk 
3897453645fSAndriy Voskoboinyk 	/* Update Rx filter. */
3907453645fSAndriy Voskoboinyk 	rtwn_set_promisc(sc);
3917453645fSAndriy Voskoboinyk }
3927453645fSAndriy Voskoboinyk 
3937453645fSAndriy Voskoboinyk void
3947453645fSAndriy Voskoboinyk rtwn_rxfilter_init(struct rtwn_softc *sc)
3957453645fSAndriy Voskoboinyk {
3967453645fSAndriy Voskoboinyk 
3977453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
3987453645fSAndriy Voskoboinyk 
3997453645fSAndriy Voskoboinyk 	/* Setup multicast filter. */
4007453645fSAndriy Voskoboinyk 	rtwn_set_multi(sc);
4017453645fSAndriy Voskoboinyk 
4027453645fSAndriy Voskoboinyk 	/* Reject all control frames. */
4037453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000);
4047453645fSAndriy Voskoboinyk 
4057453645fSAndriy Voskoboinyk 	/* Reject all data frames. */
4067453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_RXFLTMAP2, 0x0000);
4077453645fSAndriy Voskoboinyk 
408*c15d8692SAndriy Voskoboinyk 	/* Append generic Rx filter bits. */
409*c15d8692SAndriy Voskoboinyk 	sc->rcr |= R92C_RCR_AM | R92C_RCR_AB | R92C_RCR_APM |
4107453645fSAndriy Voskoboinyk 	    R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS |
4117453645fSAndriy Voskoboinyk 	    R92C_RCR_APP_ICV | R92C_RCR_APP_MIC;
4127453645fSAndriy Voskoboinyk 
4137453645fSAndriy Voskoboinyk 	/* Update dynamic Rx filter parts. */
4147453645fSAndriy Voskoboinyk 	rtwn_rxfilter_update(sc);
4157453645fSAndriy Voskoboinyk }
4167453645fSAndriy Voskoboinyk 
4177453645fSAndriy Voskoboinyk void
418*c15d8692SAndriy Voskoboinyk rtwn_rxfilter_set(struct rtwn_softc *sc)
419*c15d8692SAndriy Voskoboinyk {
420*c15d8692SAndriy Voskoboinyk 	if (!(sc->sc_flags & RTWN_RCR_LOCKED))
421*c15d8692SAndriy Voskoboinyk 		rtwn_write_4(sc, R92C_RCR, sc->rcr);
422*c15d8692SAndriy Voskoboinyk }
423*c15d8692SAndriy Voskoboinyk 
424*c15d8692SAndriy Voskoboinyk void
4257453645fSAndriy Voskoboinyk rtwn_set_rx_bssid_all(struct rtwn_softc *sc, int enable)
4267453645fSAndriy Voskoboinyk {
427*c15d8692SAndriy Voskoboinyk 
4287453645fSAndriy Voskoboinyk 	if (enable)
429*c15d8692SAndriy Voskoboinyk 		sc->rcr &= ~R92C_RCR_CBSSID_BCN;
4307453645fSAndriy Voskoboinyk 	else
431*c15d8692SAndriy Voskoboinyk 		sc->rcr |= R92C_RCR_CBSSID_BCN;
432*c15d8692SAndriy Voskoboinyk 	rtwn_rxfilter_set(sc);
4337453645fSAndriy Voskoboinyk }
4347453645fSAndriy Voskoboinyk 
4357453645fSAndriy Voskoboinyk void
4367453645fSAndriy Voskoboinyk rtwn_set_promisc(struct rtwn_softc *sc)
4377453645fSAndriy Voskoboinyk {
4387453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
439*c15d8692SAndriy Voskoboinyk 	uint32_t mask_all, mask_min;
4407453645fSAndriy Voskoboinyk 
4417453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
4427453645fSAndriy Voskoboinyk 
443*c15d8692SAndriy Voskoboinyk 	mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP;
444*c15d8692SAndriy Voskoboinyk 	mask_min = R92C_RCR_APM;
4457453645fSAndriy Voskoboinyk 
4467453645fSAndriy Voskoboinyk 	if (sc->bcn_vaps == 0)
447*c15d8692SAndriy Voskoboinyk 		mask_min |= R92C_RCR_CBSSID_BCN;
4487453645fSAndriy Voskoboinyk 	if (sc->ap_vaps == 0)
449*c15d8692SAndriy Voskoboinyk 		mask_min |= R92C_RCR_CBSSID_DATA;
4507453645fSAndriy Voskoboinyk 
451*c15d8692SAndriy Voskoboinyk 	if (ic->ic_promisc == 0 && sc->mon_vaps == 0) {
452*c15d8692SAndriy Voskoboinyk 		if (sc->bcn_vaps != 0)
453*c15d8692SAndriy Voskoboinyk 			mask_all |= R92C_RCR_CBSSID_BCN;
454*c15d8692SAndriy Voskoboinyk 		if (sc->ap_vaps != 0)	/* for Null data frames */
455*c15d8692SAndriy Voskoboinyk 			mask_all |= R92C_RCR_CBSSID_DATA;
456*c15d8692SAndriy Voskoboinyk 
457*c15d8692SAndriy Voskoboinyk 		sc->rcr &= ~mask_all;
458*c15d8692SAndriy Voskoboinyk 		sc->rcr |= mask_min;
459*c15d8692SAndriy Voskoboinyk 	} else {
460*c15d8692SAndriy Voskoboinyk 		sc->rcr &= ~mask_min;
461*c15d8692SAndriy Voskoboinyk 		sc->rcr |= mask_all;
462*c15d8692SAndriy Voskoboinyk 	}
463*c15d8692SAndriy Voskoboinyk 	rtwn_rxfilter_set(sc);
4647453645fSAndriy Voskoboinyk }
465