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
557453645fSAndriy Voskoboinyk void
rtwn_get_rates(struct rtwn_softc * sc,const struct ieee80211_rateset * rs,const struct ieee80211_htrateset * rs_ht,uint32_t * rates_p,int * maxrate_p,int basic_rates)567453645fSAndriy Voskoboinyk rtwn_get_rates(struct rtwn_softc *sc, const struct ieee80211_rateset *rs,
577453645fSAndriy Voskoboinyk const struct ieee80211_htrateset *rs_ht, uint32_t *rates_p,
587453645fSAndriy Voskoboinyk int *maxrate_p, int basic_rates)
597453645fSAndriy Voskoboinyk {
607453645fSAndriy Voskoboinyk uint32_t rates;
617453645fSAndriy Voskoboinyk uint8_t ridx;
627453645fSAndriy Voskoboinyk int i, maxrate;
637453645fSAndriy Voskoboinyk
647453645fSAndriy Voskoboinyk /* Get rates mask. */
657453645fSAndriy Voskoboinyk rates = 0;
667453645fSAndriy Voskoboinyk maxrate = 0;
677453645fSAndriy Voskoboinyk
687453645fSAndriy Voskoboinyk /* This is for 11bg */
697453645fSAndriy Voskoboinyk for (i = 0; i < rs->rs_nrates; i++) {
707453645fSAndriy Voskoboinyk /* Convert 802.11 rate to HW rate index. */
717453645fSAndriy Voskoboinyk ridx = rate2ridx(IEEE80211_RV(rs->rs_rates[i]));
727453645fSAndriy Voskoboinyk if (ridx == RTWN_RIDX_UNKNOWN) /* Unknown rate, skip. */
737453645fSAndriy Voskoboinyk continue;
747453645fSAndriy Voskoboinyk if (((rs->rs_rates[i] & IEEE80211_RATE_BASIC) != 0) ||
757453645fSAndriy Voskoboinyk !basic_rates) {
767453645fSAndriy Voskoboinyk rates |= 1 << ridx;
777453645fSAndriy Voskoboinyk if (ridx > maxrate)
787453645fSAndriy Voskoboinyk maxrate = ridx;
797453645fSAndriy Voskoboinyk }
807453645fSAndriy Voskoboinyk }
817453645fSAndriy Voskoboinyk
827453645fSAndriy Voskoboinyk /* If we're doing 11n, enable 11n rates */
837453645fSAndriy Voskoboinyk if (rs_ht != NULL && !basic_rates) {
847453645fSAndriy Voskoboinyk for (i = 0; i < rs_ht->rs_nrates; i++) {
857453645fSAndriy Voskoboinyk if ((rs_ht->rs_rates[i] & 0x7f) > 0xf)
867453645fSAndriy Voskoboinyk continue;
877453645fSAndriy Voskoboinyk /* 11n rates start at index 12 */
880cc18edfSAndriy Voskoboinyk ridx = RTWN_RIDX_HT_MCS((rs_ht->rs_rates[i]) & 0xf);
897453645fSAndriy Voskoboinyk rates |= (1 << ridx);
907453645fSAndriy Voskoboinyk
917453645fSAndriy Voskoboinyk /* Guard against the rate table being oddly ordered */
927453645fSAndriy Voskoboinyk if (ridx > maxrate)
937453645fSAndriy Voskoboinyk maxrate = ridx;
947453645fSAndriy Voskoboinyk }
957453645fSAndriy Voskoboinyk }
967453645fSAndriy Voskoboinyk
977453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RA,
987453645fSAndriy Voskoboinyk "%s: rates 0x%08X, maxrate %d\n", __func__, rates, maxrate);
997453645fSAndriy Voskoboinyk
1007453645fSAndriy Voskoboinyk if (rates_p != NULL)
1017453645fSAndriy Voskoboinyk *rates_p = rates;
1027453645fSAndriy Voskoboinyk if (maxrate_p != NULL)
1037453645fSAndriy Voskoboinyk *maxrate_p = maxrate;
1047453645fSAndriy Voskoboinyk }
1057453645fSAndriy Voskoboinyk
1067453645fSAndriy Voskoboinyk void
rtwn_set_basicrates(struct rtwn_softc * sc,uint32_t rates)1077453645fSAndriy Voskoboinyk rtwn_set_basicrates(struct rtwn_softc *sc, uint32_t rates)
1087453645fSAndriy Voskoboinyk {
1097453645fSAndriy Voskoboinyk
1107453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RA, "%s: rates 0x%08X\n", __func__, rates);
1117453645fSAndriy Voskoboinyk
1127453645fSAndriy Voskoboinyk rtwn_setbits_4(sc, R92C_RRSR, R92C_RRSR_RATE_BITMAP_M, rates);
1137453645fSAndriy Voskoboinyk }
1147453645fSAndriy Voskoboinyk
1157453645fSAndriy Voskoboinyk static void
rtwn_update_avgrssi(struct rtwn_softc * sc,struct rtwn_node * un,int8_t rssi,int is_cck)11609606165SAndriy Voskoboinyk rtwn_update_avgrssi(struct rtwn_softc *sc, struct rtwn_node *un, int8_t rssi,
11709606165SAndriy Voskoboinyk int is_cck)
1187453645fSAndriy Voskoboinyk {
1197453645fSAndriy Voskoboinyk int pwdb;
1207453645fSAndriy Voskoboinyk
1217453645fSAndriy Voskoboinyk /* Convert antenna signal to percentage. */
12209606165SAndriy Voskoboinyk if (rssi <= -100 || rssi >= 20)
1237453645fSAndriy Voskoboinyk pwdb = 0;
12409606165SAndriy Voskoboinyk else if (rssi >= 0)
1257453645fSAndriy Voskoboinyk pwdb = 100;
1267453645fSAndriy Voskoboinyk else
12709606165SAndriy Voskoboinyk pwdb = 100 + rssi;
12809606165SAndriy Voskoboinyk if (is_cck) {
1297453645fSAndriy Voskoboinyk /* CCK gain is smaller than OFDM/MCS gain. */
1307453645fSAndriy Voskoboinyk pwdb += 6;
1317453645fSAndriy Voskoboinyk if (pwdb > 100)
1327453645fSAndriy Voskoboinyk pwdb = 100;
1337453645fSAndriy Voskoboinyk if (pwdb <= 14)
1347453645fSAndriy Voskoboinyk pwdb -= 4;
1357453645fSAndriy Voskoboinyk else if (pwdb <= 26)
1367453645fSAndriy Voskoboinyk pwdb -= 8;
1377453645fSAndriy Voskoboinyk else if (pwdb <= 34)
1387453645fSAndriy Voskoboinyk pwdb -= 6;
1397453645fSAndriy Voskoboinyk else if (pwdb <= 42)
1407453645fSAndriy Voskoboinyk pwdb -= 2;
1417453645fSAndriy Voskoboinyk }
1427453645fSAndriy Voskoboinyk
1437453645fSAndriy Voskoboinyk if (un->avg_pwdb == -1) /* Init. */
1447453645fSAndriy Voskoboinyk un->avg_pwdb = pwdb;
1457453645fSAndriy Voskoboinyk else if (un->avg_pwdb < pwdb)
1467453645fSAndriy Voskoboinyk un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20) + 1;
1477453645fSAndriy Voskoboinyk else
1487453645fSAndriy Voskoboinyk un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20);
1497453645fSAndriy Voskoboinyk
1507453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI,
1517453645fSAndriy Voskoboinyk "MACID %d, PWDB %d, EMA %d\n", un->id, pwdb, un->avg_pwdb);
1527453645fSAndriy Voskoboinyk }
1537453645fSAndriy Voskoboinyk
1547453645fSAndriy Voskoboinyk static int8_t
rtwn_get_rssi(struct rtwn_softc * sc,void * physt,int is_cck)15509606165SAndriy Voskoboinyk rtwn_get_rssi(struct rtwn_softc *sc, void *physt, int is_cck)
1567453645fSAndriy Voskoboinyk {
1577453645fSAndriy Voskoboinyk int8_t rssi;
1587453645fSAndriy Voskoboinyk
15909606165SAndriy Voskoboinyk if (is_cck)
1607453645fSAndriy Voskoboinyk rssi = rtwn_get_rssi_cck(sc, physt);
1617453645fSAndriy Voskoboinyk else /* OFDM/HT. */
1627453645fSAndriy Voskoboinyk rssi = rtwn_get_rssi_ofdm(sc, physt);
1637453645fSAndriy Voskoboinyk
1647453645fSAndriy Voskoboinyk return (rssi);
1657453645fSAndriy Voskoboinyk }
1667453645fSAndriy Voskoboinyk
1677453645fSAndriy Voskoboinyk static uint32_t
rtwn_get_tsf_low(struct rtwn_softc * sc,int id)1687453645fSAndriy Voskoboinyk rtwn_get_tsf_low(struct rtwn_softc *sc, int id)
1697453645fSAndriy Voskoboinyk {
1707453645fSAndriy Voskoboinyk return (rtwn_read_4(sc, R92C_TSFTR(id)));
1717453645fSAndriy Voskoboinyk }
1727453645fSAndriy Voskoboinyk
1737453645fSAndriy Voskoboinyk static uint32_t
rtwn_get_tsf_high(struct rtwn_softc * sc,int id)1747453645fSAndriy Voskoboinyk rtwn_get_tsf_high(struct rtwn_softc *sc, int id)
1757453645fSAndriy Voskoboinyk {
1767453645fSAndriy Voskoboinyk return (rtwn_read_4(sc, R92C_TSFTR(id) + 4));
1777453645fSAndriy Voskoboinyk }
1787453645fSAndriy Voskoboinyk
1797453645fSAndriy Voskoboinyk static void
rtwn_get_tsf(struct rtwn_softc * sc,uint64_t * buf,int id)1807453645fSAndriy Voskoboinyk rtwn_get_tsf(struct rtwn_softc *sc, uint64_t *buf, int id)
1817453645fSAndriy Voskoboinyk {
1827453645fSAndriy Voskoboinyk /* NB: we cannot read it at once. */
1837453645fSAndriy Voskoboinyk *buf = rtwn_get_tsf_high(sc, id);
1847453645fSAndriy Voskoboinyk *buf <<= 32;
1857453645fSAndriy Voskoboinyk *buf += rtwn_get_tsf_low(sc, id);
1867453645fSAndriy Voskoboinyk }
1877453645fSAndriy Voskoboinyk
18809606165SAndriy Voskoboinyk static uint64_t
rtwn_extend_rx_tsf(struct rtwn_softc * sc,const struct rtwn_rx_stat_common * stat)189c5ad99fcSAndriy Voskoboinyk rtwn_extend_rx_tsf(struct rtwn_softc *sc,
190c5ad99fcSAndriy Voskoboinyk const struct rtwn_rx_stat_common *stat)
19109606165SAndriy Voskoboinyk {
19209606165SAndriy Voskoboinyk uint64_t tsft;
19309606165SAndriy Voskoboinyk uint32_t rxdw3, tsfl, tsfl_curr;
19409606165SAndriy Voskoboinyk int id;
19509606165SAndriy Voskoboinyk
19609606165SAndriy Voskoboinyk rxdw3 = le32toh(stat->rxdw3);
19709606165SAndriy Voskoboinyk tsfl = le32toh(stat->tsf_low);
198c5ad99fcSAndriy Voskoboinyk id = MS(rxdw3, RTWN_RXDW3_BSSID01_FIT);
19909606165SAndriy Voskoboinyk
20009606165SAndriy Voskoboinyk switch (id) {
20109606165SAndriy Voskoboinyk case 1:
20209606165SAndriy Voskoboinyk case 2:
20309606165SAndriy Voskoboinyk id >>= 1;
20409606165SAndriy Voskoboinyk tsfl_curr = rtwn_get_tsf_low(sc, id);
20509606165SAndriy Voskoboinyk break;
20609606165SAndriy Voskoboinyk default:
20709606165SAndriy Voskoboinyk {
20809606165SAndriy Voskoboinyk uint32_t tsfl0, tsfl1;
20909606165SAndriy Voskoboinyk
21009606165SAndriy Voskoboinyk tsfl0 = rtwn_get_tsf_low(sc, 0);
21109606165SAndriy Voskoboinyk tsfl1 = rtwn_get_tsf_low(sc, 1);
21209606165SAndriy Voskoboinyk
21309606165SAndriy Voskoboinyk if (abs(tsfl0 - tsfl) < abs(tsfl1 - tsfl)) {
21409606165SAndriy Voskoboinyk id = 0;
21509606165SAndriy Voskoboinyk tsfl_curr = tsfl0;
21609606165SAndriy Voskoboinyk } else {
21709606165SAndriy Voskoboinyk id = 1;
21809606165SAndriy Voskoboinyk tsfl_curr = tsfl1;
21909606165SAndriy Voskoboinyk }
22009606165SAndriy Voskoboinyk break;
22109606165SAndriy Voskoboinyk }
22209606165SAndriy Voskoboinyk }
22309606165SAndriy Voskoboinyk
22409606165SAndriy Voskoboinyk tsft = rtwn_get_tsf_high(sc, id);
22509606165SAndriy Voskoboinyk if (tsfl > tsfl_curr && tsfl > 0xffff0000)
22609606165SAndriy Voskoboinyk tsft--;
22709606165SAndriy Voskoboinyk tsft <<= 32;
22809606165SAndriy Voskoboinyk tsft += tsfl;
22909606165SAndriy Voskoboinyk
23009606165SAndriy Voskoboinyk return (tsft);
23109606165SAndriy Voskoboinyk }
23209606165SAndriy Voskoboinyk
2337453645fSAndriy Voskoboinyk struct ieee80211_node *
rtwn_rx_common(struct rtwn_softc * sc,struct mbuf * m,void * desc)23409606165SAndriy Voskoboinyk rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc)
2357453645fSAndriy Voskoboinyk {
2367453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic;
2377453645fSAndriy Voskoboinyk struct ieee80211_node *ni;
2387453645fSAndriy Voskoboinyk struct ieee80211_frame_min *wh;
23909606165SAndriy Voskoboinyk struct ieee80211_rx_stats rxs;
2407453645fSAndriy Voskoboinyk struct rtwn_node *un;
241c5ad99fcSAndriy Voskoboinyk struct rtwn_rx_stat_common *stat;
24209606165SAndriy Voskoboinyk void *physt;
24309606165SAndriy Voskoboinyk uint32_t rxdw0;
24409606165SAndriy Voskoboinyk int8_t rssi;
24509606165SAndriy Voskoboinyk int cipher, infosz, is_cck, pktlen, shift;
2467453645fSAndriy Voskoboinyk
2477453645fSAndriy Voskoboinyk stat = desc;
2487453645fSAndriy Voskoboinyk rxdw0 = le32toh(stat->rxdw0);
2497453645fSAndriy Voskoboinyk
250c5ad99fcSAndriy Voskoboinyk cipher = MS(rxdw0, RTWN_RXDW0_CIPHER);
251c5ad99fcSAndriy Voskoboinyk infosz = MS(rxdw0, RTWN_RXDW0_INFOSZ) * 8;
252c5ad99fcSAndriy Voskoboinyk pktlen = MS(rxdw0, RTWN_RXDW0_PKTLEN);
253c5ad99fcSAndriy Voskoboinyk shift = MS(rxdw0, RTWN_RXDW0_SHIFT);
2547453645fSAndriy Voskoboinyk
2557453645fSAndriy Voskoboinyk wh = (struct ieee80211_frame_min *)(mtodo(m, shift + infosz));
2567453645fSAndriy Voskoboinyk if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
2577453645fSAndriy Voskoboinyk cipher != R92C_CAM_ALGO_NONE)
2587453645fSAndriy Voskoboinyk m->m_flags |= M_WEP;
2597453645fSAndriy Voskoboinyk
26009606165SAndriy Voskoboinyk if (pktlen >= sizeof(*wh)) {
2617453645fSAndriy Voskoboinyk ni = ieee80211_find_rxnode(ic, wh);
26209606165SAndriy Voskoboinyk if (ni != NULL && (ni->ni_flags & IEEE80211_NODE_HT))
26309606165SAndriy Voskoboinyk m->m_flags |= M_AMPDU;
26409606165SAndriy Voskoboinyk } else
2657453645fSAndriy Voskoboinyk ni = NULL;
2667453645fSAndriy Voskoboinyk un = RTWN_NODE(ni);
2677453645fSAndriy Voskoboinyk
268c5ad99fcSAndriy Voskoboinyk if (infosz != 0 && (rxdw0 & RTWN_RXDW0_PHYST))
26909606165SAndriy Voskoboinyk physt = (void *)mtodo(m, shift);
27009606165SAndriy Voskoboinyk else
27109606165SAndriy Voskoboinyk physt = (un != NULL) ? &un->last_physt : &sc->last_physt;
2727453645fSAndriy Voskoboinyk
27309606165SAndriy Voskoboinyk bzero(&rxs, sizeof(rxs));
27409606165SAndriy Voskoboinyk rtwn_get_rx_stats(sc, &rxs, desc, physt);
27509606165SAndriy Voskoboinyk if (rxs.c_pktflags & IEEE80211_RX_F_AMPDU) {
27609606165SAndriy Voskoboinyk /* Next MPDU will come without PHY info. */
27709606165SAndriy Voskoboinyk memcpy(&sc->last_physt, physt, sizeof(sc->last_physt));
27809606165SAndriy Voskoboinyk if (un != NULL)
27909606165SAndriy Voskoboinyk memcpy(&un->last_physt, physt, sizeof(sc->last_physt));
2807453645fSAndriy Voskoboinyk }
28109606165SAndriy Voskoboinyk
28209606165SAndriy Voskoboinyk /* Add some common bits. */
28309606165SAndriy Voskoboinyk /* NB: should not happen. */
284c5ad99fcSAndriy Voskoboinyk if (rxdw0 & RTWN_RXDW0_CRCERR)
28509606165SAndriy Voskoboinyk rxs.c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC;
28609606165SAndriy Voskoboinyk
28709606165SAndriy Voskoboinyk rxs.r_flags |= IEEE80211_R_TSF_START; /* XXX undocumented */
28809606165SAndriy Voskoboinyk rxs.r_flags |= IEEE80211_R_TSF64;
28909606165SAndriy Voskoboinyk rxs.c_rx_tsf = rtwn_extend_rx_tsf(sc, stat);
29009606165SAndriy Voskoboinyk
29109606165SAndriy Voskoboinyk /* Get RSSI from PHY status descriptor. */
29209606165SAndriy Voskoboinyk is_cck = (rxs.c_pktflags & IEEE80211_RX_F_CCK) != 0;
29309606165SAndriy Voskoboinyk rssi = rtwn_get_rssi(sc, physt, is_cck);
29409606165SAndriy Voskoboinyk
29509606165SAndriy Voskoboinyk /* XXX TODO: we really need a rate-to-string method */
29609606165SAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, "%s: rssi %d, rate %d\n",
29709606165SAndriy Voskoboinyk __func__, rssi, rxs.c_rate);
298c5ad99fcSAndriy Voskoboinyk if (un != NULL && infosz != 0 && (rxdw0 & RTWN_RXDW0_PHYST)) {
29909606165SAndriy Voskoboinyk /* Update our average RSSI. */
30009606165SAndriy Voskoboinyk rtwn_update_avgrssi(sc, un, rssi, is_cck);
30109606165SAndriy Voskoboinyk }
30209606165SAndriy Voskoboinyk
30309606165SAndriy Voskoboinyk rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
30409606165SAndriy Voskoboinyk rxs.c_nf = RTWN_NOISE_FLOOR;
30509606165SAndriy Voskoboinyk rxs.c_rssi = rssi - rxs.c_nf;
30609606165SAndriy Voskoboinyk (void) ieee80211_add_rx_params(m, &rxs);
3077453645fSAndriy Voskoboinyk
3087453645fSAndriy Voskoboinyk if (ieee80211_radiotap_active(ic)) {
3097453645fSAndriy Voskoboinyk struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap;
3107453645fSAndriy Voskoboinyk
3117453645fSAndriy Voskoboinyk tap->wr_flags = rtwn_rx_radiotap_flags(sc, desc);
31209606165SAndriy Voskoboinyk tap->wr_tsft = htole64(rxs.c_rx_tsf);
31309606165SAndriy Voskoboinyk tap->wr_rate = rxs.c_rate;
31409606165SAndriy Voskoboinyk tap->wr_dbm_antsignal = rssi;
31509606165SAndriy Voskoboinyk tap->wr_dbm_antnoise = rxs.c_nf;
3167453645fSAndriy Voskoboinyk }
3177453645fSAndriy Voskoboinyk
3187453645fSAndriy Voskoboinyk /* Drop PHY descriptor. */
3197453645fSAndriy Voskoboinyk m_adj(m, infosz + shift);
3207453645fSAndriy Voskoboinyk
3217453645fSAndriy Voskoboinyk return (ni);
3227453645fSAndriy Voskoboinyk }
3237453645fSAndriy Voskoboinyk
3247453645fSAndriy Voskoboinyk void
rtwn_adhoc_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)3257453645fSAndriy Voskoboinyk rtwn_adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
3267453645fSAndriy Voskoboinyk const struct ieee80211_rx_stats *rxs,
3277453645fSAndriy Voskoboinyk int rssi, int nf)
3287453645fSAndriy Voskoboinyk {
3297453645fSAndriy Voskoboinyk struct ieee80211vap *vap = ni->ni_vap;
3307453645fSAndriy Voskoboinyk struct rtwn_softc *sc = vap->iv_ic->ic_softc;
3317453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap);
3327453645fSAndriy Voskoboinyk uint64_t ni_tstamp, curr_tstamp;
3337453645fSAndriy Voskoboinyk
3347453645fSAndriy Voskoboinyk uvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);
3357453645fSAndriy Voskoboinyk
3367453645fSAndriy Voskoboinyk if (vap->iv_state == IEEE80211_S_RUN &&
3377453645fSAndriy Voskoboinyk (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
3387453645fSAndriy Voskoboinyk subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
3397453645fSAndriy Voskoboinyk ni_tstamp = le64toh(ni->ni_tstamp.tsf);
3407453645fSAndriy Voskoboinyk RTWN_LOCK(sc);
3417453645fSAndriy Voskoboinyk rtwn_get_tsf(sc, &curr_tstamp, uvp->id);
3427453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc);
3437453645fSAndriy Voskoboinyk
3447453645fSAndriy Voskoboinyk if (ni_tstamp >= curr_tstamp)
3457453645fSAndriy Voskoboinyk (void) ieee80211_ibss_merge(ni);
3467453645fSAndriy Voskoboinyk }
3477453645fSAndriy Voskoboinyk }
3487453645fSAndriy Voskoboinyk
3497453645fSAndriy Voskoboinyk static uint8_t
rtwn_get_multi_pos(const uint8_t maddr[])3507453645fSAndriy Voskoboinyk rtwn_get_multi_pos(const uint8_t maddr[])
3517453645fSAndriy Voskoboinyk {
3527453645fSAndriy Voskoboinyk uint64_t mask = 0x00004d101df481b4;
3537453645fSAndriy Voskoboinyk uint8_t pos = 0x27; /* initial value */
3547453645fSAndriy Voskoboinyk int i, j;
3557453645fSAndriy Voskoboinyk
3567453645fSAndriy Voskoboinyk for (i = 0; i < IEEE80211_ADDR_LEN; i++)
3577453645fSAndriy Voskoboinyk for (j = (i == 0) ? 1 : 0; j < 8; j++)
3587453645fSAndriy Voskoboinyk if ((maddr[i] >> j) & 1)
3597453645fSAndriy Voskoboinyk pos ^= (mask >> (i * 8 + j - 1));
3607453645fSAndriy Voskoboinyk
3617453645fSAndriy Voskoboinyk pos &= 0x3f;
3627453645fSAndriy Voskoboinyk
3637453645fSAndriy Voskoboinyk return (pos);
3647453645fSAndriy Voskoboinyk }
3657453645fSAndriy Voskoboinyk
366*bc0bdf25SGleb Smirnoff static u_int
rtwm_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)367*bc0bdf25SGleb Smirnoff rtwm_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
368*bc0bdf25SGleb Smirnoff {
369*bc0bdf25SGleb Smirnoff uint32_t *mfilt = arg;
370*bc0bdf25SGleb Smirnoff uint8_t pos;
371*bc0bdf25SGleb Smirnoff
372*bc0bdf25SGleb Smirnoff pos = rtwn_get_multi_pos(LLADDR(sdl));
373*bc0bdf25SGleb Smirnoff mfilt[pos / 32] |= (1 << (pos % 32));
374*bc0bdf25SGleb Smirnoff
375*bc0bdf25SGleb Smirnoff return (1);
376*bc0bdf25SGleb Smirnoff }
377*bc0bdf25SGleb Smirnoff
3787453645fSAndriy Voskoboinyk void
rtwn_set_multi(struct rtwn_softc * sc)3797453645fSAndriy Voskoboinyk rtwn_set_multi(struct rtwn_softc *sc)
3807453645fSAndriy Voskoboinyk {
3817453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic;
3827453645fSAndriy Voskoboinyk uint32_t mfilt[2];
3837453645fSAndriy Voskoboinyk
3847453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc);
3857453645fSAndriy Voskoboinyk
3867453645fSAndriy Voskoboinyk /* general structure was copied from ath(4). */
3877453645fSAndriy Voskoboinyk if (ic->ic_allmulti == 0) {
3887453645fSAndriy Voskoboinyk struct ieee80211vap *vap;
3897453645fSAndriy Voskoboinyk
3907453645fSAndriy Voskoboinyk /*
3917453645fSAndriy Voskoboinyk * Merge multicast addresses to form the hardware filter.
3927453645fSAndriy Voskoboinyk */
3937453645fSAndriy Voskoboinyk mfilt[0] = mfilt[1] = 0;
394*bc0bdf25SGleb Smirnoff TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
395*bc0bdf25SGleb Smirnoff if_foreach_llmaddr(vap->iv_ifp, rtwm_hash_maddr, mfilt);
3967453645fSAndriy Voskoboinyk } else
3977453645fSAndriy Voskoboinyk mfilt[0] = mfilt[1] = ~0;
3987453645fSAndriy Voskoboinyk
3997453645fSAndriy Voskoboinyk rtwn_write_4(sc, R92C_MAR + 0, mfilt[0]);
4007453645fSAndriy Voskoboinyk rtwn_write_4(sc, R92C_MAR + 4, mfilt[1]);
4017453645fSAndriy Voskoboinyk
4027453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s: MC filter %08x:%08x\n",
4037453645fSAndriy Voskoboinyk __func__, mfilt[0], mfilt[1]);
4047453645fSAndriy Voskoboinyk }
4057453645fSAndriy Voskoboinyk
4067453645fSAndriy Voskoboinyk static void
rtwn_rxfilter_update_mgt(struct rtwn_softc * sc)4077453645fSAndriy Voskoboinyk rtwn_rxfilter_update_mgt(struct rtwn_softc *sc)
4087453645fSAndriy Voskoboinyk {
4097453645fSAndriy Voskoboinyk uint16_t filter;
4107453645fSAndriy Voskoboinyk
411c15d8692SAndriy Voskoboinyk filter = 0x7f7f;
4127453645fSAndriy Voskoboinyk if (sc->bcn_vaps == 0) { /* STA and/or MONITOR mode vaps */
4137453645fSAndriy Voskoboinyk filter &= ~(
4147453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_REQ) |
4157453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_REQ) |
4167453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_PROBE_REQ));
4177453645fSAndriy Voskoboinyk }
4187453645fSAndriy Voskoboinyk if (sc->ap_vaps == sc->nvaps - sc->mon_vaps) { /* AP vaps only */
4197453645fSAndriy Voskoboinyk filter &= ~(
4207453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_RESP) |
4217453645fSAndriy Voskoboinyk R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_RESP));
4227453645fSAndriy Voskoboinyk }
4237453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_RXFLTMAP0, filter);
4247453645fSAndriy Voskoboinyk }
4257453645fSAndriy Voskoboinyk
4267453645fSAndriy Voskoboinyk void
rtwn_rxfilter_update(struct rtwn_softc * sc)4277453645fSAndriy Voskoboinyk rtwn_rxfilter_update(struct rtwn_softc *sc)
4287453645fSAndriy Voskoboinyk {
4297453645fSAndriy Voskoboinyk
4307453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc);
4317453645fSAndriy Voskoboinyk
4327453645fSAndriy Voskoboinyk /* Filter for management frames. */
4337453645fSAndriy Voskoboinyk rtwn_rxfilter_update_mgt(sc);
4347453645fSAndriy Voskoboinyk
4357453645fSAndriy Voskoboinyk /* Update Rx filter. */
4367453645fSAndriy Voskoboinyk rtwn_set_promisc(sc);
4377453645fSAndriy Voskoboinyk }
4387453645fSAndriy Voskoboinyk
4397453645fSAndriy Voskoboinyk void
rtwn_rxfilter_init(struct rtwn_softc * sc)4407453645fSAndriy Voskoboinyk rtwn_rxfilter_init(struct rtwn_softc *sc)
4417453645fSAndriy Voskoboinyk {
4427453645fSAndriy Voskoboinyk
4437453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc);
4447453645fSAndriy Voskoboinyk
4457453645fSAndriy Voskoboinyk /* Setup multicast filter. */
4467453645fSAndriy Voskoboinyk rtwn_set_multi(sc);
4477453645fSAndriy Voskoboinyk
4487453645fSAndriy Voskoboinyk /* Reject all control frames. */
4497453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000);
4507453645fSAndriy Voskoboinyk
4517453645fSAndriy Voskoboinyk /* Reject all data frames. */
4527453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_RXFLTMAP2, 0x0000);
4537453645fSAndriy Voskoboinyk
454c15d8692SAndriy Voskoboinyk /* Append generic Rx filter bits. */
455c15d8692SAndriy Voskoboinyk sc->rcr |= R92C_RCR_AM | R92C_RCR_AB | R92C_RCR_APM |
4567453645fSAndriy Voskoboinyk R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS |
4577453645fSAndriy Voskoboinyk R92C_RCR_APP_ICV | R92C_RCR_APP_MIC;
4587453645fSAndriy Voskoboinyk
4597453645fSAndriy Voskoboinyk /* Update dynamic Rx filter parts. */
4607453645fSAndriy Voskoboinyk rtwn_rxfilter_update(sc);
4617453645fSAndriy Voskoboinyk }
4627453645fSAndriy Voskoboinyk
4637453645fSAndriy Voskoboinyk void
rtwn_rxfilter_set(struct rtwn_softc * sc)464c15d8692SAndriy Voskoboinyk rtwn_rxfilter_set(struct rtwn_softc *sc)
465c15d8692SAndriy Voskoboinyk {
466c15d8692SAndriy Voskoboinyk if (!(sc->sc_flags & RTWN_RCR_LOCKED))
467c15d8692SAndriy Voskoboinyk rtwn_write_4(sc, R92C_RCR, sc->rcr);
468c15d8692SAndriy Voskoboinyk }
469c15d8692SAndriy Voskoboinyk
470c15d8692SAndriy Voskoboinyk void
rtwn_set_rx_bssid_all(struct rtwn_softc * sc,int enable)4717453645fSAndriy Voskoboinyk rtwn_set_rx_bssid_all(struct rtwn_softc *sc, int enable)
4727453645fSAndriy Voskoboinyk {
473c15d8692SAndriy Voskoboinyk
4747453645fSAndriy Voskoboinyk if (enable)
475c15d8692SAndriy Voskoboinyk sc->rcr &= ~R92C_RCR_CBSSID_BCN;
4767453645fSAndriy Voskoboinyk else
477c15d8692SAndriy Voskoboinyk sc->rcr |= R92C_RCR_CBSSID_BCN;
478c15d8692SAndriy Voskoboinyk rtwn_rxfilter_set(sc);
4797453645fSAndriy Voskoboinyk }
4807453645fSAndriy Voskoboinyk
4817453645fSAndriy Voskoboinyk void
rtwn_set_promisc(struct rtwn_softc * sc)4827453645fSAndriy Voskoboinyk rtwn_set_promisc(struct rtwn_softc *sc)
4837453645fSAndriy Voskoboinyk {
4847453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic;
485c15d8692SAndriy Voskoboinyk uint32_t mask_all, mask_min;
4867453645fSAndriy Voskoboinyk
4877453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc);
4887453645fSAndriy Voskoboinyk
489c15d8692SAndriy Voskoboinyk mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP;
490c15d8692SAndriy Voskoboinyk mask_min = R92C_RCR_APM;
4917453645fSAndriy Voskoboinyk
4927453645fSAndriy Voskoboinyk if (sc->bcn_vaps == 0)
493c15d8692SAndriy Voskoboinyk mask_min |= R92C_RCR_CBSSID_BCN;
4947453645fSAndriy Voskoboinyk if (sc->ap_vaps == 0)
495c15d8692SAndriy Voskoboinyk mask_min |= R92C_RCR_CBSSID_DATA;
4967453645fSAndriy Voskoboinyk
497c15d8692SAndriy Voskoboinyk if (ic->ic_promisc == 0 && sc->mon_vaps == 0) {
498c15d8692SAndriy Voskoboinyk if (sc->bcn_vaps != 0)
499c15d8692SAndriy Voskoboinyk mask_all |= R92C_RCR_CBSSID_BCN;
500c15d8692SAndriy Voskoboinyk if (sc->ap_vaps != 0) /* for Null data frames */
501c15d8692SAndriy Voskoboinyk mask_all |= R92C_RCR_CBSSID_DATA;
502c15d8692SAndriy Voskoboinyk
503c15d8692SAndriy Voskoboinyk sc->rcr &= ~mask_all;
504c15d8692SAndriy Voskoboinyk sc->rcr |= mask_min;
505c15d8692SAndriy Voskoboinyk } else {
506c15d8692SAndriy Voskoboinyk sc->rcr &= ~mask_min;
507c15d8692SAndriy Voskoboinyk sc->rcr |= mask_all;
508c15d8692SAndriy Voskoboinyk }
509c15d8692SAndriy Voskoboinyk rtwn_rxfilter_set(sc);
5107453645fSAndriy Voskoboinyk }
511