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 577453645fSAndriy Voskoboinyk 587453645fSAndriy Voskoboinyk void 597453645fSAndriy Voskoboinyk rtwn_get_rates(struct rtwn_softc *sc, const struct ieee80211_rateset *rs, 607453645fSAndriy Voskoboinyk const struct ieee80211_htrateset *rs_ht, uint32_t *rates_p, 617453645fSAndriy Voskoboinyk int *maxrate_p, int basic_rates) 627453645fSAndriy Voskoboinyk { 637453645fSAndriy Voskoboinyk uint32_t rates; 647453645fSAndriy Voskoboinyk uint8_t ridx; 657453645fSAndriy Voskoboinyk int i, maxrate; 667453645fSAndriy Voskoboinyk 677453645fSAndriy Voskoboinyk /* Get rates mask. */ 687453645fSAndriy Voskoboinyk rates = 0; 697453645fSAndriy Voskoboinyk maxrate = 0; 707453645fSAndriy Voskoboinyk 717453645fSAndriy Voskoboinyk /* This is for 11bg */ 727453645fSAndriy Voskoboinyk for (i = 0; i < rs->rs_nrates; i++) { 737453645fSAndriy Voskoboinyk /* Convert 802.11 rate to HW rate index. */ 747453645fSAndriy Voskoboinyk ridx = rate2ridx(IEEE80211_RV(rs->rs_rates[i])); 757453645fSAndriy Voskoboinyk if (ridx == RTWN_RIDX_UNKNOWN) /* Unknown rate, skip. */ 767453645fSAndriy Voskoboinyk continue; 777453645fSAndriy Voskoboinyk if (((rs->rs_rates[i] & IEEE80211_RATE_BASIC) != 0) || 787453645fSAndriy Voskoboinyk !basic_rates) { 797453645fSAndriy Voskoboinyk rates |= 1 << ridx; 807453645fSAndriy Voskoboinyk if (ridx > maxrate) 817453645fSAndriy Voskoboinyk maxrate = ridx; 827453645fSAndriy Voskoboinyk } 837453645fSAndriy Voskoboinyk } 847453645fSAndriy Voskoboinyk 857453645fSAndriy Voskoboinyk /* If we're doing 11n, enable 11n rates */ 867453645fSAndriy Voskoboinyk if (rs_ht != NULL && !basic_rates) { 877453645fSAndriy Voskoboinyk for (i = 0; i < rs_ht->rs_nrates; i++) { 887453645fSAndriy Voskoboinyk if ((rs_ht->rs_rates[i] & 0x7f) > 0xf) 897453645fSAndriy Voskoboinyk continue; 907453645fSAndriy Voskoboinyk /* 11n rates start at index 12 */ 91*0cc18edfSAndriy Voskoboinyk ridx = RTWN_RIDX_HT_MCS((rs_ht->rs_rates[i]) & 0xf); 927453645fSAndriy Voskoboinyk rates |= (1 << ridx); 937453645fSAndriy Voskoboinyk 947453645fSAndriy Voskoboinyk /* Guard against the rate table being oddly ordered */ 957453645fSAndriy Voskoboinyk if (ridx > maxrate) 967453645fSAndriy Voskoboinyk maxrate = ridx; 977453645fSAndriy Voskoboinyk } 987453645fSAndriy Voskoboinyk } 997453645fSAndriy Voskoboinyk 1007453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RA, 1017453645fSAndriy Voskoboinyk "%s: rates 0x%08X, maxrate %d\n", __func__, rates, maxrate); 1027453645fSAndriy Voskoboinyk 1037453645fSAndriy Voskoboinyk if (rates_p != NULL) 1047453645fSAndriy Voskoboinyk *rates_p = rates; 1057453645fSAndriy Voskoboinyk if (maxrate_p != NULL) 1067453645fSAndriy Voskoboinyk *maxrate_p = maxrate; 1077453645fSAndriy Voskoboinyk } 1087453645fSAndriy Voskoboinyk 1097453645fSAndriy Voskoboinyk void 1107453645fSAndriy Voskoboinyk rtwn_set_basicrates(struct rtwn_softc *sc, uint32_t rates) 1117453645fSAndriy Voskoboinyk { 1127453645fSAndriy Voskoboinyk 1137453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RA, "%s: rates 0x%08X\n", __func__, rates); 1147453645fSAndriy Voskoboinyk 1157453645fSAndriy Voskoboinyk rtwn_setbits_4(sc, R92C_RRSR, R92C_RRSR_RATE_BITMAP_M, rates); 1167453645fSAndriy Voskoboinyk } 1177453645fSAndriy Voskoboinyk 1187453645fSAndriy Voskoboinyk static void 11909606165SAndriy Voskoboinyk rtwn_update_avgrssi(struct rtwn_softc *sc, struct rtwn_node *un, int8_t rssi, 12009606165SAndriy Voskoboinyk int is_cck) 1217453645fSAndriy Voskoboinyk { 1227453645fSAndriy Voskoboinyk int pwdb; 1237453645fSAndriy Voskoboinyk 1247453645fSAndriy Voskoboinyk /* Convert antenna signal to percentage. */ 12509606165SAndriy Voskoboinyk if (rssi <= -100 || rssi >= 20) 1267453645fSAndriy Voskoboinyk pwdb = 0; 12709606165SAndriy Voskoboinyk else if (rssi >= 0) 1287453645fSAndriy Voskoboinyk pwdb = 100; 1297453645fSAndriy Voskoboinyk else 13009606165SAndriy Voskoboinyk pwdb = 100 + rssi; 13109606165SAndriy Voskoboinyk if (is_cck) { 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 15809606165SAndriy Voskoboinyk rtwn_get_rssi(struct rtwn_softc *sc, void *physt, int is_cck) 1597453645fSAndriy Voskoboinyk { 1607453645fSAndriy Voskoboinyk int8_t rssi; 1617453645fSAndriy Voskoboinyk 16209606165SAndriy Voskoboinyk if (is_cck) 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 19109606165SAndriy Voskoboinyk static uint64_t 192c5ad99fcSAndriy Voskoboinyk rtwn_extend_rx_tsf(struct rtwn_softc *sc, 193c5ad99fcSAndriy Voskoboinyk const struct rtwn_rx_stat_common *stat) 19409606165SAndriy Voskoboinyk { 19509606165SAndriy Voskoboinyk uint64_t tsft; 19609606165SAndriy Voskoboinyk uint32_t rxdw3, tsfl, tsfl_curr; 19709606165SAndriy Voskoboinyk int id; 19809606165SAndriy Voskoboinyk 19909606165SAndriy Voskoboinyk rxdw3 = le32toh(stat->rxdw3); 20009606165SAndriy Voskoboinyk tsfl = le32toh(stat->tsf_low); 201c5ad99fcSAndriy Voskoboinyk id = MS(rxdw3, RTWN_RXDW3_BSSID01_FIT); 20209606165SAndriy Voskoboinyk 20309606165SAndriy Voskoboinyk switch (id) { 20409606165SAndriy Voskoboinyk case 1: 20509606165SAndriy Voskoboinyk case 2: 20609606165SAndriy Voskoboinyk id >>= 1; 20709606165SAndriy Voskoboinyk tsfl_curr = rtwn_get_tsf_low(sc, id); 20809606165SAndriy Voskoboinyk break; 20909606165SAndriy Voskoboinyk default: 21009606165SAndriy Voskoboinyk { 21109606165SAndriy Voskoboinyk uint32_t tsfl0, tsfl1; 21209606165SAndriy Voskoboinyk 21309606165SAndriy Voskoboinyk tsfl0 = rtwn_get_tsf_low(sc, 0); 21409606165SAndriy Voskoboinyk tsfl1 = rtwn_get_tsf_low(sc, 1); 21509606165SAndriy Voskoboinyk 21609606165SAndriy Voskoboinyk if (abs(tsfl0 - tsfl) < abs(tsfl1 - tsfl)) { 21709606165SAndriy Voskoboinyk id = 0; 21809606165SAndriy Voskoboinyk tsfl_curr = tsfl0; 21909606165SAndriy Voskoboinyk } else { 22009606165SAndriy Voskoboinyk id = 1; 22109606165SAndriy Voskoboinyk tsfl_curr = tsfl1; 22209606165SAndriy Voskoboinyk } 22309606165SAndriy Voskoboinyk break; 22409606165SAndriy Voskoboinyk } 22509606165SAndriy Voskoboinyk } 22609606165SAndriy Voskoboinyk 22709606165SAndriy Voskoboinyk tsft = rtwn_get_tsf_high(sc, id); 22809606165SAndriy Voskoboinyk if (tsfl > tsfl_curr && tsfl > 0xffff0000) 22909606165SAndriy Voskoboinyk tsft--; 23009606165SAndriy Voskoboinyk tsft <<= 32; 23109606165SAndriy Voskoboinyk tsft += tsfl; 23209606165SAndriy Voskoboinyk 23309606165SAndriy Voskoboinyk return (tsft); 23409606165SAndriy Voskoboinyk } 23509606165SAndriy Voskoboinyk 2367453645fSAndriy Voskoboinyk struct ieee80211_node * 23709606165SAndriy Voskoboinyk rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc) 2387453645fSAndriy Voskoboinyk { 2397453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 2407453645fSAndriy Voskoboinyk struct ieee80211_node *ni; 2417453645fSAndriy Voskoboinyk struct ieee80211_frame_min *wh; 24209606165SAndriy Voskoboinyk struct ieee80211_rx_stats rxs; 2437453645fSAndriy Voskoboinyk struct rtwn_node *un; 244c5ad99fcSAndriy Voskoboinyk struct rtwn_rx_stat_common *stat; 24509606165SAndriy Voskoboinyk void *physt; 24609606165SAndriy Voskoboinyk uint32_t rxdw0; 24709606165SAndriy Voskoboinyk int8_t rssi; 24809606165SAndriy Voskoboinyk int cipher, infosz, is_cck, pktlen, shift; 2497453645fSAndriy Voskoboinyk 2507453645fSAndriy Voskoboinyk stat = desc; 2517453645fSAndriy Voskoboinyk rxdw0 = le32toh(stat->rxdw0); 2527453645fSAndriy Voskoboinyk 253c5ad99fcSAndriy Voskoboinyk cipher = MS(rxdw0, RTWN_RXDW0_CIPHER); 254c5ad99fcSAndriy Voskoboinyk infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8; 255c5ad99fcSAndriy Voskoboinyk pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN); 256c5ad99fcSAndriy Voskoboinyk shift = MS(rxdw0, RTWN_RXDW0_SHIFT); 2577453645fSAndriy Voskoboinyk 2587453645fSAndriy Voskoboinyk wh = (struct ieee80211_frame_min *)(mtodo(m, shift + infosz)); 2597453645fSAndriy Voskoboinyk if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && 2607453645fSAndriy Voskoboinyk cipher != R92C_CAM_ALGO_NONE) 2617453645fSAndriy Voskoboinyk m->m_flags |= M_WEP; 2627453645fSAndriy Voskoboinyk 26309606165SAndriy Voskoboinyk if (pktlen >= sizeof(*wh)) { 2647453645fSAndriy Voskoboinyk ni = ieee80211_find_rxnode(ic, wh); 26509606165SAndriy Voskoboinyk if (ni != NULL && (ni->ni_flags & IEEE80211_NODE_HT)) 26609606165SAndriy Voskoboinyk m->m_flags |= M_AMPDU; 26709606165SAndriy Voskoboinyk } else 2687453645fSAndriy Voskoboinyk ni = NULL; 2697453645fSAndriy Voskoboinyk un = RTWN_NODE(ni); 2707453645fSAndriy Voskoboinyk 271c5ad99fcSAndriy Voskoboinyk if (infosz != 0 && (rxdw0 & RTWN_RXDW0_PHYST)) 27209606165SAndriy Voskoboinyk physt = (void *)mtodo(m, shift); 27309606165SAndriy Voskoboinyk else 27409606165SAndriy Voskoboinyk physt = (un != NULL) ? &un->last_physt : &sc->last_physt; 2757453645fSAndriy Voskoboinyk 27609606165SAndriy Voskoboinyk bzero(&rxs, sizeof(rxs)); 27709606165SAndriy Voskoboinyk rtwn_get_rx_stats(sc, &rxs, desc, physt); 27809606165SAndriy Voskoboinyk if (rxs.c_pktflags & IEEE80211_RX_F_AMPDU) { 27909606165SAndriy Voskoboinyk /* Next MPDU will come without PHY info. */ 28009606165SAndriy Voskoboinyk memcpy(&sc->last_physt, physt, sizeof(sc->last_physt)); 28109606165SAndriy Voskoboinyk if (un != NULL) 28209606165SAndriy Voskoboinyk memcpy(&un->last_physt, physt, sizeof(sc->last_physt)); 2837453645fSAndriy Voskoboinyk } 28409606165SAndriy Voskoboinyk 28509606165SAndriy Voskoboinyk /* Add some common bits. */ 28609606165SAndriy Voskoboinyk /* NB: should not happen. */ 287c5ad99fcSAndriy Voskoboinyk if (rxdw0 & RTWN_RXDW0_CRCERR) 28809606165SAndriy Voskoboinyk rxs.c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC; 28909606165SAndriy Voskoboinyk 29009606165SAndriy Voskoboinyk rxs.r_flags |= IEEE80211_R_TSF_START; /* XXX undocumented */ 29109606165SAndriy Voskoboinyk rxs.r_flags |= IEEE80211_R_TSF64; 29209606165SAndriy Voskoboinyk rxs.c_rx_tsf = rtwn_extend_rx_tsf(sc, stat); 29309606165SAndriy Voskoboinyk 29409606165SAndriy Voskoboinyk /* Get RSSI from PHY status descriptor. */ 29509606165SAndriy Voskoboinyk is_cck = (rxs.c_pktflags & IEEE80211_RX_F_CCK) != 0; 29609606165SAndriy Voskoboinyk rssi = rtwn_get_rssi(sc, physt, is_cck); 29709606165SAndriy Voskoboinyk 29809606165SAndriy Voskoboinyk /* XXX TODO: we really need a rate-to-string method */ 29909606165SAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, "%s: rssi %d, rate %d\n", 30009606165SAndriy Voskoboinyk __func__, rssi, rxs.c_rate); 301c5ad99fcSAndriy Voskoboinyk if (un != NULL && infosz != 0 && (rxdw0 & RTWN_RXDW0_PHYST)) { 30209606165SAndriy Voskoboinyk /* Update our average RSSI. */ 30309606165SAndriy Voskoboinyk rtwn_update_avgrssi(sc, un, rssi, is_cck); 30409606165SAndriy Voskoboinyk } 30509606165SAndriy Voskoboinyk 30609606165SAndriy Voskoboinyk rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; 30709606165SAndriy Voskoboinyk rxs.c_nf = RTWN_NOISE_FLOOR; 30809606165SAndriy Voskoboinyk rxs.c_rssi = rssi - rxs.c_nf; 30909606165SAndriy Voskoboinyk (void) ieee80211_add_rx_params(m, &rxs); 3107453645fSAndriy Voskoboinyk 3117453645fSAndriy Voskoboinyk if (ieee80211_radiotap_active(ic)) { 3127453645fSAndriy Voskoboinyk struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap; 3137453645fSAndriy Voskoboinyk 3147453645fSAndriy Voskoboinyk tap->wr_flags = rtwn_rx_radiotap_flags(sc, desc); 31509606165SAndriy Voskoboinyk tap->wr_tsft = htole64(rxs.c_rx_tsf); 31609606165SAndriy Voskoboinyk tap->wr_rate = rxs.c_rate; 31709606165SAndriy Voskoboinyk tap->wr_dbm_antsignal = rssi; 31809606165SAndriy Voskoboinyk tap->wr_dbm_antnoise = rxs.c_nf; 3197453645fSAndriy Voskoboinyk } 3207453645fSAndriy Voskoboinyk 3217453645fSAndriy Voskoboinyk /* Drop PHY descriptor. */ 3227453645fSAndriy Voskoboinyk m_adj(m, infosz + shift); 3237453645fSAndriy Voskoboinyk 3247453645fSAndriy Voskoboinyk return (ni); 3257453645fSAndriy Voskoboinyk } 3267453645fSAndriy Voskoboinyk 3277453645fSAndriy Voskoboinyk void 3287453645fSAndriy Voskoboinyk rtwn_adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, 3297453645fSAndriy Voskoboinyk const struct ieee80211_rx_stats *rxs, 3307453645fSAndriy Voskoboinyk int rssi, int nf) 3317453645fSAndriy Voskoboinyk { 3327453645fSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap; 3337453645fSAndriy Voskoboinyk struct rtwn_softc *sc = vap->iv_ic->ic_softc; 3347453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 3357453645fSAndriy Voskoboinyk uint64_t ni_tstamp, curr_tstamp; 3367453645fSAndriy Voskoboinyk 3377453645fSAndriy Voskoboinyk uvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); 3387453645fSAndriy Voskoboinyk 3397453645fSAndriy Voskoboinyk if (vap->iv_state == IEEE80211_S_RUN && 3407453645fSAndriy Voskoboinyk (subtype == IEEE80211_FC0_SUBTYPE_BEACON || 3417453645fSAndriy Voskoboinyk subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { 3427453645fSAndriy Voskoboinyk ni_tstamp = le64toh(ni->ni_tstamp.tsf); 3437453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 3447453645fSAndriy Voskoboinyk rtwn_get_tsf(sc, &curr_tstamp, uvp->id); 3457453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 3467453645fSAndriy Voskoboinyk 3477453645fSAndriy Voskoboinyk if (ni_tstamp >= curr_tstamp) 3487453645fSAndriy Voskoboinyk (void) ieee80211_ibss_merge(ni); 3497453645fSAndriy Voskoboinyk } 3507453645fSAndriy Voskoboinyk } 3517453645fSAndriy Voskoboinyk 3527453645fSAndriy Voskoboinyk static uint8_t 3537453645fSAndriy Voskoboinyk rtwn_get_multi_pos(const uint8_t maddr[]) 3547453645fSAndriy Voskoboinyk { 3557453645fSAndriy Voskoboinyk uint64_t mask = 0x00004d101df481b4; 3567453645fSAndriy Voskoboinyk uint8_t pos = 0x27; /* initial value */ 3577453645fSAndriy Voskoboinyk int i, j; 3587453645fSAndriy Voskoboinyk 3597453645fSAndriy Voskoboinyk for (i = 0; i < IEEE80211_ADDR_LEN; i++) 3607453645fSAndriy Voskoboinyk for (j = (i == 0) ? 1 : 0; j < 8; j++) 3617453645fSAndriy Voskoboinyk if ((maddr[i] >> j) & 1) 3627453645fSAndriy Voskoboinyk pos ^= (mask >> (i * 8 + j - 1)); 3637453645fSAndriy Voskoboinyk 3647453645fSAndriy Voskoboinyk pos &= 0x3f; 3657453645fSAndriy Voskoboinyk 3667453645fSAndriy Voskoboinyk return (pos); 3677453645fSAndriy Voskoboinyk } 3687453645fSAndriy Voskoboinyk 3697453645fSAndriy Voskoboinyk void 3707453645fSAndriy Voskoboinyk rtwn_set_multi(struct rtwn_softc *sc) 3717453645fSAndriy Voskoboinyk { 3727453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 3737453645fSAndriy Voskoboinyk uint32_t mfilt[2]; 3747453645fSAndriy Voskoboinyk 3757453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 3767453645fSAndriy Voskoboinyk 3777453645fSAndriy Voskoboinyk /* general structure was copied from ath(4). */ 3787453645fSAndriy Voskoboinyk if (ic->ic_allmulti == 0) { 3797453645fSAndriy Voskoboinyk struct ieee80211vap *vap; 3807453645fSAndriy Voskoboinyk struct ifnet *ifp; 3817453645fSAndriy Voskoboinyk struct ifmultiaddr *ifma; 3827453645fSAndriy Voskoboinyk 3837453645fSAndriy Voskoboinyk /* 3847453645fSAndriy Voskoboinyk * Merge multicast addresses to form the hardware filter. 3857453645fSAndriy Voskoboinyk */ 3867453645fSAndriy Voskoboinyk mfilt[0] = mfilt[1] = 0; 3877453645fSAndriy Voskoboinyk TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 3887453645fSAndriy Voskoboinyk ifp = vap->iv_ifp; 3897453645fSAndriy Voskoboinyk if_maddr_rlock(ifp); 3907453645fSAndriy Voskoboinyk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 3917453645fSAndriy Voskoboinyk caddr_t dl; 3927453645fSAndriy Voskoboinyk uint8_t pos; 3937453645fSAndriy Voskoboinyk 3947453645fSAndriy Voskoboinyk dl = LLADDR((struct sockaddr_dl *) 3957453645fSAndriy Voskoboinyk ifma->ifma_addr); 3967453645fSAndriy Voskoboinyk pos = rtwn_get_multi_pos(dl); 3977453645fSAndriy Voskoboinyk 3987453645fSAndriy Voskoboinyk mfilt[pos / 32] |= (1 << (pos % 32)); 3997453645fSAndriy Voskoboinyk } 4007453645fSAndriy Voskoboinyk if_maddr_runlock(ifp); 4017453645fSAndriy Voskoboinyk } 4027453645fSAndriy Voskoboinyk } else 4037453645fSAndriy Voskoboinyk mfilt[0] = mfilt[1] = ~0; 4047453645fSAndriy Voskoboinyk 4057453645fSAndriy Voskoboinyk 4067453645fSAndriy Voskoboinyk rtwn_write_4(sc, R92C_MAR + 0, mfilt[0]); 4077453645fSAndriy Voskoboinyk rtwn_write_4(sc, R92C_MAR + 4, mfilt[1]); 4087453645fSAndriy Voskoboinyk 4097453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s: MC filter %08x:%08x\n", 4107453645fSAndriy Voskoboinyk __func__, mfilt[0], mfilt[1]); 4117453645fSAndriy Voskoboinyk } 4127453645fSAndriy Voskoboinyk 4137453645fSAndriy Voskoboinyk static void 4147453645fSAndriy Voskoboinyk rtwn_rxfilter_update_mgt(struct rtwn_softc *sc) 4157453645fSAndriy Voskoboinyk { 4167453645fSAndriy Voskoboinyk uint16_t filter; 4177453645fSAndriy Voskoboinyk 418c15d8692SAndriy Voskoboinyk filter = 0x7f7f; 4197453645fSAndriy Voskoboinyk if (sc->bcn_vaps == 0) { /* STA and/or MONITOR mode vaps */ 4207453645fSAndriy Voskoboinyk filter &= ~( 4217453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_REQ) | 4227453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_REQ) | 4237453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_PROBE_REQ)); 4247453645fSAndriy Voskoboinyk } 4257453645fSAndriy Voskoboinyk if (sc->ap_vaps == sc->nvaps - sc->mon_vaps) { /* AP vaps only */ 4267453645fSAndriy Voskoboinyk filter &= ~( 4277453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_RESP) | 4287453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_RESP)); 4297453645fSAndriy Voskoboinyk } 4307453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_RXFLTMAP0, filter); 4317453645fSAndriy Voskoboinyk } 4327453645fSAndriy Voskoboinyk 4337453645fSAndriy Voskoboinyk void 4347453645fSAndriy Voskoboinyk rtwn_rxfilter_update(struct rtwn_softc *sc) 4357453645fSAndriy Voskoboinyk { 4367453645fSAndriy Voskoboinyk 4377453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 4387453645fSAndriy Voskoboinyk 4397453645fSAndriy Voskoboinyk /* Filter for management frames. */ 4407453645fSAndriy Voskoboinyk rtwn_rxfilter_update_mgt(sc); 4417453645fSAndriy Voskoboinyk 4427453645fSAndriy Voskoboinyk /* Update Rx filter. */ 4437453645fSAndriy Voskoboinyk rtwn_set_promisc(sc); 4447453645fSAndriy Voskoboinyk } 4457453645fSAndriy Voskoboinyk 4467453645fSAndriy Voskoboinyk void 4477453645fSAndriy Voskoboinyk rtwn_rxfilter_init(struct rtwn_softc *sc) 4487453645fSAndriy Voskoboinyk { 4497453645fSAndriy Voskoboinyk 4507453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 4517453645fSAndriy Voskoboinyk 4527453645fSAndriy Voskoboinyk /* Setup multicast filter. */ 4537453645fSAndriy Voskoboinyk rtwn_set_multi(sc); 4547453645fSAndriy Voskoboinyk 4557453645fSAndriy Voskoboinyk /* Reject all control frames. */ 4567453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000); 4577453645fSAndriy Voskoboinyk 4587453645fSAndriy Voskoboinyk /* Reject all data frames. */ 4597453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_RXFLTMAP2, 0x0000); 4607453645fSAndriy Voskoboinyk 461c15d8692SAndriy Voskoboinyk /* Append generic Rx filter bits. */ 462c15d8692SAndriy Voskoboinyk sc->rcr |= R92C_RCR_AM | R92C_RCR_AB | R92C_RCR_APM | 4637453645fSAndriy Voskoboinyk R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS | 4647453645fSAndriy Voskoboinyk R92C_RCR_APP_ICV | R92C_RCR_APP_MIC; 4657453645fSAndriy Voskoboinyk 4667453645fSAndriy Voskoboinyk /* Update dynamic Rx filter parts. */ 4677453645fSAndriy Voskoboinyk rtwn_rxfilter_update(sc); 4687453645fSAndriy Voskoboinyk } 4697453645fSAndriy Voskoboinyk 4707453645fSAndriy Voskoboinyk void 471c15d8692SAndriy Voskoboinyk rtwn_rxfilter_set(struct rtwn_softc *sc) 472c15d8692SAndriy Voskoboinyk { 473c15d8692SAndriy Voskoboinyk if (!(sc->sc_flags & RTWN_RCR_LOCKED)) 474c15d8692SAndriy Voskoboinyk rtwn_write_4(sc, R92C_RCR, sc->rcr); 475c15d8692SAndriy Voskoboinyk } 476c15d8692SAndriy Voskoboinyk 477c15d8692SAndriy Voskoboinyk void 4787453645fSAndriy Voskoboinyk rtwn_set_rx_bssid_all(struct rtwn_softc *sc, int enable) 4797453645fSAndriy Voskoboinyk { 480c15d8692SAndriy Voskoboinyk 4817453645fSAndriy Voskoboinyk if (enable) 482c15d8692SAndriy Voskoboinyk sc->rcr &= ~R92C_RCR_CBSSID_BCN; 4837453645fSAndriy Voskoboinyk else 484c15d8692SAndriy Voskoboinyk sc->rcr |= R92C_RCR_CBSSID_BCN; 485c15d8692SAndriy Voskoboinyk rtwn_rxfilter_set(sc); 4867453645fSAndriy Voskoboinyk } 4877453645fSAndriy Voskoboinyk 4887453645fSAndriy Voskoboinyk void 4897453645fSAndriy Voskoboinyk rtwn_set_promisc(struct rtwn_softc *sc) 4907453645fSAndriy Voskoboinyk { 4917453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 492c15d8692SAndriy Voskoboinyk uint32_t mask_all, mask_min; 4937453645fSAndriy Voskoboinyk 4947453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 4957453645fSAndriy Voskoboinyk 496c15d8692SAndriy Voskoboinyk mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP; 497c15d8692SAndriy Voskoboinyk mask_min = R92C_RCR_APM; 4987453645fSAndriy Voskoboinyk 4997453645fSAndriy Voskoboinyk if (sc->bcn_vaps == 0) 500c15d8692SAndriy Voskoboinyk mask_min |= R92C_RCR_CBSSID_BCN; 5017453645fSAndriy Voskoboinyk if (sc->ap_vaps == 0) 502c15d8692SAndriy Voskoboinyk mask_min |= R92C_RCR_CBSSID_DATA; 5037453645fSAndriy Voskoboinyk 504c15d8692SAndriy Voskoboinyk if (ic->ic_promisc == 0 && sc->mon_vaps == 0) { 505c15d8692SAndriy Voskoboinyk if (sc->bcn_vaps != 0) 506c15d8692SAndriy Voskoboinyk mask_all |= R92C_RCR_CBSSID_BCN; 507c15d8692SAndriy Voskoboinyk if (sc->ap_vaps != 0) /* for Null data frames */ 508c15d8692SAndriy Voskoboinyk mask_all |= R92C_RCR_CBSSID_DATA; 509c15d8692SAndriy Voskoboinyk 510c15d8692SAndriy Voskoboinyk sc->rcr &= ~mask_all; 511c15d8692SAndriy Voskoboinyk sc->rcr |= mask_min; 512c15d8692SAndriy Voskoboinyk } else { 513c15d8692SAndriy Voskoboinyk sc->rcr &= ~mask_min; 514c15d8692SAndriy Voskoboinyk sc->rcr |= mask_all; 515c15d8692SAndriy Voskoboinyk } 516c15d8692SAndriy Voskoboinyk rtwn_rxfilter_set(sc); 5177453645fSAndriy Voskoboinyk } 518