xref: /wlan-dirver/qca-wifi-host-cmn/umac/dfs/core/src/filtering/dfs_phyerr_tlv.c (revision 8cfe6b10058a04cafb17eed051f2ddf11bee8931)
1 /*
2  * Copyright (c) 2012, 2016-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /**
19  * DOC: This file contains TLV frame processing functions.
20  */
21 
22 #include "../dfs.h"
23 #include "../dfs_channel.h"
24 #include "../dfs_phyerr_tlv.h"
25 #include "wlan_dfs_mlme_api.h"
26 #include "../dfs_internal.h"
27 
28 #define AGC_MB_GAIN_THRESH1    68
29 #define AGC_OTHER_GAIN_THRESH1 40
30 #define AGC_MB_GAIN_THRESH2    80
31 #define AGC_OTHER_GAIN_THRESH2 60
32 #define AGC_GAIN_RSSI_THRESH   25
33 
34 /*
35  * Until "fastclk" is stored in the DFS configuration.
36  */
37 #define PERE_IS_OVERSAMPLING(_dfs) \
38 	(_dfs->dfs_caps.wlan_chip_is_over_sampled ? 1 : 0)
39 
40 /**
41  * dfs_sign_extend_32() - Calculates extended 32bit value.
42  * @v: Value.
43  * @nb: Offset.
44  *
45  * Return: Returns Extend vale.
46  */
47 static int32_t dfs_sign_extend_32(uint32_t v, int nb)
48 {
49 	uint32_t m = 1U << (nb - 1);
50 
51 	/* Chop off high bits, just in case. */
52 	v &= v & ((1U << nb) - 1);
53 
54 	/* Extend */
55 	return (v ^ m) - m;
56 }
57 
58 /**
59  * dfs_calc_freq_offset() - Calculate the frequency offset.
60  * @sindex: signed bin index.
61  * @is_oversampling: oversampling mode
62  *
63  * Calculate the frequency offset from the given signed bin index from the
64  * radar summary report. This takes the oversampling mode into account.
65  * For oversampling, each bin has resolution 44MHz/128. For non-oversampling,
66  * each bin has resolution 40MHz/128. It returns kHz - ie, 1000th's of MHz.
67  */
68 static int dfs_calc_freq_offset(int sindex, int is_oversampling)
69 {
70 	if (is_oversampling)
71 		return sindex * (44000 / 128);
72 	else
73 		return sindex * (40000 / 128);
74 }
75 
76 /**
77  * dfs_radar_summary_print() - Prints the Radar summary.
78  * @dfs: Pointer to wlan_dfs structure.
79  * @rsu: Pointer rx_radar_status structure.
80  */
81 static void dfs_radar_summary_print(struct wlan_dfs *dfs,
82 		struct rx_radar_status *rsu)
83 {
84 
85 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
86 		"    pulsedur=%d", rsu->pulse_duration);
87 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
88 		"    rssi=%d", rsu->rssi);
89 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
90 		"    ischirp=%d", rsu->is_chirp);
91 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
92 		"    sidx=%d", rsu->sidx);
93 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
94 		"    raw tsf=%d", rsu->raw_tsf);
95 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
96 		"    tsf_offset=%d", rsu->tsf_offset);
97 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
98 		"    cooked tsf=%d", rsu->raw_tsf - rsu->tsf_offset);
99 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
100 		"    frequency offset=%d.%d MHz (oversampling=%d)",
101 		(int)(rsu->freq_offset / 1000),
102 		(int)abs(rsu->freq_offset % 1000),
103 		PERE_IS_OVERSAMPLING(dfs));
104 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
105 		"    agc_total_gain=%d", rsu->agc_total_gain);
106 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
107 		"    agc_mb_gain=%d", rsu->agc_mb_gain);
108 }
109 
110 /**
111  * dfs_radar_summary_parse() - Parse the radar summary frame.
112  * @dfs: pointer to wlan_dfs structure.
113  * @buf: Phyerr buffer.
114  * @len: Phyerr buflen.
115  * @rsu: Pointer to rx_radar_status structure.
116  *
117  * The frame contents _minus_ the TLV are passed in.
118  */
119 static void dfs_radar_summary_parse(struct wlan_dfs *dfs,
120 		const char *buf,
121 		size_t len,
122 		struct rx_radar_status *rsu)
123 {
124 	uint32_t rs[3];
125 
126 	/* Drop out if we have < 2 DWORDs available. */
127 	if (len < sizeof(rs)) {
128 		dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR |
129 			WLAN_DEBUG_DFS_PHYERR_SUM,
130 			"len (%zu) < expected (%zu)!", len, sizeof(rs));
131 	}
132 
133 	/*
134 	 * Since the TLVs may be unaligned for some reason
135 	 * we take a private copy into aligned memory.
136 	 * This enables us to use the HAL-like accessor macros
137 	 * into the DWORDs to access sub-DWORD fields.
138 	 */
139 	qdf_mem_copy(rs, buf, sizeof(rs));
140 
141 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
142 		"two 32 bit values are: %08x %08x", rs[0], rs[1]);
143 
144 	/* Populate the fields from the summary report. */
145 	rsu->tsf_offset =
146 		MS(rs[RADAR_REPORT_PULSE_REG_2], RADAR_REPORT_PULSE_TSF_OFFSET);
147 	rsu->pulse_duration =
148 		MS(rs[RADAR_REPORT_PULSE_REG_2], RADAR_REPORT_PULSE_DUR);
149 	rsu->is_chirp =
150 		MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_PULSE_IS_CHIRP);
151 	rsu->sidx = dfs_sign_extend_32(MS(rs[RADAR_REPORT_PULSE_REG_1],
152 				RADAR_REPORT_PULSE_SIDX),
153 			10);
154 	rsu->freq_offset =
155 		dfs_calc_freq_offset(rsu->sidx, PERE_IS_OVERSAMPLING(dfs));
156 
157 	/* These are only relevant if the pulse is a chirp. */
158 	rsu->delta_peak = dfs_sign_extend_32(MS(rs[RADAR_REPORT_PULSE_REG_1],
159 		    RADAR_REPORT_PULSE_DELTA_PEAK), 6);
160 	rsu->delta_diff =
161 		MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_PULSE_DELTA_DIFF);
162 	rsu->agc_total_gain =
163 		MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_AGC_TOTAL_GAIN);
164 	rsu->agc_mb_gain = MS(rs[RADAR_REPORT_PULSE_REG_2],
165 		RADAR_REPORT_PULSE_AGC_MB_GAIN);
166 }
167 
168 /**
169  * dfs_radar_fft_search_report_parse() - Parse FFT report.
170  * @dfs: pointer to wlan_dfs structure.
171  * @buf: Phyerr buffer.
172  * @len: Phyerr buflen.
173  * @rsfr: Pointer to rx_search_fft_report structure.
174  */
175 static void dfs_radar_fft_search_report_parse(struct wlan_dfs *dfs,
176 		const char *buf,
177 		size_t len,
178 		struct rx_search_fft_report *rsfr)
179 {
180 	uint32_t rs[3];
181 
182 	/* Drop out if we have < 2 DWORDs available. */
183 	if (len < sizeof(rs)) {
184 		dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR |
185 			WLAN_DEBUG_DFS_PHYERR_SUM,
186 			"len (%zu) < expected (%zu)!", len, sizeof(rs));
187 	}
188 
189 	/*
190 	 * Since the TLVs may be unaligned for some reason we take a private
191 	 * copy into aligned memory. This enables us to use the HAL-like
192 	 * accessor macros into the DWORDs to access sub-DWORD fields.
193 	 */
194 	qdf_mem_copy(rs, buf, sizeof(rs));
195 
196 	rsfr->total_gain_db =
197 	    MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_TOTAL_GAIN_DB);
198 
199 	rsfr->base_pwr_db =
200 	    MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_BASE_PWR_DB);
201 
202 	rsfr->fft_chn_idx =
203 	    MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_FFT_CHN_IDX);
204 
205 	rsfr->peak_sidx = dfs_sign_extend_32(MS(rs[SEARCH_FFT_REPORT_REG_1],
206 				SEARCH_FFT_REPORT_PEAK_SIDX), 12);
207 
208 	rsfr->relpwr_db =
209 	    MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_RELPWR_DB);
210 
211 	rsfr->avgpwr_db =
212 	    MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_AVGPWR_DB);
213 
214 	rsfr->peak_mag =
215 	    MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_PEAK_MAG);
216 
217 	rsfr->num_str_bins_ib =
218 	    MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_NUM_STR_BINS_IB);
219 
220 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
221 		"two 32 bit values are: %08x %08x", rs[0], rs[1]);
222 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
223 		"rsfr->total_gain_db = %d", rsfr->total_gain_db);
224 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
225 		"rsfr->base_pwr_db = %d", rsfr->base_pwr_db);
226 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
227 		"rsfr->fft_chn_idx = %d", rsfr->fft_chn_idx);
228 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
229 		"rsfr->peak_sidx = %d", rsfr->peak_sidx);
230 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
231 		"rsfr->relpwr_db = %d", rsfr->relpwr_db);
232 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
233 		"rsfr->avgpwr_db = %d", rsfr->avgpwr_db);
234 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
235 		"rsfr->peak_mag = %d", rsfr->peak_mag);
236 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
237 		"rsfr->num_str_bins_ib = %d", rsfr->num_str_bins_ib);
238 
239 	if (dfs->dfs_caps.wlan_chip_is_ht160) {
240 		rsfr->seg_id =
241 		    MS(rs[SEARCH_FFT_REPORT_REG_3], SEARCH_FFT_REPORT_SEG_ID);
242 		dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
243 			"rsfr->seg_id = %d", rsfr->seg_id);
244 	}
245 }
246 
247 /**
248  * dfs_check_for_false_detection() -  Check for possible false detection on
249  * beeliner this may also work for Cascade but parameters
250  * (e.g. AGC_MB_GAIN_THRESH1) may be different for Cascade.
251  * @dfs: pointer to wlan_dfs structure.
252  * @rs: pointer to rx_radar_status structure.
253  * @false_detect: Pointer to save false detect value.
254  * @rssi: RSSI.
255  */
256 static inline void dfs_check_for_false_detection(
257 		struct wlan_dfs *dfs,
258 		struct rx_radar_status *rs,
259 		bool *false_detect,
260 		uint8_t rssi)
261 {
262 	bool is_ht160 = false;
263 	bool is_false_detect = false;
264 
265 	is_ht160 = dfs->dfs_caps.wlan_chip_is_ht160;
266 	is_false_detect = dfs->dfs_caps.wlan_chip_is_false_detect;
267 
268 	if ((dfs->dfs_caps.wlan_chip_is_over_sampled == 0) &&
269 			(is_ht160 == 0 && is_false_detect)) {
270 		if ((rs->agc_mb_gain > AGC_MB_GAIN_THRESH1) &&
271 				((rs->agc_total_gain - rs->agc_mb_gain) <
272 				 AGC_OTHER_GAIN_THRESH1)) {
273 			*false_detect = true;
274 		}
275 
276 		if ((rs->agc_mb_gain > AGC_MB_GAIN_THRESH2) &&
277 				((rs->agc_total_gain - rs->agc_mb_gain) >
278 				 AGC_OTHER_GAIN_THRESH2) &&
279 				(rssi > AGC_GAIN_RSSI_THRESH)) {
280 			*false_detect = true;
281 		}
282 	}
283 
284 	if (*false_detect)
285 		dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
286 				"setting false_detect to TRUE because of mb/total_gain/rssi, agc_mb_gain=%d, agc_total_gain=%d, rssi=%d",
287 				rs->agc_mb_gain, rs->agc_total_gain, rssi);
288 }
289 
290 /**
291  * dfs_tlv_parse_frame () - Parse a Peregrine BB TLV frame.
292  * @dfs: pointer to wlan_dfs structure.
293  * @rs: pointer to rx_radar_status structure.
294  * @rsfr: Pointer to rx_search_fft_report structure.
295  * @buf: Phyerr buffer.
296  * @len: Phyerr buflen.
297  * @rssi: RSSI.
298  * @first_short_fft_peak_mag: first short FFT peak_mag.
299  * @psidx_diff: Pointer to psidx diff.
300  *
301  * This routine parses each TLV, prints out what's going on and calls an
302  * appropriate sub-function. Since the TLV format doesn't _specify_ all TLV
303  * components are DWORD aligned, we must treat them as not and access the
304  * fields appropriately.
305  */
306 static int dfs_tlv_parse_frame(struct wlan_dfs *dfs,
307 		struct rx_radar_status *rs,
308 		struct rx_search_fft_report *rsfr,
309 		const char *buf,
310 		size_t len,
311 		uint8_t rssi,
312 		int *first_short_fft_peak_mag,
313 		int16_t *psidx_diff)
314 {
315 	int i = 0;
316 	uint32_t tlv_hdr[1];
317 	bool false_detect = false;
318 	/* total search FFT reports including short and long */
319 	int8_t sfr_count = 0;
320 	int16_t first_short_fft_psidx = 0;
321 
322 	*psidx_diff = 0;
323 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
324 			"total length = %zu bytes", len);
325 	while ((i < len) && (false_detect == false)) {
326 		/* Ensure we at least have four bytes. */
327 		if ((len - i) < sizeof(tlv_hdr)) {
328 			dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR |
329 				WLAN_DEBUG_DFS_PHYERR_SUM,
330 				"ran out of bytes, len=%zu, i=%d", len, i);
331 			return 0;
332 		}
333 
334 		/*
335 		 * Copy the offset into the header, so the DWORD style access
336 		 * macros can be used.
337 		 */
338 		qdf_mem_copy(&tlv_hdr, buf + i, sizeof(tlv_hdr));
339 
340 		dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
341 			"HDR: TLV SIG=0x%x, TAG=0x%x, LEN=%d bytes",
342 			MS(tlv_hdr[TLV_REG], TLV_SIG),
343 			MS(tlv_hdr[TLV_REG], TLV_TAG),
344 			MS(tlv_hdr[TLV_REG], TLV_LEN));
345 
346 		/*
347 		 * Sanity check the length field is available in the remaining
348 		 * frame. Drop out if this isn't the case - we can't trust the
349 		 * rest of the TLV entries.
350 		 */
351 		if (MS(tlv_hdr[TLV_REG], TLV_LEN) + i >= len) {
352 			dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
353 				"TLV oversize: TLV LEN=%d, available=%zu, i=%d",
354 				 MS(tlv_hdr[TLV_REG], TLV_LEN),
355 				len, i);
356 			break;
357 		}
358 
359 		/* Skip the TLV header - one DWORD. */
360 		i += sizeof(tlv_hdr);
361 
362 		/* Handle the payload. */
363 		switch (MS(tlv_hdr[TLV_REG], TLV_SIG)) {
364 		case TAG_ID_RADAR_PULSE_SUMMARY: /* Radar pulse summary */
365 			dfs_radar_summary_parse(dfs, buf + i,
366 					MS(tlv_hdr[TLV_REG], TLV_LEN), rs);
367 
368 			dfs_check_for_false_detection(dfs, rs, &false_detect,
369 					rssi);
370 			break;
371 		case TAG_ID_SEARCH_FFT_REPORT:
372 			sfr_count++;
373 			dfs_radar_fft_search_report_parse(dfs, buf + i,
374 					MS(tlv_hdr[TLV_REG], TLV_LEN), rsfr);
375 
376 			/* we are interested in the first short FFT report's
377 			 * peak_mag for this value to be reliable, we must
378 			 * ensure that
379 			 * BB_srch_fft_ctrl_4.radar_fft_short_rpt_scl is set to
380 			 * 0.
381 			 */
382 			if (sfr_count == 1) {
383 				*first_short_fft_peak_mag = rsfr->peak_mag;
384 				first_short_fft_psidx = rsfr->peak_sidx;
385 			}
386 
387 			/*
388 			 * Check for possible false detection on Peregrine.
389 			 * we examine search FFT report and make the following
390 			 * assumption as per algorithms group's input:
391 			 * (1) There may be multiple TLV
392 			 * (2) We make false detection decision solely based on
393 			 * the first TLV
394 			 * (3) If the first TLV is a serch FFT report then we
395 			 * check the peak_mag value.
396 			 * When RSSI is equal to dfs->wlan_dfs_false_rssI_thres
397 			 * (default 50) and peak_mag is less than
398 			 * 2 * dfs->wlan_dfs_peak_mag (default 40) we treat it
399 			 * as false detect. Please note that 50 is not a true
400 			 * RSSI estimate, but value indicated by HW for RF
401 			 * saturation event.
402 			 */
403 			if (PERE_IS_OVERSAMPLING(dfs) &&
404 				(sfr_count == 1) &&
405 				(rssi == dfs->wlan_dfs_false_rssi_thres) &&
406 				(rsfr->peak_mag < (2 * dfs->wlan_dfs_peak_mag))
407 				) {
408 				false_detect = true;
409 				dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
410 					"setting false_detect to TRUE because of false_rssi_thres");
411 			}
412 
413 			/*
414 			 * The first FFT report indicated by (sfr_count == 1)
415 			 * should correspond to the first short FFT report from
416 			 * HW and the second FFT report indicated by
417 			 * (sfr_count == 2) should correspond to the first long
418 			 * FFT report from HW for the same pulse. The short and
419 			 * log FFT reports have a factor of 4 difference in
420 			 * resolution; hence the need to multiply by 4 when
421 			 * computing the psidx_diff.
422 			 */
423 			if (sfr_count == 2)
424 				*psidx_diff = rsfr->peak_sidx -
425 					      4 * first_short_fft_psidx;
426 
427 			break;
428 		default:
429 			dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
430 				"unknown entry, SIG=0x%02x",
431 				 MS(tlv_hdr[TLV_REG], TLV_SIG));
432 		}
433 
434 		/* Skip the payload. */
435 		i += MS(tlv_hdr[TLV_REG], TLV_LEN);
436 	}
437 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR, "done");
438 
439 	return false_detect ? 0 : 1;
440 }
441 
442 /**
443  * dfs_tlv_calc_freq_info() - Calculate the channel centre in MHz.
444  * @dfs: pointer to wlan_dfs structure.
445  * @rs: pointer to rx_radar_status structure.
446  *
447  * Return: Returns the channel center.
448  */
449 #ifdef CONFIG_CHAN_FREQ_API
450 static int dfs_tlv_calc_freq_info(struct wlan_dfs *dfs,
451 				  struct rx_radar_status *rs)
452 {
453 	uint32_t chan_centre;
454 	uint32_t chan_width;
455 	int chan_offset;
456 
457 	/* For now, just handle up to VHT80 correctly. */
458 	if (!dfs->dfs_curchan) {
459 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs_curchan is null");
460 		return 0;
461 		/*
462 		 * For now, the only 11ac channel with freq1/freq2 setup is
463 		 * VHT80. Should have a flag macro to check this!
464 		 */
465 	} else if (WLAN_IS_CHAN_11AC_VHT80(dfs->dfs_curchan)) {
466 		/*
467 		 * 11AC, so cfreq1/cfreq2 are setup.
468 		 * If it's 80+80 this won't work - need to use seg
469 		 * appropriately!
470 		 */
471 		chan_centre = dfs->dfs_curchan->dfs_ch_mhz_freq_seg1;
472 	} else {
473 		/*
474 		 * HT20/HT40.
475 		 * This is hard-coded - it should be 5 or 10 for half/quarter
476 		 * appropriately.
477 		 */
478 		chan_width = 20;
479 
480 		/* Grab default channel centre. */
481 		chan_centre = dfs->dfs_curchan->dfs_ch_freq;
482 
483 		/* Calculate offset based on HT40U/HT40D and VHT40U/VHT40D. */
484 		if (WLAN_IS_CHAN_11N_HT40PLUS(dfs->dfs_curchan) ||
485 		    WLAN_IS_CHAN_VHT40PLUS(dfs->dfs_curchan))
486 			chan_offset = chan_width;
487 		else if (WLAN_IS_CHAN_11N_HT40MINUS(dfs->dfs_curchan) ||
488 			  WLAN_IS_CHAN_VHT40MINUS(dfs->dfs_curchan))
489 			chan_offset = -chan_width;
490 		else
491 			chan_offset = 0;
492 
493 		/* Calculate new _real_ channel centre. */
494 		chan_centre += (chan_offset / 2);
495 	}
496 
497 	/* Return ev_chan_centre in MHz. */
498 	return chan_centre;
499 }
500 #endif
501 
502 
503 /**
504  * dfs_tlv_calc_event_freq_pulse() - Calculate the centre frequency and
505  *                                   low/high range for a radar pulse event.
506  * @dfs: pointer to wlan_dfs structure.
507  * @rs: pointer to rx_radar_status structure.
508  * @freq_centre: center frequency
509  * @freq_lo: lower bounds of frequency.
510  * @freq_hi: upper bounds of frequency.
511  *
512  * XXX TODO: Handle half/quarter rates correctly!
513  * XXX TODO: handle VHT160 correctly!
514  * XXX TODO: handle VHT80+80 correctly!
515  *
516  * Return: Returns 1.
517  */
518 static int dfs_tlv_calc_event_freq_pulse(struct wlan_dfs *dfs,
519 		struct rx_radar_status *rs,
520 		uint32_t *freq_centre,
521 		uint32_t *freq_lo,
522 		uint32_t *freq_hi)
523 {
524 	int chan_width;
525 	int chan_centre;
526 
527 	/* Fetch the channel centre frequency in MHz. */
528 	chan_centre = dfs_tlv_calc_freq_info(dfs, rs);
529 
530 	/* Convert to KHz. */
531 	chan_centre *= 1000;
532 
533 	/*
534 	 * XXX hard-code event width to be 2 * bin size for now;
535 	 * XXX this needs to take into account the core clock speed
536 	 * XXX for half/quarter rate mode.
537 	 */
538 	if (PERE_IS_OVERSAMPLING(dfs))
539 		chan_width = (44000 * 2 / 128);
540 	else
541 		chan_width = (40000 * 2 / 128);
542 
543 	/* XXX adjust chan_width for half/quarter rate! */
544 
545 	/* Now we can do the math to figure out the correct channel range. */
546 	(*freq_centre) = (uint32_t) (chan_centre + rs->freq_offset);
547 	(*freq_lo) = (uint32_t) ((chan_centre + rs->freq_offset) - chan_width);
548 	(*freq_hi) = (uint32_t) ((chan_centre + rs->freq_offset) + chan_width);
549 
550 	return 1;
551 }
552 
553 /**
554  * dfs_tlv_calc_event_freq_chirp() - Calculate the event freq.
555  * @dfs: pointer to wlan_dfs structure.
556  * @rs: pointer to rx_radar_status structure.
557  * @freq_centre: center frequency
558  * @freq_lo: lower bounds of frequency.
559  * @freq_hi: upper bounds of frequency.
560  *
561  * The chirp bandwidth in KHz is defined as:
562  * totalBW(KHz) = delta_peak(mean)
563  *    * [ (bin resolution in KHz) / (radar_fft_long_period in uS) ]
564  *    * pulse_duration (us)
565  * The bin resolution depends upon oversampling.
566  * For now, we treat the radar_fft_long_period as a hard-coded 8uS.
567  *
568  * Return: Returns 1
569  */
570 static int dfs_tlv_calc_event_freq_chirp(struct wlan_dfs *dfs,
571 		struct rx_radar_status *rs,
572 		uint32_t *freq_centre,
573 		uint32_t *freq_lo,
574 		uint32_t *freq_hi)
575 {
576 	int32_t bin_resolution; /* KHz * 100 */
577 	int32_t radar_fft_long_period = 8; /* microseconds */
578 	int32_t delta_peak;
579 	int32_t pulse_duration;
580 	int32_t total_bw;
581 	int32_t chan_centre;
582 	int32_t freq_1, freq_2;
583 
584 	/*
585 	 * KHz isn't enough resolution here!
586 	 * So treat it as deci-hertz (10Hz) and convert back to KHz later.
587 	 */
588 
589 	if (PERE_IS_OVERSAMPLING(dfs))
590 		bin_resolution = (OVER_SAMPLING_FREQ * HUNDRED) / NUM_BINS;
591 	else
592 		bin_resolution = (SAMPLING_FREQ * HUNDRED) / NUM_BINS;
593 
594 	delta_peak = rs->delta_peak;
595 	pulse_duration = rs->pulse_duration;
596 
597 	total_bw = delta_peak * (bin_resolution / radar_fft_long_period) *
598 		pulse_duration;
599 
600 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR | WLAN_DEBUG_DFS_PHYERR_SUM,
601 		"delta_peak=%d, pulse_duration=%d, bin_resolution=%d.%dKHz, radar_fft_long_period=%d, total_bw=%d.%ldKHz",
602 		delta_peak, pulse_duration, bin_resolution / THOUSAND,
603 		bin_resolution % THOUSAND, radar_fft_long_period,
604 		total_bw / HUNDRED,
605 		(long)abs(total_bw % HUNDRED));
606 
607 	total_bw /= HUNDRED; /* back to KHz */
608 	/* Grab the channel centre frequency in MHz. */
609 	chan_centre = dfs_tlv_calc_freq_info(dfs, rs);
610 
611 	/* Early abort! */
612 	if (chan_centre == 0) {
613 		(*freq_centre) = 0;
614 		return 0;
615 	}
616 
617 	/* Convert to KHz. */
618 	chan_centre *= THOUSAND;
619 
620 	/*
621 	 * Sidx is the starting frequency; total_bw is a signed value and for
622 	 * negative chirps (ie, moving down in frequency rather than up) the end
623 	 * frequency may be less than the start frequency.
624 	 */
625 	if (total_bw > 0) {
626 		freq_1 = chan_centre + rs->freq_offset;
627 		freq_2 = chan_centre + rs->freq_offset + total_bw;
628 	} else {
629 		freq_1 = chan_centre + rs->freq_offset + total_bw;
630 		freq_2 = chan_centre + rs->freq_offset;
631 	}
632 
633 	(*freq_lo) = (uint32_t)(freq_1);
634 	(*freq_hi) = (uint32_t)(freq_2);
635 	(*freq_centre) = (uint32_t) (freq_1 + (abs(total_bw) / 2));
636 
637 	return 1;
638 }
639 
640 /**
641  * dfs_tlv_calc_event_freq() - Calculate the centre and band edge frequencies
642  *                             of the given radar event.
643  * @dfs: Pointer to wlan_dfs structure.
644  * @rs: Pointer to rx_radar_status structure.
645  * @freq_centre: Center frequency
646  * @freq_lo: Lower bounds of frequency.
647  * @freq_hi: Upper bounds of frequency.
648  */
649 static int dfs_tlv_calc_event_freq(struct wlan_dfs *dfs,
650 		struct rx_radar_status *rs,
651 		uint32_t *freq_centre,
652 		uint32_t *freq_lo,
653 		uint32_t *freq_hi)
654 {
655 	if (rs->is_chirp)
656 		return dfs_tlv_calc_event_freq_chirp(dfs, rs, freq_centre,
657 				freq_lo, freq_hi);
658 	else
659 		return dfs_tlv_calc_event_freq_pulse(dfs, rs, freq_centre,
660 				freq_lo, freq_hi);
661 }
662 
663 int dfs_process_phyerr_bb_tlv(struct wlan_dfs *dfs,
664 		void *buf,
665 		uint16_t datalen,
666 		uint8_t rssi,
667 		uint8_t ext_rssi,
668 		uint32_t rs_tstamp,
669 		uint64_t fulltsf,
670 		struct dfs_phy_err *e)
671 {
672 	struct rx_radar_status rs;
673 	struct rx_search_fft_report rsfr;
674 	int first_short_fft_peak_mag = 0;
675 	int16_t psidx_diff;
676 
677 	qdf_mem_zero(&rs, sizeof(rs));
678 	qdf_mem_zero(&rsfr, sizeof(rsfr));
679 
680 	/*
681 	 * Add the ppdu_start/ppdu_end fields given to us by the upper layers.
682 	 * The firmware gives us a summary set of parameters rather than the
683 	 * whole PPDU_START/PPDU_END descriptor contenst.
684 	 */
685 	rs.rssi = rssi;
686 	rs.raw_tsf = rs_tstamp;
687 
688 	/* Try parsing the TLV set. */
689 	if (!dfs_tlv_parse_frame(dfs, &rs, &rsfr, buf, datalen, rssi,
690 				&first_short_fft_peak_mag, &psidx_diff))
691 		return 0;
692 
693 	/* For debugging, print what we have parsed. */
694 	dfs_radar_summary_print(dfs, &rs);
695 
696 	/* Populate dfs_phy_err from rs. */
697 	qdf_mem_zero(e, sizeof(*e));
698 	e->rssi = rs.rssi;
699 	e->dur = rs.pulse_duration;
700 	e->is_pri = 1; /* Always PRI for now */
701 	e->is_ext = 0;
702 	e->is_dc = 0;
703 	e->is_early = 0;
704 
705 	/*
706 	 * XXX TODO: add a "chirp detection enabled" capability or config bit
707 	 * somewhere, in case for some reason the hardware chirp detection AND
708 	 * FFTs are disabled.
709 	 * For now, assume this hardware always does chirp detection.
710 	 */
711 	e->do_check_chirp = 1;
712 	e->is_hw_chirp = !!(rs.is_chirp);
713 	e->is_sw_chirp = 0; /* We don't yet do software chirp checking */
714 
715 	e->fulltsf = fulltsf;
716 	e->rs_tstamp = rs.raw_tsf - rs.tsf_offset;
717 
718 	/* XXX error check */
719 	(void)dfs_tlv_calc_event_freq(dfs, &rs, &e->freq, &e->freq_lo,
720 			&e->freq_hi);
721 
722 	e->seg_id = rsfr.seg_id;
723 	e->sidx = rs.sidx;
724 	e->freq_offset_khz = rs.freq_offset;
725 	e->peak_mag = first_short_fft_peak_mag;
726 	e->total_gain = rs.agc_total_gain;
727 	e->mb_gain = rs.agc_mb_gain;
728 	e->relpwr_db = rsfr.relpwr_db;
729 	e->pulse_delta_peak = rs.delta_peak;
730 	e->pulse_psidx_diff = psidx_diff;
731 	e->pulse_delta_diff = rs.delta_diff;
732 
733 	dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR_SUM,
734 		"fbin=%d, freq=%d.%d MHz, raw tsf=%u, offset=%d, cooked tsf=%u, rssi=%d, dur=%d, is_chirp=%d, fulltsf=%llu, freq=%d.%d MHz, freq_lo=%d.%dMHz, freq_hi=%d.%d MHz",
735 		 rs.sidx, (int) (rs.freq_offset / 1000),
736 		(int) abs(rs.freq_offset % 1000), rs.raw_tsf, rs.tsf_offset,
737 		e->rs_tstamp, rs.rssi, rs.pulse_duration, (int)rs.is_chirp,
738 		(unsigned long long) fulltsf, (int)e->freq / 1000,
739 		(int) abs(e->freq) % 1000, (int)e->freq_lo / 1000,
740 		(int) abs(e->freq_lo) % 1000, (int)e->freq_hi / 1000,
741 		(int) abs(e->freq_hi) % 1000);
742 
743 	dfs_debug(dfs, WLAN_DEBUG_DFS_FALSE_DET,
744 		"ts=%u, dur=%d, rssi=%d, freq_offset=%d.%dMHz, is_chirp=%d, seg_id=%d, peak_mag=%d, total_gain=%d, mb_gain=%d, relpwr_db=%d, delta_peak=%d, delta_diff=%d, psidx_diff=%d",
745 		e->rs_tstamp, rs.pulse_duration, rs.rssi,
746 		(int)e->freq_offset_khz / 1000,
747 		(int)abs(e->freq_offset_khz) % 1000, (int)rs.is_chirp,
748 		rsfr.seg_id, rsfr.peak_mag, rs.agc_total_gain, rs.agc_mb_gain,
749 		rsfr.relpwr_db,
750 		rs.delta_peak,
751 		rs.delta_diff,
752 		psidx_diff);
753 
754 	return 1;
755 }
756