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