1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ |
---|
4 | |
---|
5 | /*- |
---|
6 | * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> |
---|
7 | * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org> |
---|
8 | * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org> |
---|
9 | * |
---|
10 | * Permission to use, copy, modify, and distribute this software for any |
---|
11 | * purpose with or without fee is hereby granted, provided that the above |
---|
12 | * copyright notice and this permission notice appear in all copies. |
---|
13 | * |
---|
14 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
---|
15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
---|
16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
---|
17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
---|
19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
---|
20 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
21 | */ |
---|
22 | |
---|
23 | #include <sys/cdefs.h> |
---|
24 | __FBSDID("$FreeBSD$"); |
---|
25 | |
---|
26 | #include <rtems/bsd/local/opt_wlan.h> |
---|
27 | |
---|
28 | #include <rtems/bsd/sys/param.h> |
---|
29 | #include <rtems/bsd/sys/lock.h> |
---|
30 | #include <sys/mutex.h> |
---|
31 | #include <sys/mbuf.h> |
---|
32 | #include <sys/kernel.h> |
---|
33 | #include <sys/socket.h> |
---|
34 | #include <sys/systm.h> |
---|
35 | #include <sys/malloc.h> |
---|
36 | #include <sys/queue.h> |
---|
37 | #include <sys/taskqueue.h> |
---|
38 | #include <sys/bus.h> |
---|
39 | #include <sys/endian.h> |
---|
40 | |
---|
41 | #include <net/if.h> |
---|
42 | #include <net/if_var.h> |
---|
43 | #include <net/ethernet.h> |
---|
44 | #include <net/if_dl.h> |
---|
45 | #include <net/if_media.h> |
---|
46 | |
---|
47 | #include <net80211/ieee80211_var.h> |
---|
48 | #include <net80211/ieee80211_radiotap.h> |
---|
49 | |
---|
50 | #include <dev/rtwn/if_rtwnreg.h> |
---|
51 | #include <dev/rtwn/if_rtwnvar.h> |
---|
52 | |
---|
53 | #include <dev/rtwn/if_rtwn_debug.h> |
---|
54 | #include <dev/rtwn/if_rtwn_ridx.h> |
---|
55 | #include <dev/rtwn/if_rtwn_rx.h> |
---|
56 | |
---|
57 | #include <dev/rtwn/rtl8192c/r92c_reg.h> |
---|
58 | #include <dev/rtwn/rtl8192c/r92c_rx_desc.h> |
---|
59 | |
---|
60 | |
---|
61 | void |
---|
62 | rtwn_get_rates(struct rtwn_softc *sc, const struct ieee80211_rateset *rs, |
---|
63 | const struct ieee80211_htrateset *rs_ht, uint32_t *rates_p, |
---|
64 | int *maxrate_p, int basic_rates) |
---|
65 | { |
---|
66 | uint32_t rates; |
---|
67 | uint8_t ridx; |
---|
68 | int i, maxrate; |
---|
69 | |
---|
70 | /* Get rates mask. */ |
---|
71 | rates = 0; |
---|
72 | maxrate = 0; |
---|
73 | |
---|
74 | /* This is for 11bg */ |
---|
75 | for (i = 0; i < rs->rs_nrates; i++) { |
---|
76 | /* Convert 802.11 rate to HW rate index. */ |
---|
77 | ridx = rate2ridx(IEEE80211_RV(rs->rs_rates[i])); |
---|
78 | if (ridx == RTWN_RIDX_UNKNOWN) /* Unknown rate, skip. */ |
---|
79 | continue; |
---|
80 | if (((rs->rs_rates[i] & IEEE80211_RATE_BASIC) != 0) || |
---|
81 | !basic_rates) { |
---|
82 | rates |= 1 << ridx; |
---|
83 | if (ridx > maxrate) |
---|
84 | maxrate = ridx; |
---|
85 | } |
---|
86 | } |
---|
87 | |
---|
88 | /* If we're doing 11n, enable 11n rates */ |
---|
89 | if (rs_ht != NULL && !basic_rates) { |
---|
90 | for (i = 0; i < rs_ht->rs_nrates; i++) { |
---|
91 | if ((rs_ht->rs_rates[i] & 0x7f) > 0xf) |
---|
92 | continue; |
---|
93 | /* 11n rates start at index 12 */ |
---|
94 | ridx = RTWN_RIDX_MCS((rs_ht->rs_rates[i]) & 0xf); |
---|
95 | rates |= (1 << ridx); |
---|
96 | |
---|
97 | /* Guard against the rate table being oddly ordered */ |
---|
98 | if (ridx > maxrate) |
---|
99 | maxrate = ridx; |
---|
100 | } |
---|
101 | } |
---|
102 | |
---|
103 | RTWN_DPRINTF(sc, RTWN_DEBUG_RA, |
---|
104 | "%s: rates 0x%08X, maxrate %d\n", __func__, rates, maxrate); |
---|
105 | |
---|
106 | if (rates_p != NULL) |
---|
107 | *rates_p = rates; |
---|
108 | if (maxrate_p != NULL) |
---|
109 | *maxrate_p = maxrate; |
---|
110 | } |
---|
111 | |
---|
112 | void |
---|
113 | rtwn_set_basicrates(struct rtwn_softc *sc, uint32_t rates) |
---|
114 | { |
---|
115 | |
---|
116 | RTWN_DPRINTF(sc, RTWN_DEBUG_RA, "%s: rates 0x%08X\n", __func__, rates); |
---|
117 | |
---|
118 | rtwn_setbits_4(sc, R92C_RRSR, R92C_RRSR_RATE_BITMAP_M, rates); |
---|
119 | } |
---|
120 | |
---|
121 | static void |
---|
122 | rtwn_update_avgrssi(struct rtwn_softc *sc, struct rtwn_node *un, int rate) |
---|
123 | { |
---|
124 | int pwdb; |
---|
125 | |
---|
126 | /* Convert antenna signal to percentage. */ |
---|
127 | if (un->last_rssi <= -100 || un->last_rssi >= 20) |
---|
128 | pwdb = 0; |
---|
129 | else if (un->last_rssi >= 0) |
---|
130 | pwdb = 100; |
---|
131 | else |
---|
132 | pwdb = 100 + un->last_rssi; |
---|
133 | if (RTWN_RATE_IS_CCK(rate)) { |
---|
134 | /* CCK gain is smaller than OFDM/MCS gain. */ |
---|
135 | pwdb += 6; |
---|
136 | if (pwdb > 100) |
---|
137 | pwdb = 100; |
---|
138 | if (pwdb <= 14) |
---|
139 | pwdb -= 4; |
---|
140 | else if (pwdb <= 26) |
---|
141 | pwdb -= 8; |
---|
142 | else if (pwdb <= 34) |
---|
143 | pwdb -= 6; |
---|
144 | else if (pwdb <= 42) |
---|
145 | pwdb -= 2; |
---|
146 | } |
---|
147 | |
---|
148 | if (un->avg_pwdb == -1) /* Init. */ |
---|
149 | un->avg_pwdb = pwdb; |
---|
150 | else if (un->avg_pwdb < pwdb) |
---|
151 | un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20) + 1; |
---|
152 | else |
---|
153 | un->avg_pwdb = ((un->avg_pwdb * 19 + pwdb) / 20); |
---|
154 | |
---|
155 | RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, |
---|
156 | "MACID %d, PWDB %d, EMA %d\n", un->id, pwdb, un->avg_pwdb); |
---|
157 | } |
---|
158 | |
---|
159 | static int8_t |
---|
160 | rtwn_get_rssi(struct rtwn_softc *sc, int rate, void *physt) |
---|
161 | { |
---|
162 | int8_t rssi; |
---|
163 | |
---|
164 | if (RTWN_RATE_IS_CCK(rate)) |
---|
165 | rssi = rtwn_get_rssi_cck(sc, physt); |
---|
166 | else /* OFDM/HT. */ |
---|
167 | rssi = rtwn_get_rssi_ofdm(sc, physt); |
---|
168 | |
---|
169 | return (rssi); |
---|
170 | } |
---|
171 | |
---|
172 | static uint32_t |
---|
173 | rtwn_get_tsf_low(struct rtwn_softc *sc, int id) |
---|
174 | { |
---|
175 | return (rtwn_read_4(sc, R92C_TSFTR(id))); |
---|
176 | } |
---|
177 | |
---|
178 | static uint32_t |
---|
179 | rtwn_get_tsf_high(struct rtwn_softc *sc, int id) |
---|
180 | { |
---|
181 | return (rtwn_read_4(sc, R92C_TSFTR(id) + 4)); |
---|
182 | } |
---|
183 | |
---|
184 | static void |
---|
185 | rtwn_get_tsf(struct rtwn_softc *sc, uint64_t *buf, int id) |
---|
186 | { |
---|
187 | /* NB: we cannot read it at once. */ |
---|
188 | *buf = rtwn_get_tsf_high(sc, id); |
---|
189 | *buf <<= 32; |
---|
190 | *buf += rtwn_get_tsf_low(sc, id); |
---|
191 | } |
---|
192 | |
---|
193 | struct ieee80211_node * |
---|
194 | rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc, |
---|
195 | int8_t *rssi) |
---|
196 | { |
---|
197 | struct ieee80211com *ic = &sc->sc_ic; |
---|
198 | struct ieee80211_node *ni; |
---|
199 | struct ieee80211_frame_min *wh; |
---|
200 | struct rtwn_node *un; |
---|
201 | struct r92c_rx_stat *stat; |
---|
202 | uint32_t rxdw0, rxdw3; |
---|
203 | int cipher, infosz, pktlen, rate, shift; |
---|
204 | |
---|
205 | stat = desc; |
---|
206 | rxdw0 = le32toh(stat->rxdw0); |
---|
207 | rxdw3 = le32toh(stat->rxdw3); |
---|
208 | |
---|
209 | cipher = MS(rxdw0, R92C_RXDW0_CIPHER); |
---|
210 | infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; |
---|
211 | pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); |
---|
212 | shift = MS(rxdw0, R92C_RXDW0_SHIFT); |
---|
213 | rate = MS(rxdw3, R92C_RXDW3_RATE); |
---|
214 | |
---|
215 | wh = (struct ieee80211_frame_min *)(mtodo(m, shift + infosz)); |
---|
216 | if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && |
---|
217 | cipher != R92C_CAM_ALGO_NONE) |
---|
218 | m->m_flags |= M_WEP; |
---|
219 | |
---|
220 | if (pktlen >= sizeof(*wh)) |
---|
221 | ni = ieee80211_find_rxnode(ic, wh); |
---|
222 | else |
---|
223 | ni = NULL; |
---|
224 | un = RTWN_NODE(ni); |
---|
225 | |
---|
226 | /* Get RSSI from PHY status descriptor if present. */ |
---|
227 | if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) { |
---|
228 | *rssi = rtwn_get_rssi(sc, rate, mtod(m, void *)); |
---|
229 | RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, "%s: rssi %d, ridx %d\n", |
---|
230 | __func__, *rssi, rate); |
---|
231 | |
---|
232 | sc->last_rssi = *rssi; |
---|
233 | if (un != NULL) { |
---|
234 | un->last_rssi = *rssi; |
---|
235 | |
---|
236 | /* Update our average RSSI. */ |
---|
237 | rtwn_update_avgrssi(sc, un, rate); |
---|
238 | } |
---|
239 | } else |
---|
240 | *rssi = (un != NULL) ? un->last_rssi : sc->last_rssi; |
---|
241 | |
---|
242 | if (ieee80211_radiotap_active(ic)) { |
---|
243 | struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap; |
---|
244 | int id = RTWN_VAP_ID_INVALID; |
---|
245 | |
---|
246 | if (ni != NULL) |
---|
247 | id = RTWN_VAP(ni->ni_vap)->id; |
---|
248 | if (id == RTWN_VAP_ID_INVALID) |
---|
249 | id = 0; |
---|
250 | |
---|
251 | tap->wr_flags = rtwn_rx_radiotap_flags(sc, desc); |
---|
252 | tap->wr_tsft = rtwn_get_tsf_high(sc, id); |
---|
253 | if (le32toh(stat->tsf_low) > rtwn_get_tsf_low(sc, id)) |
---|
254 | tap->wr_tsft--; |
---|
255 | tap->wr_tsft = (uint64_t)htole32(tap->wr_tsft) << 32; |
---|
256 | tap->wr_tsft += stat->tsf_low; |
---|
257 | |
---|
258 | /* XXX 20/40? */ |
---|
259 | |
---|
260 | /* Map HW rate index to 802.11 rate. */ |
---|
261 | if (rate < RTWN_RIDX_MCS(0)) |
---|
262 | tap->wr_rate = ridx2rate[rate]; |
---|
263 | else /* MCS0~15. */ |
---|
264 | tap->wr_rate = IEEE80211_RATE_MCS | (rate - 12); |
---|
265 | |
---|
266 | tap->wr_dbm_antsignal = *rssi; |
---|
267 | tap->wr_dbm_antnoise = RTWN_NOISE_FLOOR; |
---|
268 | } |
---|
269 | |
---|
270 | /* Drop PHY descriptor. */ |
---|
271 | m_adj(m, infosz + shift); |
---|
272 | |
---|
273 | return (ni); |
---|
274 | } |
---|
275 | |
---|
276 | void |
---|
277 | rtwn_adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, |
---|
278 | const struct ieee80211_rx_stats *rxs, |
---|
279 | int rssi, int nf) |
---|
280 | { |
---|
281 | struct ieee80211vap *vap = ni->ni_vap; |
---|
282 | struct rtwn_softc *sc = vap->iv_ic->ic_softc; |
---|
283 | struct rtwn_vap *uvp = RTWN_VAP(vap); |
---|
284 | uint64_t ni_tstamp, curr_tstamp; |
---|
285 | |
---|
286 | uvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); |
---|
287 | |
---|
288 | if (vap->iv_state == IEEE80211_S_RUN && |
---|
289 | (subtype == IEEE80211_FC0_SUBTYPE_BEACON || |
---|
290 | subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { |
---|
291 | ni_tstamp = le64toh(ni->ni_tstamp.tsf); |
---|
292 | RTWN_LOCK(sc); |
---|
293 | rtwn_get_tsf(sc, &curr_tstamp, uvp->id); |
---|
294 | RTWN_UNLOCK(sc); |
---|
295 | |
---|
296 | if (ni_tstamp >= curr_tstamp) |
---|
297 | (void) ieee80211_ibss_merge(ni); |
---|
298 | } |
---|
299 | } |
---|
300 | |
---|
301 | static uint8_t |
---|
302 | rtwn_get_multi_pos(const uint8_t maddr[]) |
---|
303 | { |
---|
304 | uint64_t mask = 0x00004d101df481b4; |
---|
305 | uint8_t pos = 0x27; /* initial value */ |
---|
306 | int i, j; |
---|
307 | |
---|
308 | for (i = 0; i < IEEE80211_ADDR_LEN; i++) |
---|
309 | for (j = (i == 0) ? 1 : 0; j < 8; j++) |
---|
310 | if ((maddr[i] >> j) & 1) |
---|
311 | pos ^= (mask >> (i * 8 + j - 1)); |
---|
312 | |
---|
313 | pos &= 0x3f; |
---|
314 | |
---|
315 | return (pos); |
---|
316 | } |
---|
317 | |
---|
318 | void |
---|
319 | rtwn_set_multi(struct rtwn_softc *sc) |
---|
320 | { |
---|
321 | struct ieee80211com *ic = &sc->sc_ic; |
---|
322 | uint32_t mfilt[2]; |
---|
323 | |
---|
324 | RTWN_ASSERT_LOCKED(sc); |
---|
325 | |
---|
326 | /* general structure was copied from ath(4). */ |
---|
327 | if (ic->ic_allmulti == 0) { |
---|
328 | struct ieee80211vap *vap; |
---|
329 | struct ifnet *ifp; |
---|
330 | struct ifmultiaddr *ifma; |
---|
331 | |
---|
332 | /* |
---|
333 | * Merge multicast addresses to form the hardware filter. |
---|
334 | */ |
---|
335 | mfilt[0] = mfilt[1] = 0; |
---|
336 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
---|
337 | ifp = vap->iv_ifp; |
---|
338 | if_maddr_rlock(ifp); |
---|
339 | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { |
---|
340 | caddr_t dl; |
---|
341 | uint8_t pos; |
---|
342 | |
---|
343 | dl = LLADDR((struct sockaddr_dl *) |
---|
344 | ifma->ifma_addr); |
---|
345 | pos = rtwn_get_multi_pos(dl); |
---|
346 | |
---|
347 | mfilt[pos / 32] |= (1 << (pos % 32)); |
---|
348 | } |
---|
349 | if_maddr_runlock(ifp); |
---|
350 | } |
---|
351 | } else |
---|
352 | mfilt[0] = mfilt[1] = ~0; |
---|
353 | |
---|
354 | |
---|
355 | rtwn_write_4(sc, R92C_MAR + 0, mfilt[0]); |
---|
356 | rtwn_write_4(sc, R92C_MAR + 4, mfilt[1]); |
---|
357 | |
---|
358 | RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s: MC filter %08x:%08x\n", |
---|
359 | __func__, mfilt[0], mfilt[1]); |
---|
360 | } |
---|
361 | |
---|
362 | static void |
---|
363 | rtwn_rxfilter_update_mgt(struct rtwn_softc *sc) |
---|
364 | { |
---|
365 | uint16_t filter; |
---|
366 | |
---|
367 | filter = 0x7f7f; |
---|
368 | if (sc->bcn_vaps == 0) { /* STA and/or MONITOR mode vaps */ |
---|
369 | filter &= ~( |
---|
370 | R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_REQ) | |
---|
371 | R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_REQ) | |
---|
372 | R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_PROBE_REQ)); |
---|
373 | } |
---|
374 | if (sc->ap_vaps == sc->nvaps - sc->mon_vaps) { /* AP vaps only */ |
---|
375 | filter &= ~( |
---|
376 | R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_RESP) | |
---|
377 | R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_RESP)); |
---|
378 | } |
---|
379 | rtwn_write_2(sc, R92C_RXFLTMAP0, filter); |
---|
380 | } |
---|
381 | |
---|
382 | void |
---|
383 | rtwn_rxfilter_update(struct rtwn_softc *sc) |
---|
384 | { |
---|
385 | |
---|
386 | RTWN_ASSERT_LOCKED(sc); |
---|
387 | |
---|
388 | /* Filter for management frames. */ |
---|
389 | rtwn_rxfilter_update_mgt(sc); |
---|
390 | |
---|
391 | /* Update Rx filter. */ |
---|
392 | rtwn_set_promisc(sc); |
---|
393 | } |
---|
394 | |
---|
395 | void |
---|
396 | rtwn_rxfilter_init(struct rtwn_softc *sc) |
---|
397 | { |
---|
398 | |
---|
399 | RTWN_ASSERT_LOCKED(sc); |
---|
400 | |
---|
401 | /* Setup multicast filter. */ |
---|
402 | rtwn_set_multi(sc); |
---|
403 | |
---|
404 | /* Reject all control frames. */ |
---|
405 | rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000); |
---|
406 | |
---|
407 | /* Reject all data frames. */ |
---|
408 | rtwn_write_2(sc, R92C_RXFLTMAP2, 0x0000); |
---|
409 | |
---|
410 | /* Append generic Rx filter bits. */ |
---|
411 | sc->rcr |= R92C_RCR_AM | R92C_RCR_AB | R92C_RCR_APM | |
---|
412 | R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS | |
---|
413 | R92C_RCR_APP_ICV | R92C_RCR_APP_MIC; |
---|
414 | |
---|
415 | /* Update dynamic Rx filter parts. */ |
---|
416 | rtwn_rxfilter_update(sc); |
---|
417 | } |
---|
418 | |
---|
419 | void |
---|
420 | rtwn_rxfilter_set(struct rtwn_softc *sc) |
---|
421 | { |
---|
422 | if (!(sc->sc_flags & RTWN_RCR_LOCKED)) |
---|
423 | rtwn_write_4(sc, R92C_RCR, sc->rcr); |
---|
424 | } |
---|
425 | |
---|
426 | void |
---|
427 | rtwn_set_rx_bssid_all(struct rtwn_softc *sc, int enable) |
---|
428 | { |
---|
429 | |
---|
430 | if (enable) |
---|
431 | sc->rcr &= ~R92C_RCR_CBSSID_BCN; |
---|
432 | else |
---|
433 | sc->rcr |= R92C_RCR_CBSSID_BCN; |
---|
434 | rtwn_rxfilter_set(sc); |
---|
435 | } |
---|
436 | |
---|
437 | void |
---|
438 | rtwn_set_promisc(struct rtwn_softc *sc) |
---|
439 | { |
---|
440 | struct ieee80211com *ic = &sc->sc_ic; |
---|
441 | uint32_t mask_all, mask_min; |
---|
442 | |
---|
443 | RTWN_ASSERT_LOCKED(sc); |
---|
444 | |
---|
445 | mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP; |
---|
446 | mask_min = R92C_RCR_APM; |
---|
447 | |
---|
448 | if (sc->bcn_vaps == 0) |
---|
449 | mask_min |= R92C_RCR_CBSSID_BCN; |
---|
450 | if (sc->ap_vaps == 0) |
---|
451 | mask_min |= R92C_RCR_CBSSID_DATA; |
---|
452 | |
---|
453 | if (ic->ic_promisc == 0 && sc->mon_vaps == 0) { |
---|
454 | if (sc->bcn_vaps != 0) |
---|
455 | mask_all |= R92C_RCR_CBSSID_BCN; |
---|
456 | if (sc->ap_vaps != 0) /* for Null data frames */ |
---|
457 | mask_all |= R92C_RCR_CBSSID_DATA; |
---|
458 | |
---|
459 | sc->rcr &= ~mask_all; |
---|
460 | sc->rcr |= mask_min; |
---|
461 | } else { |
---|
462 | sc->rcr &= ~mask_min; |
---|
463 | sc->rcr |= mask_all; |
---|
464 | } |
---|
465 | rtwn_rxfilter_set(sc); |
---|
466 | } |
---|