xref: /freebsd-src/sys/dev/rtwn/if_rtwn_rx.c (revision f45f66fadacc2efff3c2ff64e3528b8dbaad3673)
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_dl.h>
417453645fSAndriy Voskoboinyk #include <net/if_media.h>
427453645fSAndriy Voskoboinyk 
437453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
447453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h>
457453645fSAndriy Voskoboinyk 
467453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
477453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
487453645fSAndriy Voskoboinyk 
497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
507453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_ridx.h>
517453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_rx.h>
527453645fSAndriy Voskoboinyk 
537453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_reg.h>
547453645fSAndriy Voskoboinyk 
55745a8582SAdrian Chadd /*
56745a8582SAdrian Chadd  * Get the driver rate set for the current operating rateset(s).
57745a8582SAdrian Chadd  *
58745a8582SAdrian Chadd  * rates_p is set to a mask of 11abg ridx values (not HW rate values.)
59745a8582SAdrian Chadd  * htrates_p is set to a mask of 11n ridx values (not HW rate values),
60745a8582SAdrian Chadd  *  starting at MCS0 == bit 0.
61745a8582SAdrian Chadd  *
62745a8582SAdrian Chadd  * maxrate_p is set to the ridx value.
63745a8582SAdrian Chadd  *
64745a8582SAdrian Chadd  * If basic_rates is 1 then only the 11abg basic rate logic will
65aaaca5f2SAdrian Chadd  * be applied; the HT rateset will be applied to 11n rates.
66745a8582SAdrian Chadd  */
677453645fSAndriy Voskoboinyk void
687453645fSAndriy Voskoboinyk rtwn_get_rates(struct rtwn_softc *sc, const struct ieee80211_rateset *rs,
697453645fSAndriy Voskoboinyk     const struct ieee80211_htrateset *rs_ht, uint32_t *rates_p,
70745a8582SAdrian Chadd     uint32_t *htrates_p, int *maxrate_p, int basic_rates)
717453645fSAndriy Voskoboinyk {
72745a8582SAdrian Chadd 	uint32_t rates = 0, htrates = 0;
737453645fSAndriy Voskoboinyk 	uint8_t ridx;
747453645fSAndriy Voskoboinyk 	int i, maxrate;
757453645fSAndriy Voskoboinyk 
767453645fSAndriy Voskoboinyk 	/* Get rates mask. */
777453645fSAndriy Voskoboinyk 	rates = 0;
787453645fSAndriy Voskoboinyk 	maxrate = 0;
797453645fSAndriy Voskoboinyk 
80745a8582SAdrian Chadd 	/* This is for 11abg */
817453645fSAndriy Voskoboinyk 	for (i = 0; i < rs->rs_nrates; i++) {
827453645fSAndriy Voskoboinyk 		/* Convert 802.11 rate to HW rate index. */
837453645fSAndriy Voskoboinyk 		ridx = rate2ridx(IEEE80211_RV(rs->rs_rates[i]));
847453645fSAndriy Voskoboinyk 		if (ridx == RTWN_RIDX_UNKNOWN)	/* Unknown rate, skip. */
857453645fSAndriy Voskoboinyk 			continue;
867453645fSAndriy Voskoboinyk 		if (((rs->rs_rates[i] & IEEE80211_RATE_BASIC) != 0) ||
877453645fSAndriy Voskoboinyk 		    !basic_rates) {
887453645fSAndriy Voskoboinyk 			rates |= 1 << ridx;
897453645fSAndriy Voskoboinyk 			if (ridx > maxrate)
907453645fSAndriy Voskoboinyk 				maxrate = ridx;
917453645fSAndriy Voskoboinyk 		}
927453645fSAndriy Voskoboinyk 	}
937453645fSAndriy Voskoboinyk 
947453645fSAndriy Voskoboinyk 	/* If we're doing 11n, enable 11n rates */
95aaaca5f2SAdrian Chadd 	if (rs_ht != NULL) {
967453645fSAndriy Voskoboinyk 		for (i = 0; i < rs_ht->rs_nrates; i++) {
97aaaca5f2SAdrian Chadd 			uint8_t rate = rs_ht->rs_rates[i] & 0x7f;
98aaaca5f2SAdrian Chadd 			bool is_basic = rs_ht->rs_rates[i] &
99aaaca5f2SAdrian Chadd 			    IEEE80211_RATE_BASIC;
100745a8582SAdrian Chadd 			/* Only do up to 2-stream rates for now */
101aaaca5f2SAdrian Chadd 			if ((rate) > 0xf)
1027453645fSAndriy Voskoboinyk 				continue;
103aaaca5f2SAdrian Chadd 
104aaaca5f2SAdrian Chadd 			if (basic_rates && is_basic == false)
105aaaca5f2SAdrian Chadd 				continue;
106aaaca5f2SAdrian Chadd 
107aaaca5f2SAdrian Chadd 			ridx = rate & 0xf;
108745a8582SAdrian Chadd 			htrates |= (1 << ridx);
1097453645fSAndriy Voskoboinyk 
1107453645fSAndriy Voskoboinyk 			/* Guard against the rate table being oddly ordered */
111745a8582SAdrian Chadd 			if (RTWN_RIDX_HT_MCS(ridx) > maxrate)
112745a8582SAdrian Chadd 				maxrate = RTWN_RIDX_HT_MCS(ridx);
1137453645fSAndriy Voskoboinyk 		}
1147453645fSAndriy Voskoboinyk 	}
1157453645fSAndriy Voskoboinyk 
1167453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RA,
117aaaca5f2SAdrian Chadd 	    "%s: rates 0x%08X htrates 0x%08X, maxrate %d\n",
118aaaca5f2SAdrian Chadd 	    __func__, rates, htrates, maxrate);
1197453645fSAndriy Voskoboinyk 
1207453645fSAndriy Voskoboinyk 	if (rates_p != NULL)
1217453645fSAndriy Voskoboinyk 		*rates_p = rates;
122745a8582SAdrian Chadd 	if (htrates_p != NULL)
123745a8582SAdrian Chadd 		*htrates_p = htrates;
1247453645fSAndriy Voskoboinyk 	if (maxrate_p != NULL)
1257453645fSAndriy Voskoboinyk 		*maxrate_p = maxrate;
1267453645fSAndriy Voskoboinyk }
1277453645fSAndriy Voskoboinyk 
1287453645fSAndriy Voskoboinyk void
1297453645fSAndriy Voskoboinyk rtwn_set_basicrates(struct rtwn_softc *sc, uint32_t rates)
1307453645fSAndriy Voskoboinyk {
1317453645fSAndriy Voskoboinyk 
1327453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RA, "%s: rates 0x%08X\n", __func__, rates);
1337453645fSAndriy Voskoboinyk 
1347453645fSAndriy Voskoboinyk 	rtwn_setbits_4(sc, R92C_RRSR, R92C_RRSR_RATE_BITMAP_M, rates);
1357453645fSAndriy Voskoboinyk }
1367453645fSAndriy Voskoboinyk 
137*f45f66faSAdrian Chadd /*
138*f45f66faSAdrian Chadd  * Configure the initial RTS rate to use.
139*f45f66faSAdrian Chadd  */
140*f45f66faSAdrian Chadd void
141*f45f66faSAdrian Chadd rtwn_set_rts_rate(struct rtwn_softc *sc, uint32_t rates)
142*f45f66faSAdrian Chadd {
143*f45f66faSAdrian Chadd 	uint8_t ridx;
144*f45f66faSAdrian Chadd 
145*f45f66faSAdrian Chadd 	/*
146*f45f66faSAdrian Chadd 	 * We shouldn't set the initial RTS/CTS generation rate
147*f45f66faSAdrian Chadd 	 * as the highest available rate - that may end up
148*f45f66faSAdrian Chadd 	 * with trying to configure something like MCS1 RTS/CTS.
149*f45f66faSAdrian Chadd 	 *
150*f45f66faSAdrian Chadd 	 * Instead, choose a suitable low OFDM/CCK rate based
151*f45f66faSAdrian Chadd 	 * on the basic rate bitmask.  Assume the caller
152*f45f66faSAdrian Chadd 	 * has filtered out CCK modes in 5GHz.
153*f45f66faSAdrian Chadd 	 */
154*f45f66faSAdrian Chadd 	rates &= (1 << RTWN_RIDX_CCK1) | (1 << RTWN_RIDX_CCK55) |
155*f45f66faSAdrian Chadd 	    (1 << RTWN_RIDX_CCK11) | (1 << RTWN_RIDX_OFDM6) |
156*f45f66faSAdrian Chadd 	    (1 << RTWN_RIDX_OFDM9) | (1 << RTWN_RIDX_OFDM12) |
157*f45f66faSAdrian Chadd 	    (1 << RTWN_RIDX_OFDM18) | (1 << RTWN_RIDX_OFDM24);
158*f45f66faSAdrian Chadd 	if (rates == 0) {
159*f45f66faSAdrian Chadd 		device_printf(sc->sc_dev,
160*f45f66faSAdrian Chadd 		    "WARNING: no configured basic RTS rate!\n");
161*f45f66faSAdrian Chadd 		return;
162*f45f66faSAdrian Chadd 	}
163*f45f66faSAdrian Chadd 	ridx = fls(rates) - 1;
164*f45f66faSAdrian Chadd 
165*f45f66faSAdrian Chadd 	RTWN_DPRINTF(sc, RTWN_DEBUG_RA,
166*f45f66faSAdrian Chadd 	    "%s: mask=0x%08x, ridx=%d\n",
167*f45f66faSAdrian Chadd 	    __func__, rates, ridx);
168*f45f66faSAdrian Chadd 
169*f45f66faSAdrian Chadd 	rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, ridx);
170*f45f66faSAdrian Chadd }
171*f45f66faSAdrian Chadd 
1727453645fSAndriy Voskoboinyk static void
17309606165SAndriy Voskoboinyk rtwn_update_avgrssi(struct rtwn_softc *sc, struct rtwn_node *un, int8_t rssi,
17409606165SAndriy Voskoboinyk     int is_cck)
1757453645fSAndriy Voskoboinyk {
1767453645fSAndriy Voskoboinyk 	int pwdb;
1777453645fSAndriy Voskoboinyk 
1787453645fSAndriy Voskoboinyk 	/* Convert antenna signal to percentage. */
17909606165SAndriy Voskoboinyk 	if (rssi <= -100 || rssi >= 20)
1807453645fSAndriy Voskoboinyk 		pwdb = 0;
18109606165SAndriy Voskoboinyk 	else if (rssi >= 0)
1827453645fSAndriy Voskoboinyk 		pwdb = 100;
1837453645fSAndriy Voskoboinyk 	else
18409606165SAndriy Voskoboinyk 		pwdb = 100 + rssi;
18509606165SAndriy Voskoboinyk 	if (is_cck) {
1867453645fSAndriy Voskoboinyk 		/* CCK gain is smaller than OFDM/MCS gain. */
1877453645fSAndriy Voskoboinyk 		pwdb += 6;
1887453645fSAndriy Voskoboinyk 		if (pwdb > 100)
1897453645fSAndriy Voskoboinyk 			pwdb = 100;
1907453645fSAndriy Voskoboinyk 		if (pwdb <= 14)
1917453645fSAndriy Voskoboinyk 			pwdb -= 4;
1927453645fSAndriy Voskoboinyk 		else if (pwdb <= 26)
1937453645fSAndriy Voskoboinyk 			pwdb -= 8;
1947453645fSAndriy Voskoboinyk 		else if (pwdb <= 34)
1957453645fSAndriy Voskoboinyk 			pwdb -= 6;
1967453645fSAndriy Voskoboinyk 		else if (pwdb <= 42)
1977453645fSAndriy Voskoboinyk 			pwdb -= 2;
1987453645fSAndriy Voskoboinyk 	}
1997453645fSAndriy Voskoboinyk 
2007453645fSAndriy Voskoboinyk 	if (un->avg_pwdb == -1)	/* Init. */
2017453645fSAndriy Voskoboinyk 		un->avg_pwdb = pwdb;
2027453645fSAndriy Voskoboinyk 	else if (un->avg_pwdb < pwdb)
2037453645fSAndriy Voskoboinyk 		un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20) + 1;
2047453645fSAndriy Voskoboinyk 	else
2057453645fSAndriy Voskoboinyk 		un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20);
2067453645fSAndriy Voskoboinyk 
2077453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI,
2087453645fSAndriy Voskoboinyk 	    "MACID %d, PWDB %d, EMA %d\n", un->id, pwdb, un->avg_pwdb);
2097453645fSAndriy Voskoboinyk }
2107453645fSAndriy Voskoboinyk 
2117453645fSAndriy Voskoboinyk static int8_t
21209606165SAndriy Voskoboinyk rtwn_get_rssi(struct rtwn_softc *sc, void *physt, int is_cck)
2137453645fSAndriy Voskoboinyk {
2147453645fSAndriy Voskoboinyk 	int8_t rssi;
2157453645fSAndriy Voskoboinyk 
21609606165SAndriy Voskoboinyk 	if (is_cck)
2177453645fSAndriy Voskoboinyk 		rssi = rtwn_get_rssi_cck(sc, physt);
2187453645fSAndriy Voskoboinyk 	else	/* OFDM/HT. */
2197453645fSAndriy Voskoboinyk 		rssi = rtwn_get_rssi_ofdm(sc, physt);
2207453645fSAndriy Voskoboinyk 
2217453645fSAndriy Voskoboinyk 	return (rssi);
2227453645fSAndriy Voskoboinyk }
2237453645fSAndriy Voskoboinyk 
2247453645fSAndriy Voskoboinyk static uint32_t
2257453645fSAndriy Voskoboinyk rtwn_get_tsf_low(struct rtwn_softc *sc, int id)
2267453645fSAndriy Voskoboinyk {
2277453645fSAndriy Voskoboinyk 	return (rtwn_read_4(sc, R92C_TSFTR(id)));
2287453645fSAndriy Voskoboinyk }
2297453645fSAndriy Voskoboinyk 
2307453645fSAndriy Voskoboinyk static uint32_t
2317453645fSAndriy Voskoboinyk rtwn_get_tsf_high(struct rtwn_softc *sc, int id)
2327453645fSAndriy Voskoboinyk {
2337453645fSAndriy Voskoboinyk 	return (rtwn_read_4(sc, R92C_TSFTR(id) + 4));
2347453645fSAndriy Voskoboinyk }
2357453645fSAndriy Voskoboinyk 
2367453645fSAndriy Voskoboinyk static void
2377453645fSAndriy Voskoboinyk rtwn_get_tsf(struct rtwn_softc *sc, uint64_t *buf, int id)
2387453645fSAndriy Voskoboinyk {
2397453645fSAndriy Voskoboinyk 	/* NB: we cannot read it at once. */
2407453645fSAndriy Voskoboinyk 	*buf = rtwn_get_tsf_high(sc, id);
2417453645fSAndriy Voskoboinyk 	*buf <<= 32;
2427453645fSAndriy Voskoboinyk 	*buf += rtwn_get_tsf_low(sc, id);
2437453645fSAndriy Voskoboinyk }
2447453645fSAndriy Voskoboinyk 
24509606165SAndriy Voskoboinyk static uint64_t
246c5ad99fcSAndriy Voskoboinyk rtwn_extend_rx_tsf(struct rtwn_softc *sc,
247c5ad99fcSAndriy Voskoboinyk     const struct rtwn_rx_stat_common *stat)
24809606165SAndriy Voskoboinyk {
24909606165SAndriy Voskoboinyk 	uint64_t tsft;
25009606165SAndriy Voskoboinyk 	uint32_t rxdw3, tsfl, tsfl_curr;
25109606165SAndriy Voskoboinyk 	int id;
25209606165SAndriy Voskoboinyk 
25309606165SAndriy Voskoboinyk 	rxdw3 = le32toh(stat->rxdw3);
25409606165SAndriy Voskoboinyk 	tsfl = le32toh(stat->tsf_low);
255c5ad99fcSAndriy Voskoboinyk 	id = MS(rxdw3, RTWN_RXDW3_BSSID01_FIT);
25609606165SAndriy Voskoboinyk 
25709606165SAndriy Voskoboinyk 	switch (id) {
25809606165SAndriy Voskoboinyk 	case 1:
25909606165SAndriy Voskoboinyk 	case 2:
26009606165SAndriy Voskoboinyk 		id >>= 1;
26109606165SAndriy Voskoboinyk 		tsfl_curr = rtwn_get_tsf_low(sc, id);
26209606165SAndriy Voskoboinyk 		break;
26309606165SAndriy Voskoboinyk 	default:
26409606165SAndriy Voskoboinyk 	{
26509606165SAndriy Voskoboinyk 		uint32_t tsfl0, tsfl1;
26609606165SAndriy Voskoboinyk 
26709606165SAndriy Voskoboinyk 		tsfl0 = rtwn_get_tsf_low(sc, 0);
26809606165SAndriy Voskoboinyk 		tsfl1 = rtwn_get_tsf_low(sc, 1);
26909606165SAndriy Voskoboinyk 
27009606165SAndriy Voskoboinyk 		if (abs(tsfl0 - tsfl) < abs(tsfl1 - tsfl)) {
27109606165SAndriy Voskoboinyk 			id = 0;
27209606165SAndriy Voskoboinyk 			tsfl_curr = tsfl0;
27309606165SAndriy Voskoboinyk 		} else {
27409606165SAndriy Voskoboinyk 			id = 1;
27509606165SAndriy Voskoboinyk 			tsfl_curr = tsfl1;
27609606165SAndriy Voskoboinyk 		}
27709606165SAndriy Voskoboinyk 		break;
27809606165SAndriy Voskoboinyk 	}
27909606165SAndriy Voskoboinyk 	}
28009606165SAndriy Voskoboinyk 
28109606165SAndriy Voskoboinyk 	tsft = rtwn_get_tsf_high(sc, id);
28209606165SAndriy Voskoboinyk 	if (tsfl > tsfl_curr && tsfl > 0xffff0000)
28309606165SAndriy Voskoboinyk 		tsft--;
28409606165SAndriy Voskoboinyk 	tsft <<= 32;
28509606165SAndriy Voskoboinyk 	tsft += tsfl;
28609606165SAndriy Voskoboinyk 
28709606165SAndriy Voskoboinyk 	return (tsft);
28809606165SAndriy Voskoboinyk }
28909606165SAndriy Voskoboinyk 
2907453645fSAndriy Voskoboinyk struct ieee80211_node *
29109606165SAndriy Voskoboinyk rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc)
2927453645fSAndriy Voskoboinyk {
2937453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
2947453645fSAndriy Voskoboinyk 	struct ieee80211_node *ni;
2957453645fSAndriy Voskoboinyk 	struct ieee80211_frame_min *wh;
29609606165SAndriy Voskoboinyk 	struct ieee80211_rx_stats rxs;
2977453645fSAndriy Voskoboinyk 	struct rtwn_node *un;
298c5ad99fcSAndriy Voskoboinyk 	struct rtwn_rx_stat_common *stat;
29909606165SAndriy Voskoboinyk 	void *physt;
30009606165SAndriy Voskoboinyk 	uint32_t rxdw0;
30109606165SAndriy Voskoboinyk 	int8_t rssi;
30209606165SAndriy Voskoboinyk 	int cipher, infosz, is_cck, pktlen, shift;
3037453645fSAndriy Voskoboinyk 
3047453645fSAndriy Voskoboinyk 	stat = desc;
3057453645fSAndriy Voskoboinyk 	rxdw0 = le32toh(stat->rxdw0);
3067453645fSAndriy Voskoboinyk 
307c5ad99fcSAndriy Voskoboinyk 	cipher = MS(rxdw0, RTWN_RXDW0_CIPHER);
308c5ad99fcSAndriy Voskoboinyk 	infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8;
309c5ad99fcSAndriy Voskoboinyk 	pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN);
310c5ad99fcSAndriy Voskoboinyk 	shift = MS(rxdw0, RTWN_RXDW0_SHIFT);
3117453645fSAndriy Voskoboinyk 
3127453645fSAndriy Voskoboinyk 	wh = (struct ieee80211_frame_min *)(mtodo(m, shift + infosz));
3137453645fSAndriy Voskoboinyk 	if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
3147453645fSAndriy Voskoboinyk 	    cipher != R92C_CAM_ALGO_NONE)
3157453645fSAndriy Voskoboinyk 		m->m_flags |= M_WEP;
3167453645fSAndriy Voskoboinyk 
31709606165SAndriy Voskoboinyk 	if (pktlen >= sizeof(*wh)) {
3187453645fSAndriy Voskoboinyk 		ni = ieee80211_find_rxnode(ic, wh);
31909606165SAndriy Voskoboinyk 		if (ni != NULL && (ni->ni_flags & IEEE80211_NODE_HT))
32009606165SAndriy Voskoboinyk 			m->m_flags |= M_AMPDU;
32109606165SAndriy Voskoboinyk 	} else
3227453645fSAndriy Voskoboinyk 		ni = NULL;
3237453645fSAndriy Voskoboinyk 	un = RTWN_NODE(ni);
3247453645fSAndriy Voskoboinyk 
325c5ad99fcSAndriy Voskoboinyk 	if (infosz != 0 && (rxdw0 & RTWN_RXDW0_PHYST))
32609606165SAndriy Voskoboinyk 		physt = (void *)mtodo(m, shift);
32709606165SAndriy Voskoboinyk 	else
32809606165SAndriy Voskoboinyk 		physt = (un != NULL) ? &un->last_physt : &sc->last_physt;
3297453645fSAndriy Voskoboinyk 
33009606165SAndriy Voskoboinyk 	bzero(&rxs, sizeof(rxs));
33109606165SAndriy Voskoboinyk 	rtwn_get_rx_stats(sc, &rxs, desc, physt);
33209606165SAndriy Voskoboinyk 	if (rxs.c_pktflags & IEEE80211_RX_F_AMPDU) {
33309606165SAndriy Voskoboinyk 		/* Next MPDU will come without PHY info. */
33409606165SAndriy Voskoboinyk 		memcpy(&sc->last_physt, physt, sizeof(sc->last_physt));
33509606165SAndriy Voskoboinyk 		if (un != NULL)
33609606165SAndriy Voskoboinyk 			memcpy(&un->last_physt, physt, sizeof(sc->last_physt));
3377453645fSAndriy Voskoboinyk 	}
33809606165SAndriy Voskoboinyk 
33909606165SAndriy Voskoboinyk 	/* Add some common bits. */
34009606165SAndriy Voskoboinyk 	/* NB: should not happen. */
341c5ad99fcSAndriy Voskoboinyk 	if (rxdw0 & RTWN_RXDW0_CRCERR)
34209606165SAndriy Voskoboinyk 		rxs.c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC;
34309606165SAndriy Voskoboinyk 
34409606165SAndriy Voskoboinyk 	rxs.r_flags |= IEEE80211_R_TSF_START;	/* XXX undocumented */
345fcb5e8d0SAdrian Chadd 
346fcb5e8d0SAdrian Chadd 	/*
347fcb5e8d0SAdrian Chadd 	 * Doing the TSF64 extension on USB is expensive, especially
348fcb5e8d0SAdrian Chadd 	 * if it's being done on every MPDU in an AMPDU burst.
349fcb5e8d0SAdrian Chadd 	 */
350fcb5e8d0SAdrian Chadd 	if (sc->sc_ena_tsf64) {
35109606165SAndriy Voskoboinyk 		rxs.r_flags |= IEEE80211_R_TSF64;
35209606165SAndriy Voskoboinyk 		rxs.c_rx_tsf = rtwn_extend_rx_tsf(sc, stat);
353fcb5e8d0SAdrian Chadd 	} else {
354fcb5e8d0SAdrian Chadd 		rxs.r_flags |= IEEE80211_R_TSF32;
355fcb5e8d0SAdrian Chadd 		rxs.c_rx_tsf = le32toh(stat->tsf_low);
356fcb5e8d0SAdrian Chadd 	}
35709606165SAndriy Voskoboinyk 
35809606165SAndriy Voskoboinyk 	/* Get RSSI from PHY status descriptor. */
35909606165SAndriy Voskoboinyk 	is_cck = (rxs.c_pktflags & IEEE80211_RX_F_CCK) != 0;
36009606165SAndriy Voskoboinyk 	rssi = rtwn_get_rssi(sc, physt, is_cck);
36109606165SAndriy Voskoboinyk 
36209606165SAndriy Voskoboinyk 	/* XXX TODO: we really need a rate-to-string method */
36309606165SAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, "%s: rssi %d, rate %d\n",
36409606165SAndriy Voskoboinyk 	    __func__, rssi, rxs.c_rate);
365c5ad99fcSAndriy Voskoboinyk 	if (un != NULL && infosz != 0 && (rxdw0 & RTWN_RXDW0_PHYST)) {
36609606165SAndriy Voskoboinyk 		/* Update our average RSSI. */
36709606165SAndriy Voskoboinyk 		rtwn_update_avgrssi(sc, un, rssi, is_cck);
36809606165SAndriy Voskoboinyk 	}
36909606165SAndriy Voskoboinyk 
37009606165SAndriy Voskoboinyk 	rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
37109606165SAndriy Voskoboinyk 	rxs.c_nf = RTWN_NOISE_FLOOR;
37209606165SAndriy Voskoboinyk 	rxs.c_rssi = rssi - rxs.c_nf;
37309606165SAndriy Voskoboinyk 	(void) ieee80211_add_rx_params(m, &rxs);
3747453645fSAndriy Voskoboinyk 
3757453645fSAndriy Voskoboinyk 	if (ieee80211_radiotap_active(ic)) {
3767453645fSAndriy Voskoboinyk 		struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap;
3777453645fSAndriy Voskoboinyk 
3787453645fSAndriy Voskoboinyk 		tap->wr_flags = rtwn_rx_radiotap_flags(sc, desc);
37909606165SAndriy Voskoboinyk 		tap->wr_tsft = htole64(rxs.c_rx_tsf);
38009606165SAndriy Voskoboinyk 		tap->wr_rate = rxs.c_rate;
38109606165SAndriy Voskoboinyk 		tap->wr_dbm_antsignal = rssi;
38209606165SAndriy Voskoboinyk 		tap->wr_dbm_antnoise = rxs.c_nf;
3837453645fSAndriy Voskoboinyk 	}
3847453645fSAndriy Voskoboinyk 
3857453645fSAndriy Voskoboinyk 	/* Drop PHY descriptor. */
3867453645fSAndriy Voskoboinyk 	m_adj(m, infosz + shift);
3877453645fSAndriy Voskoboinyk 
388d76247e8SAdrian Chadd 	/* If APPFCS, drop FCS */
389d76247e8SAdrian Chadd 	if (sc->rcr & R92C_RCR_APPFCS)
390d76247e8SAdrian Chadd 		m_adj(m, -IEEE80211_CRC_LEN);
391d76247e8SAdrian Chadd 
3927453645fSAndriy Voskoboinyk 	return (ni);
3937453645fSAndriy Voskoboinyk }
3947453645fSAndriy Voskoboinyk 
3957453645fSAndriy Voskoboinyk void
3967453645fSAndriy Voskoboinyk rtwn_adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
3977453645fSAndriy Voskoboinyk     const struct ieee80211_rx_stats *rxs,
3987453645fSAndriy Voskoboinyk     int rssi, int nf)
3997453645fSAndriy Voskoboinyk {
4007453645fSAndriy Voskoboinyk 	struct ieee80211vap *vap = ni->ni_vap;
4017453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = vap->iv_ic->ic_softc;
4027453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
4037453645fSAndriy Voskoboinyk 	uint64_t ni_tstamp, curr_tstamp;
4047453645fSAndriy Voskoboinyk 
4057453645fSAndriy Voskoboinyk 	uvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);
4067453645fSAndriy Voskoboinyk 
4077453645fSAndriy Voskoboinyk 	if (vap->iv_state == IEEE80211_S_RUN &&
4087453645fSAndriy Voskoboinyk 	    (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
4097453645fSAndriy Voskoboinyk 	    subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
4107453645fSAndriy Voskoboinyk 		ni_tstamp = le64toh(ni->ni_tstamp.tsf);
4117453645fSAndriy Voskoboinyk 		RTWN_LOCK(sc);
4127453645fSAndriy Voskoboinyk 		rtwn_get_tsf(sc, &curr_tstamp, uvp->id);
4137453645fSAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
4147453645fSAndriy Voskoboinyk 
4157453645fSAndriy Voskoboinyk 		if (ni_tstamp >= curr_tstamp)
4167453645fSAndriy Voskoboinyk 			(void) ieee80211_ibss_merge(ni);
4177453645fSAndriy Voskoboinyk 	}
4187453645fSAndriy Voskoboinyk }
4197453645fSAndriy Voskoboinyk 
4207453645fSAndriy Voskoboinyk static uint8_t
4217453645fSAndriy Voskoboinyk rtwn_get_multi_pos(const uint8_t maddr[])
4227453645fSAndriy Voskoboinyk {
4237453645fSAndriy Voskoboinyk 	uint64_t mask = 0x00004d101df481b4;
4247453645fSAndriy Voskoboinyk 	uint8_t pos = 0x27;	/* initial value */
4257453645fSAndriy Voskoboinyk 	int i, j;
4267453645fSAndriy Voskoboinyk 
4277453645fSAndriy Voskoboinyk 	for (i = 0; i < IEEE80211_ADDR_LEN; i++)
4287453645fSAndriy Voskoboinyk 		for (j = (i == 0) ? 1 : 0; j < 8; j++)
4297453645fSAndriy Voskoboinyk 			if ((maddr[i] >> j) & 1)
4307453645fSAndriy Voskoboinyk 				pos ^= (mask >> (i * 8 + j - 1));
4317453645fSAndriy Voskoboinyk 
4327453645fSAndriy Voskoboinyk 	pos &= 0x3f;
4337453645fSAndriy Voskoboinyk 
4347453645fSAndriy Voskoboinyk 	return (pos);
4357453645fSAndriy Voskoboinyk }
4367453645fSAndriy Voskoboinyk 
437bc0bdf25SGleb Smirnoff static u_int
438bc0bdf25SGleb Smirnoff rtwm_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
439bc0bdf25SGleb Smirnoff {
440bc0bdf25SGleb Smirnoff 	uint32_t *mfilt = arg;
441bc0bdf25SGleb Smirnoff 	uint8_t pos;
442bc0bdf25SGleb Smirnoff 
443bc0bdf25SGleb Smirnoff 	pos = rtwn_get_multi_pos(LLADDR(sdl));
444bc0bdf25SGleb Smirnoff 	mfilt[pos / 32] |= (1 << (pos % 32));
445bc0bdf25SGleb Smirnoff 
446bc0bdf25SGleb Smirnoff 	return (1);
447bc0bdf25SGleb Smirnoff }
448bc0bdf25SGleb Smirnoff 
4497453645fSAndriy Voskoboinyk void
4507453645fSAndriy Voskoboinyk rtwn_set_multi(struct rtwn_softc *sc)
4517453645fSAndriy Voskoboinyk {
4527453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
4537453645fSAndriy Voskoboinyk 	uint32_t mfilt[2];
4547453645fSAndriy Voskoboinyk 
4557453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
4567453645fSAndriy Voskoboinyk 
4577453645fSAndriy Voskoboinyk 	/* general structure was copied from ath(4). */
4587453645fSAndriy Voskoboinyk 	if (ic->ic_allmulti == 0) {
4597453645fSAndriy Voskoboinyk 		struct ieee80211vap *vap;
4607453645fSAndriy Voskoboinyk 
4617453645fSAndriy Voskoboinyk 		/*
4627453645fSAndriy Voskoboinyk 		 * Merge multicast addresses to form the hardware filter.
4637453645fSAndriy Voskoboinyk 		 */
4647453645fSAndriy Voskoboinyk 		mfilt[0] = mfilt[1] = 0;
465bc0bdf25SGleb Smirnoff 		TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
466bc0bdf25SGleb Smirnoff 			if_foreach_llmaddr(vap->iv_ifp, rtwm_hash_maddr, mfilt);
4677453645fSAndriy Voskoboinyk 	} else
4687453645fSAndriy Voskoboinyk 		mfilt[0] = mfilt[1] = ~0;
4697453645fSAndriy Voskoboinyk 
4707453645fSAndriy Voskoboinyk 	rtwn_write_4(sc, R92C_MAR + 0, mfilt[0]);
4717453645fSAndriy Voskoboinyk 	rtwn_write_4(sc, R92C_MAR + 4, mfilt[1]);
4727453645fSAndriy Voskoboinyk 
4737453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s: MC filter %08x:%08x\n",
4747453645fSAndriy Voskoboinyk 	    __func__, mfilt[0], mfilt[1]);
4757453645fSAndriy Voskoboinyk }
4767453645fSAndriy Voskoboinyk 
4777453645fSAndriy Voskoboinyk static void
4787453645fSAndriy Voskoboinyk rtwn_rxfilter_update_mgt(struct rtwn_softc *sc)
4797453645fSAndriy Voskoboinyk {
4807453645fSAndriy Voskoboinyk 	uint16_t filter;
4817453645fSAndriy Voskoboinyk 
482c15d8692SAndriy Voskoboinyk 	filter = 0x7f7f;
4837453645fSAndriy Voskoboinyk 	if (sc->bcn_vaps == 0) {	/* STA and/or MONITOR mode vaps */
4847453645fSAndriy Voskoboinyk 		filter &= ~(
4857453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_REQ) |
4867453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_REQ) |
4877453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_PROBE_REQ));
4887453645fSAndriy Voskoboinyk 	}
4897453645fSAndriy Voskoboinyk 	if (sc->ap_vaps == sc->nvaps - sc->mon_vaps) {	/* AP vaps only */
4907453645fSAndriy Voskoboinyk 		filter &= ~(
4917453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_RESP) |
4927453645fSAndriy Voskoboinyk 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_RESP));
4937453645fSAndriy Voskoboinyk 	}
4947453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_RXFLTMAP0, filter);
4957453645fSAndriy Voskoboinyk }
4967453645fSAndriy Voskoboinyk 
4977453645fSAndriy Voskoboinyk void
4987453645fSAndriy Voskoboinyk rtwn_rxfilter_update(struct rtwn_softc *sc)
4997453645fSAndriy Voskoboinyk {
5007453645fSAndriy Voskoboinyk 
5017453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
5027453645fSAndriy Voskoboinyk 
5037453645fSAndriy Voskoboinyk 	/* Filter for management frames. */
5047453645fSAndriy Voskoboinyk 	rtwn_rxfilter_update_mgt(sc);
5057453645fSAndriy Voskoboinyk 
5067453645fSAndriy Voskoboinyk 	/* Update Rx filter. */
5077453645fSAndriy Voskoboinyk 	rtwn_set_promisc(sc);
5087453645fSAndriy Voskoboinyk }
5097453645fSAndriy Voskoboinyk 
5107453645fSAndriy Voskoboinyk void
5117453645fSAndriy Voskoboinyk rtwn_rxfilter_init(struct rtwn_softc *sc)
5127453645fSAndriy Voskoboinyk {
5137453645fSAndriy Voskoboinyk 
5147453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
5157453645fSAndriy Voskoboinyk 
5167453645fSAndriy Voskoboinyk 	/* Setup multicast filter. */
5177453645fSAndriy Voskoboinyk 	rtwn_set_multi(sc);
5187453645fSAndriy Voskoboinyk 
5197453645fSAndriy Voskoboinyk 	/* Reject all control frames. */
5207453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000);
5217453645fSAndriy Voskoboinyk 
5227453645fSAndriy Voskoboinyk 	/* Reject all data frames. */
5237453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_RXFLTMAP2, 0x0000);
5247453645fSAndriy Voskoboinyk 
525c15d8692SAndriy Voskoboinyk 	/* Append generic Rx filter bits. */
526c15d8692SAndriy Voskoboinyk 	sc->rcr |= R92C_RCR_AM | R92C_RCR_AB | R92C_RCR_APM |
5277453645fSAndriy Voskoboinyk 	    R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS |
5287453645fSAndriy Voskoboinyk 	    R92C_RCR_APP_ICV | R92C_RCR_APP_MIC;
5297453645fSAndriy Voskoboinyk 
530d76247e8SAdrian Chadd 	/*
531d76247e8SAdrian Chadd 	 * Add FCS, to work around occasional 4 byte truncation
532d76247e8SAdrian Chadd 	 * with some frames.  This is more problematic on RTL8812/
533d76247e8SAdrian Chadd 	 * RTL8821 because they're also doing L3/L4 checksum offload
534d76247e8SAdrian Chadd 	 * and hardware encryption, so both are tagged as "passed"
535d76247e8SAdrian Chadd 	 * before the frame is truncated.
536d76247e8SAdrian Chadd 	 */
537d76247e8SAdrian Chadd 	sc->rcr |= R92C_RCR_APPFCS;
538d76247e8SAdrian Chadd 
5397453645fSAndriy Voskoboinyk 	/* Update dynamic Rx filter parts. */
5407453645fSAndriy Voskoboinyk 	rtwn_rxfilter_update(sc);
5417453645fSAndriy Voskoboinyk }
5427453645fSAndriy Voskoboinyk 
5437453645fSAndriy Voskoboinyk void
544c15d8692SAndriy Voskoboinyk rtwn_rxfilter_set(struct rtwn_softc *sc)
545c15d8692SAndriy Voskoboinyk {
546c15d8692SAndriy Voskoboinyk 	if (!(sc->sc_flags & RTWN_RCR_LOCKED))
547c15d8692SAndriy Voskoboinyk 		rtwn_write_4(sc, R92C_RCR, sc->rcr);
548c15d8692SAndriy Voskoboinyk }
549c15d8692SAndriy Voskoboinyk 
550c15d8692SAndriy Voskoboinyk void
5517453645fSAndriy Voskoboinyk rtwn_set_rx_bssid_all(struct rtwn_softc *sc, int enable)
5527453645fSAndriy Voskoboinyk {
553c15d8692SAndriy Voskoboinyk 
5547453645fSAndriy Voskoboinyk 	if (enable)
555c15d8692SAndriy Voskoboinyk 		sc->rcr &= ~R92C_RCR_CBSSID_BCN;
5567453645fSAndriy Voskoboinyk 	else
557c15d8692SAndriy Voskoboinyk 		sc->rcr |= R92C_RCR_CBSSID_BCN;
558c15d8692SAndriy Voskoboinyk 	rtwn_rxfilter_set(sc);
5597453645fSAndriy Voskoboinyk }
5607453645fSAndriy Voskoboinyk 
5617453645fSAndriy Voskoboinyk void
5627453645fSAndriy Voskoboinyk rtwn_set_promisc(struct rtwn_softc *sc)
5637453645fSAndriy Voskoboinyk {
5647453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
565c15d8692SAndriy Voskoboinyk 	uint32_t mask_all, mask_min;
5667453645fSAndriy Voskoboinyk 
5677453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
5687453645fSAndriy Voskoboinyk 
569c15d8692SAndriy Voskoboinyk 	mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP;
570791170aaSAdrian Chadd 	mask_min = R92C_RCR_APM;
5717453645fSAndriy Voskoboinyk 
5727453645fSAndriy Voskoboinyk 	if (sc->bcn_vaps == 0)
573c15d8692SAndriy Voskoboinyk 		mask_min |= R92C_RCR_CBSSID_BCN;
5747453645fSAndriy Voskoboinyk 	if (sc->ap_vaps == 0)
575c15d8692SAndriy Voskoboinyk 		mask_min |= R92C_RCR_CBSSID_DATA;
5767453645fSAndriy Voskoboinyk 
577c15d8692SAndriy Voskoboinyk 	if (ic->ic_promisc == 0 && sc->mon_vaps == 0) {
578c15d8692SAndriy Voskoboinyk 		if (sc->bcn_vaps != 0)
579c15d8692SAndriy Voskoboinyk 			mask_all |= R92C_RCR_CBSSID_BCN;
580c15d8692SAndriy Voskoboinyk 		if (sc->ap_vaps != 0)	/* for Null data frames */
581c15d8692SAndriy Voskoboinyk 			mask_all |= R92C_RCR_CBSSID_DATA;
582c15d8692SAndriy Voskoboinyk 
583c15d8692SAndriy Voskoboinyk 		sc->rcr &= ~mask_all;
584c15d8692SAndriy Voskoboinyk 		sc->rcr |= mask_min;
585c15d8692SAndriy Voskoboinyk 	} else {
586c15d8692SAndriy Voskoboinyk 		sc->rcr &= ~mask_min;
587c15d8692SAndriy Voskoboinyk 		sc->rcr |= mask_all;
588c15d8692SAndriy Voskoboinyk 	}
589791170aaSAdrian Chadd 
590791170aaSAdrian Chadd 	/*
591791170aaSAdrian Chadd 	 * Add FCS, to work around occasional 4 byte truncation.
592791170aaSAdrian Chadd 	 * See the previous comment above R92C_RCR_APPFCS.
593791170aaSAdrian Chadd 	 */
594791170aaSAdrian Chadd 	sc->rcr |= R92C_RCR_APPFCS;
595791170aaSAdrian Chadd 
596c15d8692SAndriy Voskoboinyk 	rtwn_rxfilter_set(sc);
5977453645fSAndriy Voskoboinyk }
598