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