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