blob: 68b0b9a61e763a9c6d8df74f4ce63cedceaf9dcd [file] [log] [blame]
Victor Hsue0cd0e72021-06-08 11:05:03 +08001/*
2 * Monitor Mode routines.
3 * This header file housing the Monitor Mode routines implementation.
4 *
5 * Broadcom Proprietary and Confidential. Copyright (C) 2021,
6 * All Rights Reserved.
7 *
8 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom;
9 * the contents of this file may not be disclosed to third parties,
10 * copied or duplicated in any form, in whole or in part, without
11 * the prior written permission of Broadcom.
12 *
13 *
14 * <<Broadcom-WL-IPTag/Proprietary:>>
15 */
16
17#include <bcmutils.h>
18#include <bcmendian.h>
19#include <hndd11.h>
20#include <bcmwifi_channels.h>
21#include <bcmwifi_radiotap.h>
22#include <bcmwifi_monitor.h>
23#include <bcmwifi_rates.h>
24#include <monitor.h>
25#include <d11_cfg.h>
26
27struct monitor_info {
28 ratespec_t ampdu_rspec; /* spec value for AMPDU sniffing */
29 uint16 ampdu_counter;
30 uint16 amsdu_len;
31 uint8* amsdu_pkt;
32 int8 headroom;
33 d11_info_t *d11_info;
34 uint8 ampdu_plcp[D11_PHY_HDR_LEN];
35};
36
37struct he_ltf_gi_info {
38 uint8 gi;
39 uint8 ltf_size;
40 uint8 num_ltf;
41};
42
43struct he_mu_ltf_mp_info {
44 uint8 num_ltf;
45 uint8 mid_per;
46};
47
48/*
49 * su ppdu - mapping of ltf and gi values from plcp to rtap data format
50 * https://www.radiotap.org/fields/HE.html
51 */
52static const struct he_ltf_gi_info he_plcp2ltf_gi[4] = {
53 {3, 0, 7}, /* reserved, reserved, reserved */
54 {0, 2, 1}, /* 0.8us, 2x, 2x */
55 {1, 2, 1}, /* 1.6us, 2x, 2x */
56 {2, 3, 2} /* 3.2us, 4x, 4x */
57};
58
59/*
60 * mu ppdu - mapping of ru type value from phy rxstatus to rtap data format
61 * https://www.radiotap.org/fields/HE.html
62 */
63static const uint8 he_mu_phyrxs2ru_type[7] = {
64 4, /* 26-tone RU */
65 5, /* 52-tone RU */
66 6, /* 106-tone RU */
67 7, /* 242-tone RU */
68 8, /* 484-tone RU */
69 9, /* 996-tone RU */
70 10 /* 2x996-tone RU */
71};
72
73/*
74 * mu ppdu - doppler:1, mapping of ltf and midamble periodicity values from plcp to rtap data format
75 * https://www.radiotap.org/fields/HE.html
76 */
77static const struct he_mu_ltf_mp_info he_mu_plcp2ltf_mp[8] = {
78 {0, 0}, /* 1x, 10 */
79 {1, 0}, /* 2x, 10 */
80 {2, 0}, /* 4x, 10 */
81 {7, 0}, /* reserved, reserved */
82 {0, 1}, /* 1x, 20 */
83 {1, 1}, /* 2x, 20 */
84 {2, 1}, /* 4x, 20 */
85 {7, 0} /* reserved, reserved */
86};
87
88/*
89 * mu ppdu - doppler:0, mapping of ltf value from plcp to rtap data format
90 * https://www.radiotap.org/fields/HE.html
91 */
92static const uint8 he_mu_plcp2ltf[8] = {
93 0, /* 1x */
94 1, /* 2x */
95 2, /* 4x */
96 3, /* 6x */
97 4, /* 8x */
98 7, /* reserved */
99 7, /* reserved */
100 7 /* reserved */
101};
102
103/** Calculate the rate of a received frame and return it as a ratespec (monitor mode) */
104static ratespec_t
105BCMFASTPATH(wlc_recv_mon_compute_rspec)(monitor_info_t* info, wlc_d11rxhdr_t *wrxh, uint8 *plcp)
106{
107 d11rxhdr_t *rxh = &wrxh->rxhdr;
108 ratespec_t rspec = 0;
109 uint16 phy_ft;
110 uint corerev = info->d11_info->major_revid;
111 uint corerev_minor = info->d11_info->minor_revid;
112 BCM_REFERENCE(corerev_minor);
113
114 phy_ft = D11PPDU_FT(rxh, corerev);
115 switch (phy_ft) {
116 case FT_CCK:
117 rspec = CCK_RSPEC(CCK_PHY2MAC_RATE(((cck_phy_hdr_t *)plcp)->signal));
118 rspec |= WL_RSPEC_BW_20MHZ;
119 break;
120 case FT_OFDM:
121 rspec = OFDM_RSPEC(OFDM_PHY2MAC_RATE(((ofdm_phy_hdr_t *)plcp)->rlpt[0]));
122 rspec |= WL_RSPEC_BW_20MHZ;
123 break;
124 case FT_HT: {
125 uint ht_sig1, ht_sig2;
126 uint8 stbc;
127
128 ht_sig1 = plcp[0]; /* only interested in low 8 bits */
129 ht_sig2 = plcp[3] | (plcp[4] << 8); /* only interested in low 10 bits */
130
131 rspec = HT_RSPEC((ht_sig1 & HT_SIG1_MCS_MASK));
132 if (ht_sig1 & HT_SIG1_CBW) {
133 /* indicate rspec is for 40 MHz mode */
134 rspec |= WL_RSPEC_BW_40MHZ;
135 } else {
136 /* indicate rspec is for 20 MHz mode */
137 rspec |= WL_RSPEC_BW_20MHZ;
138 }
139 if (ht_sig2 & HT_SIG2_SHORT_GI)
140 rspec |= WL_RSPEC_SGI;
141 if (ht_sig2 & HT_SIG2_FEC_CODING)
142 rspec |= WL_RSPEC_LDPC;
143 stbc = ((ht_sig2 & HT_SIG2_STBC_MASK) >> HT_SIG2_STBC_SHIFT);
144 if (stbc != 0) {
145 rspec |= WL_RSPEC_STBC;
146 }
147 break;
148 }
149 case FT_VHT:
150 rspec = wf_vht_plcp_to_rspec(plcp);
151 break;
152#ifdef WL11AX
153 case FT_HE:
154 rspec = wf_he_plcp_to_rspec(plcp);
155 break;
156#endif /* WL11AX */
157#ifdef WL11BE
158 case FT_EHT:
159 rspec = wf_eht_plcp_to_rspec(plcp);
160 break;
161#endif
162 default:
163 /* return a valid rspec if not a debug/assert build */
164 rspec = OFDM_RSPEC(6) | WL_RSPEC_BW_20MHZ;
165 break;
166 }
167
168 return rspec;
169} /* wlc_recv_compute_rspec */
170
171static void
172wlc_he_su_fill_rtap_data(struct wl_rxsts *sts, uint8 *plcp)
173{
174 ASSERT(plcp);
175
176 /* he ppdu format */
177 sts->data1 |= WL_RXS_HEF_SIGA_PPDU_SU;
178
179 /* bss color */
180 sts->data1 |= WL_RXS_HEF_SIGA_BSS_COLOR;
181 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, BSS_COLOR);
182
183 /* beam change */
184 sts->data1 |= WL_RXS_HEF_SIGA_BEAM_CHANGE;
185 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, BEAM_CHANGE);
186
187 /* ul/dl */
188 sts->data1 |= WL_RXS_HEF_SIGA_DL_UL;
189 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, DL_UL);
190
191 /* data mcs */
192 sts->data1 |= WL_RXS_HEF_SIGA_MCS;
193 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, MCS);
194
195 /* data dcm */
196 sts->data1 |= WL_RXS_HEF_SIGA_DCM;
197 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, DCM);
198
199 /* coding */
200 sts->data1 |= WL_RXS_HEF_SIGA_CODING;
201 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, CODING);
202
203 /* ldpc extra symbol segment */
204 sts->data1 |= WL_RXS_HEF_SIGA_LDPC;
205 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, LDPC);
206
207 /* stbc */
208 sts->data1 |= WL_RXS_HEF_SIGA_STBC;
209 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, STBC);
210
211 /* spatial reuse */
212 sts->data1 |= WL_RXS_HEF_SIGA_SPATIAL_REUSE;
213 sts->data4 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, SR);
214
215 /* data bw */
216 sts->data1 |= WL_RXS_HEF_SIGA_BW;
217 sts->data5 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, BW);
218
219 /* gi */
220 sts->data2 |= WL_RXS_HEF_SIGA_GI;
221 sts->data5 |= HE_PACK_RTAP_GI_LTF_FROM_PLCP(plcp, SU, GI, gi);
222
223 /* ltf symbol size */
224 sts->data2 |= WL_RXS_HEF_SIGA_LTF_SIZE;
225 sts->data5 |= HE_PACK_RTAP_GI_LTF_FROM_PLCP(plcp, SU, LTF_SIZE, ltf_size);
226
227 /* number of ltf symbols */
228 sts->data2 |= WL_RXS_HEF_SIGA_NUM_LTF;
229 sts->data5 |= HE_PACK_RTAP_GI_LTF_FROM_PLCP(plcp, SU, NUM_LTF, num_ltf);
230
231 /* pre-fec padding factor */
232 sts->data2 |= WL_RXS_HEF_SIGA_PADDING;
233 sts->data5 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, PADDING);
234
235 /* txbf */
236 sts->data2 |= WL_RXS_HEF_SIGA_TXBF;
237 sts->data5 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, TXBF);
238
239 /* pe disambiguity */
240 sts->data2 |= WL_RXS_HEF_SIGA_PE;
241 sts->data5 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, PE);
242
243 /*
244 * if doppler (bit:41) is set in plcp to 1 then,
245 * - bit:25 indicates 'midamble periodicity'
246 * - bit:23-24 indicate 'nsts'
247 *
248 * if doppler (bit:41) is set to 0 then,
249 * - bit:23-25 indicate 'nsts'
250 */
251 if (HE_EXTRACT_FROM_PLCP(plcp, SU, DOPPLER)) {
252 /* doppler */
253 sts->data1 |= WL_RXS_HEF_SIGA_DOPPLER;
254 sts->data6 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, DOPPLER);
255
256 /* midamble periodicity */
257 sts->data2 |= WL_RXS_HEF_SIGA_MIDAMBLE;
258 sts->data6 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, MIDAMBLE);
259
260 /* nsts */
261 sts->data6 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, DOPPLER_SET_NSTS);
262 } else {
263 /* nsts */
264 sts->data6 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, DOPPLER_NOTSET_NSTS);
265 }
266
267 /* txop */
268 sts->data2 |= WL_RXS_HEF_SIGA_TXOP;
269 sts->data6 |= HE_PACK_RTAP_FROM_PLCP(plcp, SU, TXOP);
270}
271
272static void
273wlc_he_dl_ofdma_fill_rtap_data(struct wl_rxsts *sts, d11rxhdr_t *rxh,
274 uint8 *plcp, uint32 corerev, uint32 corerev_minor)
275{
276 uint8 doppler, midamble, val;
277 ASSERT(rxh);
278 ASSERT(plcp);
279
280 /* he ppdu format */
281 sts->data1 |= WL_RXS_HEF_SIGA_PPDU_MU;
282
283 /* bss color */
284 sts->data1 |= WL_RXS_HEF_SIGA_BSS_COLOR;
285 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, BSS_COLOR);
286
287 /* beam change (doesn't apply to mu ppdu) */
288 sts->data1 &= ~WL_RXS_HEF_SIGA_BEAM_CHANGE;
289
290 /* ul/dl */
291 sts->data1 |= WL_RXS_HEF_SIGA_DL_UL;
292 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, DL_UL);
293
294 /* data mcs */
295 sts->data1 |= WL_RXS_HEF_SIGA_MCS;
296 sts->data3 |= HE_PACK_RTAP_FROM_PRXS(rxh, corerev, corerev_minor, MCS);
297
298 /* data dcm */
299 sts->data1 |= WL_RXS_HEF_SIGA_DCM;
300 sts->data3 |= HE_PACK_RTAP_FROM_PRXS(rxh, corerev, corerev_minor, DCM);
301
302 /* coding */
303 sts->data1 |= WL_RXS_HEF_SIGA_CODING;
304 sts->data3 |= HE_PACK_RTAP_FROM_PRXS(rxh, corerev, corerev_minor, CODING);
305
306 /* ldpc extra symbol segment */
307 sts->data1 |= WL_RXS_HEF_SIGA_LDPC;
308 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, LDPC);
309
310 /* stbc */
311 sts->data1 |= WL_RXS_HEF_SIGA_STBC;
312 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, STBC);
313
314 /* spatial reuse */
315 sts->data1 |= WL_RXS_HEF_SIGA_SPATIAL_REUSE;
316 sts->data4 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, SR);
317
318 /* sta-id */
319 sts->data1 |= WL_RXS_HEF_SIGA_STA_ID;
320 sts->data4 |= HE_PACK_RTAP_FROM_PRXS(rxh, corerev, corerev_minor, STAID);
321
322 /* ru allocation */
323 val = he_mu_phyrxs2ru_type[D11PPDU_RU_TYPE(rxh, corerev, corerev_minor)];
324 sts->data1 |= WL_RXS_HEF_SIGA_RU_ALLOC;
325 sts->data5 |= HE_PACK_RTAP_FROM_VAL(val, RU_ALLOC);
326
327 /* doppler */
328 sts->data1 |= WL_RXS_HEF_SIGA_DOPPLER;
329 sts->data6 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, DOPPLER);
330
331 doppler = HE_EXTRACT_FROM_PLCP(plcp, MU, DOPPLER);
332 midamble = HE_EXTRACT_FROM_PLCP(plcp, MU, MIDAMBLE);
333 if (doppler) {
334 /* number of ltf symbols */
335 val = he_mu_plcp2ltf_mp[midamble].num_ltf;
336 sts->data2 |= WL_RXS_HEF_SIGA_NUM_LTF;
337 sts->data5 |= HE_PACK_RTAP_FROM_VAL(val, NUM_LTF);
338
339 /* midamble periodicity */
340 val = he_mu_plcp2ltf_mp[midamble].mid_per;
341 sts->data2 |= WL_RXS_HEF_SIGA_MIDAMBLE;
342 sts->data6 |= HE_PACK_RTAP_FROM_VAL(val, MIDAMBLE);
343 } else {
344 /* number of ltf symbols */
345 val = he_mu_plcp2ltf[midamble];
346 sts->data2 |= WL_RXS_HEF_SIGA_NUM_LTF;
347 sts->data5 |= HE_PACK_RTAP_FROM_VAL(val, NUM_LTF);
348 }
349
350 /* nsts */
351 sts->data6 |= HE_PACK_RTAP_FROM_PRXS(rxh, corerev, corerev_minor, NSTS);
352
353 /* gi */
354 sts->data2 |= WL_RXS_HEF_SIGA_GI;
355 sts->data5 |= HE_PACK_RTAP_GI_LTF_FROM_PLCP(plcp, MU, GI, gi);
356
357 /* ltf symbol size */
358 sts->data2 |= WL_RXS_HEF_SIGA_LTF_SIZE;
359 sts->data5 |= HE_PACK_RTAP_GI_LTF_FROM_PLCP(plcp, MU, LTF_SIZE, ltf_size);
360
361 /* pre-fec padding factor */
362 sts->data2 |= WL_RXS_HEF_SIGA_PADDING;
363 sts->data5 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, PADDING);
364
365 /* txbf */
366 sts->data2 |= WL_RXS_HEF_SIGA_TXBF;
367 sts->data5 |= HE_PACK_RTAP_FROM_PRXS(rxh, corerev, corerev_minor, TXBF);
368
369 /* pe disambiguity */
370 sts->data2 |= WL_RXS_HEF_SIGA_PE;
371 sts->data5 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, PE);
372
373 /* txop */
374 sts->data2 |= WL_RXS_HEF_SIGA_TXOP;
375 sts->data6 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, TXOP);
376}
377
378static void
379wlc_he_dl_ofdma_fill_rtap_flag(struct wl_rxsts *sts, uint8 *plcp, uint32 corerev)
380{
381 ASSERT(plcp);
382
383 /* sig-b mcs */
384 sts->flag1 |= WL_RXS_HEF_SIGB_MCS_KNOWN;
385 sts->flag1 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, SIGB_MCS);
386
387 /* sig-b dcm */
388 sts->flag1 |= WL_RXS_HEF_SIGB_DCM_KNOWN;
389 sts->flag1 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, SIGB_DCM);
390
391 /* sig-b compression */
392 sts->flag1 |= WL_RXS_HEF_SIGB_COMP_KNOWN;
393 sts->flag2 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, SIGB_COMP);
394
395 /* # of he-sig-b symbols/mu-mimo users */
396 sts->flag1 |= WL_RXS_HEF_NUM_SIGB_SYMB_KNOWN;
397 sts->flag2 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, SIGB_SYM_MU_MIMO_USER);
398
399 /* bandwidth from bandwidth field in he-sig-a */
400 sts->flag2 |= WL_RXS_HEF_BW_SIGA_KNOWN;
401 sts->flag2 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, BW_SIGA);
402
403 /* preamble puncturing from bandwidth field in he-sig-a */
404 sts->flag2 |= WL_RXS_HEF_PREPUNCR_SIGA_KNOWN;
405 sts->flag2 |= HE_PACK_RTAP_FROM_PLCP(plcp, MU, PRE_PUNCR_SIGA);
406}
407
408static void
409wlc_he_ul_ofdma_fill_rtap_data(struct wl_rxsts *sts, d11rxhdr_t *rxh, uint8 *plcp,
410 uint32 corerev)
411{
412 ASSERT(rxh);
413 ASSERT(plcp);
414
415 BCM_REFERENCE(rxh);
416
417 /* he ppdu format */
418 sts->data1 |= WL_RXS_HEF_SIGA_PPDU_TRIG;
419
420 /* bss color */
421 sts->data1 |= WL_RXS_HEF_SIGA_BSS_COLOR;
422 sts->data3 |= HE_PACK_RTAP_FROM_PLCP(plcp, TRIG, BSS_COLOR);
423
424 /* beam change (doesn't apply to mu ppdu) */
425 sts->data1 &= ~WL_RXS_HEF_SIGA_BEAM_CHANGE;
426
427 /* ul/dl */
428 sts->data1 |= WL_RXS_HEF_SIGA_DL_UL;
429 sts->data3 |= HE_PACK_RTAP_FROM_VAL(1, DL_UL);
430
431 /* txop */
432 sts->data2 |= WL_RXS_HEF_SIGA_TXOP;
433 sts->data6 |= HE_PACK_RTAP_FROM_PLCP(plcp, TRIG, TXOP);
434}
435
436/* recover 32bit TSF value from the 16bit TSF value */
437/* assumption is time in rxh is within 65ms of the current tsf */
438/* local TSF inserted in the rxh is at RxStart which is before 802.11 header */
439static uint32
440wlc_recover_tsf32(uint16 rxh_tsf, uint32 ts_tsf)
441{
442 uint16 rfdly;
443
444 /* adjust rx dly added in RxTSFTime */
445 /* comment in d11.h:
446 * BWL_PRE_PACKED_STRUCT struct d11rxhdr {
447 * ...
448 * uint16 RxTSFTime; RxTSFTime time of first MAC symbol + M_PHY_PLCPRX_DLY
449 * ...
450 * }
451 */
452
453 /* TODO: add PHY type specific value here... */
454 rfdly = M_BPHY_PLCPRX_DLY;
455
456 rxh_tsf -= rfdly;
457
458 return (((ts_tsf - rxh_tsf) & 0xFFFF0000) | rxh_tsf);
459}
460
461static uint8
462wlc_vht_get_gid(uint8 *plcp)
463{
464 uint32 plcp0 = plcp[0] | (plcp[1] << 8);
465 return (plcp0 & VHT_SIGA1_GID_MASK) >> VHT_SIGA1_GID_SHIFT;
466}
467
468static uint16
469wlc_vht_get_aid(uint8 *plcp)
470{
471 uint32 plcp0 = plcp[0] | (plcp[1] << 8) | (plcp[2] << 16);
472 return (plcp0 & VHT_SIGA1_PARTIAL_AID_MASK) >> VHT_SIGA1_PARTIAL_AID_SHIFT;
473}
474
475static bool
476wlc_vht_get_txop_ps_not_allowed(uint8 *plcp)
477{
478 return !!(plcp[2] & (VHT_SIGA1_TXOP_PS_NOT_ALLOWED >> 16));
479}
480
481static bool
482wlc_vht_get_sgi_nsym_da(uint8 *plcp)
483{
484 return !!(plcp[3] & VHT_SIGA2_GI_W_MOD10);
485}
486
487static bool
488wlc_vht_get_ldpc_extra_symbol(uint8 *plcp)
489{
490 return !!(plcp[3] & VHT_SIGA2_LDPC_EXTRA_OFDM_SYM);
491}
492
493static bool
494wlc_vht_get_beamformed(uint8 *plcp)
495{
496 return !!(plcp[4] & (VHT_SIGA2_BEAMFORM_ENABLE >> 8));
497}
498/* Convert htflags and mcs values to
499* rate in units of 500kbps
500*/
501static uint16
502wlc_ht_phy_get_rate(uint8 htflags, uint8 mcs)
503{
504
505 ratespec_t rspec = HT_RSPEC(mcs);
506
507 if (htflags & WL_RXS_HTF_40)
508 rspec |= WL_RSPEC_BW_40MHZ;
509
510 if (htflags & WL_RXS_HTF_SGI)
511 rspec |= WL_RSPEC_SGI;
512
513 return RSPEC2KBPS(rspec)/500;
514}
515
516static void
517bcmwifi_update_rxpwr_per_ant(monitor_pkt_rxsts_t *pkt_rxsts, wlc_d11rxhdr_t *wrxh)
518{
519 int i = 0;
520 wlc_d11rxhdr_ext_t *wrxh_ext = (wlc_d11rxhdr_ext_t *)((uint8 *)wrxh - WLC_SWRXHDR_EXT_LEN);
521
522 BCM_REFERENCE(wrxh_ext);
523
524 pkt_rxsts->corenum = 0;
525
526 for (i = 0; i < WL_RSSI_ANT_MAX; i++) {
527#ifdef BCM_MON_QDBM_RSSI
528 pkt_rxsts->rxpwr[i].dBm = wrxh_ext->rxpwr[i].dBm;
529 pkt_rxsts->rxpwr[i].decidBm = wrxh_ext->rxpwr[i].decidBm;
530#else
531 pkt_rxsts->rxpwr[i].dBm = wrxh->rxpwr[i];
532 pkt_rxsts->rxpwr[i].decidBm = 0;
533#endif
534 if (pkt_rxsts->rxpwr[i].dBm == 0) {
535 break;
536 }
537 pkt_rxsts->corenum ++;
538 }
539}
540
541static void
542bcmwifi_parse_ampdu(monitor_info_t *info, d11rxhdr_t *rxh, uint16 subtype, ratespec_t rspec,
543 uint8 *plcp, struct wl_rxsts *sts)
544{
545 uint32 corerev = info->d11_info->major_revid;
546 uint32 corerev_minor = info->d11_info->minor_revid;
547 uint32 ft = D11PPDU_FT(rxh, corerev);
548 uint8 plcp_len = D11_PHY_RXPLCP_LEN(corerev);
549 BCM_REFERENCE(corerev_minor);
550 if ((subtype == FC_SUBTYPE_QOS_DATA) || (subtype == FC_SUBTYPE_QOS_NULL)) {
551 /* A-MPDU parsing */
552 switch (ft) {
553 case FT_HT:
554 if (WLC_IS_MIMO_PLCP_AMPDU(plcp)) {
555 sts->nfrmtype |= WL_RXS_NFRM_AMPDU_FIRST;
556 /* Save the rspec & plcp for later */
557 info->ampdu_rspec = rspec;
558 /* src & dst len are same */
559 (void)memcpy_s(info->ampdu_plcp, plcp_len, plcp, plcp_len);
560 } else if (!PLCP_VALID(plcp)) {
561 sts->nfrmtype |= WL_RXS_NFRM_AMPDU_SUB;
562 /* Use the saved rspec & plcp */
563 rspec = info->ampdu_rspec;
564 /* src & dst len are same */
565 (void)memcpy_s(plcp, plcp_len, info->ampdu_plcp, plcp_len);
566 }
567 break;
568
569 case FT_VHT:
570 case FT_HE:
571 case FT_EHT:
572 if (PLCP_VALID(plcp) &&
573 !IS_PHYRXHDR_VALID(rxh, corerev, corerev_minor)) {
574 /* First MPDU:
575 * PLCP header is valid, Phy RxStatus is not valid
576 */
577 sts->nfrmtype |= WL_RXS_NFRM_AMPDU_FIRST;
578 /* Save the rspec & plcp for later */
579 info->ampdu_rspec = rspec;
580 /* src & dst len are same */
581 (void)memcpy_s(info->ampdu_plcp, plcp_len, plcp, plcp_len);
582 info->ampdu_counter++;
583 } else if (!PLCP_VALID(plcp) &&
584 !IS_PHYRXHDR_VALID(rxh, corerev, corerev_minor)) {
585 /* Sub MPDU: * PLCP header is not valid,
586 * Phy RxStatus is not valid
587 */
588 sts->nfrmtype |= WL_RXS_NFRM_AMPDU_SUB;
589 /* Use the saved rspec & plcp */
590 rspec = info->ampdu_rspec;
591 /* src & dst len are same */
592 (void)memcpy_s(plcp, plcp_len, info->ampdu_plcp, plcp_len);
593 } else if (PLCP_VALID(plcp) &&
594 IS_PHYRXHDR_VALID(rxh, corerev, corerev_minor)) {
595 /* MPDU is not a part of A-MPDU:
596 * PLCP header is valid and Phy RxStatus is valid
597 */
598 info->ampdu_counter++;
599 } else {
600 /* Last MPDU */
601 /* done to take care of the last MPDU in A-mpdu
602 * VHT packets are considered A-mpdu
603 * Use the saved rspec
604 */
605 rspec = info->ampdu_rspec;
606 /* src & dst len are same */
607 (void)memcpy_s(plcp, plcp_len, info->ampdu_plcp, plcp_len);
608 }
609
610 sts->ampdu_counter = info->ampdu_counter;
611 break;
612
613 case FT_OFDM:
614 break;
615 default:
616 printf("invalid frame type: %d\n", ft);
617 break;
618 }
619 }
620}
621
622static void
623bcmwifi_update_rate_modulation_info(monitor_info_t *info, d11rxhdr_t *rxh, d11rxhdr_t *rxh_last,
624 ratespec_t rspec, uint8* plcp, struct wl_rxsts *sts)
625{
626 uint32 corerev = info->d11_info->major_revid;
627 uint32 corerev_minor = info->d11_info->minor_revid;
628
629 /* prepare rate/modulation info */
630 if (RSPEC_ISVHT(rspec)) {
631 uint32 bw = RSPEC_BW(rspec);
632 /* prepare VHT rate/modulation info */
633 sts->nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
634 sts->mcs = (rspec & WL_RSPEC_VHT_MCS_MASK);
635
636 if (CHSPEC_IS80(sts->chanspec)) {
637 if (bw == WL_RSPEC_BW_20MHZ) {
638 switch (CHSPEC_CTL_SB(sts->chanspec)) {
639 default:
640 case WL_CHANSPEC_CTL_SB_LL:
641 sts->bw = WL_RXS_VHT_BW_20LL;
642 break;
643 case WL_CHANSPEC_CTL_SB_LU:
644 sts->bw = WL_RXS_VHT_BW_20LU;
645 break;
646 case WL_CHANSPEC_CTL_SB_UL:
647 sts->bw = WL_RXS_VHT_BW_20UL;
648 break;
649 case WL_CHANSPEC_CTL_SB_UU:
650 sts->bw = WL_RXS_VHT_BW_20UU;
651 break;
652 }
653 } else if (bw == WL_RSPEC_BW_40MHZ) {
654 switch (CHSPEC_CTL_SB(sts->chanspec)) {
655 default:
656 case WL_CHANSPEC_CTL_SB_L:
657 sts->bw = WL_RXS_VHT_BW_40L;
658 break;
659 case WL_CHANSPEC_CTL_SB_U:
660 sts->bw = WL_RXS_VHT_BW_40U;
661 break;
662 }
663 } else {
664 sts->bw = WL_RXS_VHT_BW_80;
665 }
666 } else if (CHSPEC_IS40(sts->chanspec)) {
667 if (bw == WL_RSPEC_BW_20MHZ) {
668 switch (CHSPEC_CTL_SB(sts->chanspec)) {
669 default:
670 case WL_CHANSPEC_CTL_SB_L:
671 sts->bw = WL_RXS_VHT_BW_20L;
672 break;
673 case WL_CHANSPEC_CTL_SB_U:
674 sts->bw = WL_RXS_VHT_BW_20U;
675 break;
676 }
677 } else if (bw == WL_RSPEC_BW_40MHZ) {
678 sts->bw = WL_RXS_VHT_BW_40;
679 }
680 } else {
681 sts->bw = WL_RXS_VHT_BW_20;
682 }
683
684 if (RSPEC_ISSTBC(rspec))
685 sts->vhtflags |= WL_RXS_VHTF_STBC;
686 if (wlc_vht_get_txop_ps_not_allowed(plcp))
687 sts->vhtflags |= WL_RXS_VHTF_TXOP_PS;
688 if (RSPEC_ISSGI(rspec)) {
689 sts->vhtflags |= WL_RXS_VHTF_SGI;
690 if (wlc_vht_get_sgi_nsym_da(plcp))
691 sts->vhtflags |= WL_RXS_VHTF_SGI_NSYM_DA;
692 }
693 if (RSPEC_ISLDPC(rspec)) {
694 sts->coding = WL_RXS_VHTF_CODING_LDCP;
695 if (wlc_vht_get_ldpc_extra_symbol(plcp)) {
696 /* need to un-set for MU-MIMO */
697 sts->vhtflags |= WL_RXS_VHTF_LDPC_EXTRA;
698 }
699 }
700 if (wlc_vht_get_beamformed(plcp))
701 sts->vhtflags |= WL_RXS_VHTF_BF;
702
703 sts->gid = wlc_vht_get_gid(plcp);
704 sts->aid = wlc_vht_get_aid(plcp);
705 sts->datarate = RSPEC2KBPS(rspec)/500;
706 } else if (RSPEC_ISHT(rspec)) {
707 /* prepare HT rate/modulation info */
708 sts->mcs = (rspec & WL_RSPEC_HT_MCS_MASK);
709
710 if (CHSPEC_IS40(sts->chanspec) || CHSPEC_IS80(sts->chanspec)) {
711 uint32 bw = RSPEC_BW(rspec);
712
713 if (bw == WL_RSPEC_BW_20MHZ) {
714 if (CHSPEC_CTL_SB(sts->chanspec) == WL_CHANSPEC_CTL_SB_L) {
715 sts->htflags = WL_RXS_HTF_20L;
716 } else {
717 sts->htflags = WL_RXS_HTF_20U;
718 }
719 } else if (bw == WL_RSPEC_BW_40MHZ) {
720 sts->htflags = WL_RXS_HTF_40;
721 }
722 }
723
724 if (RSPEC_ISSGI(rspec))
725 sts->htflags |= WL_RXS_HTF_SGI;
726 if (RSPEC_ISLDPC(rspec))
727 sts->htflags |= WL_RXS_HTF_LDPC;
728 if (RSPEC_ISSTBC(rspec))
729 sts->htflags |= (1 << WL_RXS_HTF_STBC_SHIFT);
730
731 sts->datarate = wlc_ht_phy_get_rate(sts->htflags, sts->mcs);
732 } else if (FALSE ||
733#ifdef WL11BE
734 RSPEC_ISHEEXT(rspec) ||
735#else
736 RSPEC_ISHE(rspec) ||
737#endif
738 FALSE) {
739 sts->nss = (rspec & WL_RSPEC_NSS_MASK) >> WL_RSPEC_NSS_SHIFT;
740 sts->mcs = (rspec & WL_RSPEC_MCS_MASK);
741
742 if (D11PPDU_ISMU_REV80(rxh_last, corerev, corerev_minor)) {
743 if (IS_PHYRXHDR_VALID(rxh_last, corerev, corerev_minor)) {
744 uint16 ff_type = D11PPDU_FF_TYPE(rxh_last,
745 corerev, corerev_minor);
746
747 switch (ff_type) {
748 case HE_MU_PPDU:
749 wlc_he_dl_ofdma_fill_rtap_data(sts, rxh_last,
750 plcp, corerev, corerev_minor);
751 wlc_he_dl_ofdma_fill_rtap_flag(sts, plcp, corerev);
752 break;
753 case HE_TRIG_PPDU:
754 wlc_he_ul_ofdma_fill_rtap_data(sts, rxh_last,
755 plcp, corerev);
756 break;
757 default:
758 /* should not have come here */
759 ASSERT(0);
760 break;
761 }
762 }
763 } else {
764 /* frame format is either SU or SU_RE (assumption only SU is supported) */
765 wlc_he_su_fill_rtap_data(sts, plcp);
766 }
767 } else {
768 /* round non-HT data rate to nearest 500bkps unit */
769 sts->datarate = RSPEC2KBPS(rspec)/500;
770 }
771}
772
773/* Convert RX hardware status to standard format and send to wl_monitor
774 * assume p points to plcp header
775 */
776static uint16
777wl_d11rx_to_rxsts(monitor_info_t* info, monitor_pkt_info_t* pkt_info, wlc_d11rxhdr_t *wrxh,
778 wlc_d11rxhdr_t *wrxh_last, void *pkt, uint16 len, void* pout, uint16 pad_req)
779{
780 struct wl_rxsts sts;
781 monitor_pkt_rxsts_t pkt_rxsts;
782 ratespec_t rspec;
783 uint16 chan_num;
784 uint8 *plcp;
785 uint8 *p = (uint8*)pkt;
786 uint8 hwrxoff = 0;
787 uint32 corerev = 0;
788 uint32 corerev_minor = 0;
789 struct dot11_header *h;
790 uint16 subtype;
791 d11rxhdr_t *rxh = &(wrxh->rxhdr);
792 d11rxhdr_t *rxh_last = &(wrxh_last->rxhdr);
793 d11_info_t* d11i = info->d11_info;
794 uint8 plcp_len = 0;
795
796 BCM_REFERENCE(chan_num);
797
798 ASSERT(p);
799 ASSERT(info);
800 pkt_rxsts.rxsts = &sts;
801
802 hwrxoff = (pkt_info->marker >> 16) & 0xff;
803 corerev = d11i->major_revid;
804 corerev_minor = d11i->minor_revid;
805 BCM_REFERENCE(corerev_minor);
806
807 plcp = (uint8*)p + hwrxoff;
808 plcp_len = D11_PHY_RXPLCP_LEN(corerev);
809
810 /* only non short rxstatus is expected */
811 if (IS_D11RXHDRSHORT(rxh, corerev, corerev_minor)) {
812 printf("short rxstatus is not expected here!\n");
813 ASSERT(0);
814 return 0;
815 }
816
817 if (RXHDR_GET_PAD_PRES(rxh, corerev, corerev_minor)) {
818 plcp += 2;
819 }
820
821 bzero((void *)&sts, sizeof(wl_rxsts_t));
822
823 sts.mactime = wlc_recover_tsf32(pkt_info->ts.ts_high, pkt_info->ts.ts_low);
824
825 /* update rxpwr per antenna */
826 bcmwifi_update_rxpwr_per_ant(&pkt_rxsts, wrxh);
827
828 /* calculate rspec based on ppdu frame type */
829 rspec = wlc_recv_mon_compute_rspec(info, wrxh, plcp);
830
831 h = (struct dot11_header *)(plcp + plcp_len);
832 subtype = (ltoh16(h->fc) & FC_SUBTYPE_MASK) >> FC_SUBTYPE_SHIFT;
833
834 /* parse & cache respec for ampdu */
835 bcmwifi_parse_ampdu(info, rxh, subtype, rspec, plcp, &sts);
836
837 /* A-MSDU parsing */
838 if (RXHDR_GET_AMSDU(rxh, corerev, corerev_minor)) {
839 /* it's chained buffer, break it if necessary */
840 sts.nfrmtype |= WL_RXS_NFRM_AMSDU_FIRST | WL_RXS_NFRM_AMSDU_SUB;
841 }
842
843 sts.signal = (pkt_info->marker >> 8) & 0xff;
844 sts.noise = (int8)pkt_info->marker;
845 sts.chanspec = D11RXHDR_ACCESS_VAL(rxh, corerev, corerev_minor, RxChan);
846
847 if (wf_chspec_malformed(sts.chanspec)) {
848 printf("Malformed chspec, %x\n", sts.chanspec);
849 return 0;
850 }
851
852 /* 4360: is chan_num supposed to be primary or CF channel? */
853 chan_num = CHSPEC_CHANNEL(sts.chanspec);
854
855 if (PRXS5_ACPHY_DYNBWINNONHT(rxh))
856 sts.vhtflags |= WL_RXS_VHTF_DYN_BW_NONHT;
857 else
858 sts.vhtflags &= ~WL_RXS_VHTF_DYN_BW_NONHT;
859
860 switch (PRXS5_ACPHY_CHBWINNONHT(rxh)) {
861 default: case PRXS5_ACPHY_CHBWINNONHT_20MHZ:
862 sts.bw_nonht = WLC_20_MHZ;
863 break;
864 case PRXS5_ACPHY_CHBWINNONHT_40MHZ:
865 sts.bw_nonht = WLC_40_MHZ;
866 break;
867 case PRXS5_ACPHY_CHBWINNONHT_80MHZ:
868 sts.bw_nonht = WLC_80_MHZ;
869 break;
870 case PRXS5_ACPHY_CHBWINNONHT_160MHZ:
871 sts.bw_nonht = WLC_160_MHZ;
872 break;
873 }
874
875 /* update rate and modulation info */
876 bcmwifi_update_rate_modulation_info(info, rxh, rxh_last, rspec, plcp, &sts);
877
878 sts.pktlength = FRAMELEN(corerev, corerev_minor, rxh) - plcp_len;
879
880 sts.phytype = WL_RXS_PHY_N;
881
882 if (RSPEC_ISCCK(rspec)) {
883 sts.encoding = WL_RXS_ENCODING_DSSS_CCK;
884 sts.preamble = (PRXS_SHORTH(rxh, corerev, corerev_minor) ?
885 WL_RXS_PREAMBLE_SHORT : WL_RXS_PREAMBLE_LONG);
886 } else if (RSPEC_ISOFDM(rspec)) {
887 sts.encoding = WL_RXS_ENCODING_OFDM;
888 sts.preamble = WL_RXS_PREAMBLE_SHORT;
889 } if (RSPEC_ISVHT(rspec)) {
890 sts.encoding = WL_RXS_ENCODING_VHT;
891 } else if (RSPEC_ISHE(rspec)) {
892 sts.encoding = WL_RXS_ENCODING_HE;
893 } else if (RSPEC_ISEHT(rspec)) {
894 sts.encoding = WL_RXS_ENCODING_EHT;
895 } else { /* MCS rate */
896 sts.encoding = WL_RXS_ENCODING_HT;
897 sts.preamble = (uint32)((D11HT_MMPLCPLen(rxh) != 0) ?
898 WL_RXS_PREAMBLE_HT_MM : WL_RXS_PREAMBLE_HT_GF);
899 }
900
901 /* translate error code */
902 if (D11RXHDR_ACCESS_VAL(rxh, corerev, corerev_minor, RxStatus1) & RXS_DECERR)
903 sts.pkterror |= WL_RXS_DECRYPT_ERR;
904 if (D11RXHDR_ACCESS_VAL(rxh, corerev, corerev_minor, RxStatus1) & RXS_FCSERR)
905 sts.pkterror |= WL_RXS_CRC_ERROR;
906
907 if (RXHDR_GET_PAD_PRES(rxh, corerev, corerev_minor)) {
908 p += 2; len -= 2;
909 }
910
911 p += (hwrxoff + D11_PHY_RXPLCP_LEN(corerev));
912 len -= (hwrxoff + D11_PHY_RXPLCP_LEN(corerev));
913 return (wl_rxsts_to_rtap(&pkt_rxsts, p, len, pout, pad_req));
914}
915
916#ifndef MONITOR_DNGL_CONV
917/* Collect AMSDU subframe packets */
918static uint16
919wl_monitor_amsdu(monitor_info_t* info, monitor_pkt_info_t* pkt_info, wlc_d11rxhdr_t *wrxh,
920 wlc_d11rxhdr_t *wrxh_last, void *pkt, uint16 len, void* pout, uint16* offset)
921{
922 uint8 *p = pkt;
923 uint8 hwrxoff = (pkt_info->marker >> 16) & 0xff;
924 uint16 frame_len = 0;
925 uint16 aggtype = (wrxh->rxhdr.lt80.RxStatus2 & RXS_AGGTYPE_MASK) >> RXS_AGGTYPE_SHIFT;
926
927 switch (aggtype) {
928 case RXS_AMSDU_FIRST:
929 case RXS_AMSDU_N_ONE:
930 /* Flush any previously collected */
931 if (info->amsdu_len) {
932 info->amsdu_len = 0;
933 }
934
935 info->headroom = MAX_RADIOTAP_SIZE - D11_PHY_RXPLCP_LEN(corerev) - hwrxoff;
936 info->headroom -= (wrxh->rxhdr.lt80.RxStatus1 & RXS_PBPRES) ? 2 : 0;
937
938 /* Save the new starting AMSDU subframe */
939 info->amsdu_len = len;
940 info->amsdu_pkt = (uint8*)pout + (info->headroom > 0 ?
941 info->headroom : 0);
942
943 memcpy(info->amsdu_pkt, p, len);
944
945 if (aggtype == RXS_AMSDU_N_ONE) {
946 /* all-in-one AMSDU subframe */
947 frame_len = wl_d11rx_to_rxsts(info, pkt_info, wrxh, wrxh, p,
948 len, info->amsdu_pkt - info->headroom, 0);
949
950 *offset = ABS(info->headroom);
951 frame_len += *offset;
952
953 info->amsdu_len = 0;
954 }
955 break;
956
957 case RXS_AMSDU_INTERMEDIATE:
958 case RXS_AMSDU_LAST:
959 default:
960 /* Check for previously collected */
961 if (info->amsdu_len) {
962 /* Append next AMSDU subframe */
963 p += hwrxoff; len -= hwrxoff;
964
965 if (wrxh->rxhdr.lt80.RxStatus1 & RXS_PBPRES) {
966 p += 2; len -= 2;
967 }
968
969 memcpy(info->amsdu_pkt + info->amsdu_len, p, len);
970 info->amsdu_len += len;
971
972 /* complete AMSDU frame */
973 if (aggtype == RXS_AMSDU_LAST) {
974 frame_len = wl_d11rx_to_rxsts(info, pkt_info, wrxh, wrxh,
975 info->amsdu_pkt, info->amsdu_len,
976 info->amsdu_pkt - info->headroom, 0);
977
978 *offset = ABS(info->headroom);
979 frame_len += *offset;
980
981 info->amsdu_len = 0;
982 }
983 }
984 break;
985 }
986
987 return frame_len;
988}
989#endif /* MONITOR_DNGL_CONV */
990
991uint16 bcmwifi_monitor_create(monitor_info_t** info)
992{
993 *info = MALLOCZ(NULL, sizeof(struct monitor_info));
994 if ((*info) == NULL) {
995 return FALSE;
996 }
997
998 (*info)->d11_info = MALLOCZ(NULL, sizeof(struct d11_info));
999 if ((*info)->d11_info == NULL) {
1000 goto fail;
1001 }
1002
1003 return TRUE;
1004
1005fail:
1006 bcmwifi_monitor_delete(*info);
1007
1008 return FALSE;
1009}
1010
1011void
1012bcmwifi_set_corerev_major(monitor_info_t* info, int8 corerev)
1013{
1014 d11_info_t* d11i = info->d11_info;
1015 d11i->major_revid = corerev;
1016}
1017
1018void
1019bcmwifi_set_corerev_minor(monitor_info_t* info, int8 corerev)
1020{
1021 d11_info_t* d11i = info->d11_info;
1022 d11i->minor_revid = corerev;
1023}
1024
1025void
1026bcmwifi_monitor_delete(monitor_info_t* info)
1027{
1028 if (info == NULL) {
1029 return;
1030 }
1031
1032 if (info->d11_info != NULL) {
1033 MFREE(NULL, info->d11_info, sizeof(struct d11_info));
1034 }
1035
1036 MFREE(NULL, info, sizeof(struct monitor_info));
1037}
1038
1039uint16
1040bcmwifi_monitor(monitor_info_t* info, monitor_pkt_info_t* pkt_info, void *pkt, uint16 len,
1041 void* pout, uint16* offset, uint16 pad_req, void *wrxh_in, void *wrxh_last)
1042{
1043 wlc_d11rxhdr_t *wrxh;
1044 int hdr_ext_offset = 0;
1045
1046#ifdef MONITOR_DNGL_CONV
1047 wrxh = (wlc_d11rxhdr_t *)wrxh_in;
1048 if (info == NULL) {
1049 return 0;
1050 }
1051#else
1052
1053#ifdef BCM_MON_QDBM_RSSI
1054 hdr_ext_offset = WLC_SWRXHDR_EXT_LEN;
1055#endif
1056 /* move beyond the extension, if any */
1057 pkt = (void *)((uint8 *)pkt + hdr_ext_offset);
1058 wrxh = (wlc_d11rxhdr_t *)pkt;
1059
1060 if ((wrxh->rxhdr.lt80.RxStatus2 & htol16(RXS_AMSDU_MASK))) {
1061 /* Need to add support for AMSDU */
1062 return wl_monitor_amsdu(info, pkt_info, wrxh, wrxh_last, pkt, len, pout, offset);
1063 } else
1064#endif /* NO MONITOR_DNGL_CONV */
1065 {
1066 info->amsdu_len = 0; /* reset amsdu */
1067 *offset = 0;
1068 return wl_d11rx_to_rxsts(info, pkt_info, wrxh, wrxh_last,
1069 pkt, len - hdr_ext_offset, pout, pad_req);
1070 }
1071}