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