| /* |
| * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /*=========================================================================== |
| |
| dfs_phyerr_tlv.c |
| |
| OVERVIEW: |
| |
| Source code borrowed from QCA_MAIN DFS module |
| |
| DEPENDENCIES: |
| |
| Are listed for each API below. |
| |
| ===========================================================================*/ |
| |
| /*=========================================================================== |
| |
| EDIT HISTORY FOR FILE |
| |
| |
| This section contains comments describing changes made to the module. |
| Notice that changes are listed in reverse chronological order. |
| |
| |
| |
| when who what, where, why |
| ---------- --- -------------------------------------------------------- |
| |
| ===========================================================================*/ |
| |
| |
| #include "dfs.h" |
| /* TO DO DFS |
| #include <ieee80211_var.h> |
| */ |
| /* TO DO DFS |
| #include <ieee80211_channel.h> |
| */ |
| #include "dfs_phyerr.h" |
| #include "dfs_phyerr_tlv.h" |
| |
| /* |
| * Parsed radar status |
| */ |
| struct rx_radar_status { |
| uint32_t raw_tsf; |
| uint32_t tsf_offset; |
| int rssi; |
| int pulse_duration; |
| int is_chirp:1; |
| int delta_peak; |
| int delta_diff; |
| int sidx; |
| int freq_offset; /* in KHz */ |
| }; |
| |
| |
| struct rx_search_fft_report { |
| uint32_t total_gain_db; |
| uint32_t base_pwr_db; |
| int fft_chn_idx; |
| int peak_sidx; |
| int relpwr_db; |
| int avgpwr_db; |
| int peak_mag; |
| int num_str_bins_ib; |
| }; |
| |
| |
| /* |
| * XXX until "fastclk" is stored in the DFS configuration.. |
| */ |
| #define PERE_IS_OVERSAMPLING(_dfs) (1) |
| |
| /* |
| * XXX there _has_ to be a better way of doing this! |
| * -adrian |
| */ |
| static int32_t |
| sign_extend_32(uint32_t v, int nb) |
| { |
| uint32_t m = 1U << (nb - 1); |
| |
| /* Chop off high bits, just in case */ |
| v &= v & ((1U << nb) - 1); |
| |
| /* Extend */ |
| return (v ^ m) - m; |
| } |
| |
| /* |
| * Calculate the frequency offset from the given signed bin index |
| * from the radar summary report. |
| * |
| * This takes the oversampling mode into account. |
| * |
| * For oversampling, each bin has resolution 44MHz/128. |
| * For non-oversampling, each bin has resolution 40MHz/128. |
| * |
| * It returns kHz - ie, 1000th's of MHz. |
| */ |
| static int |
| calc_freq_offset(int sindex, int is_oversampling) |
| { |
| |
| if (is_oversampling) |
| return (sindex * (44000 / 128)); |
| else |
| return (sindex * (40000 / 128)); |
| } |
| |
| static void |
| radar_summary_print(struct ath_dfs *dfs, |
| struct rx_radar_status *rsu, |
| bool enable_log) |
| { |
| if (!enable_log) |
| return; |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| " \n ############ Radar Summary ############ \n"); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - pulsedur = %d\n", |
| __func__, rsu->pulse_duration); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - rssi = %d\n", |
| __func__, rsu->rssi); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - ischirp = %d\n", |
| __func__, rsu->is_chirp); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - sidx = %d\n", |
| __func__, rsu->sidx); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - delta_peak = %d\n", |
| __func__, rsu->delta_peak); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - delta_diff = %d\n", |
| __func__, rsu->delta_diff); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - raw tsf = %d\n", |
| __func__, rsu->raw_tsf); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - tsf_offset = %d\n", |
| __func__, rsu->tsf_offset); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Radar Summary - cooked tsf = %d \n", |
| __func__, (rsu->raw_tsf - rsu->tsf_offset)); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: frequency offset = %d.%d MHz (oversampling = %d) \n", |
| __func__, (int) (rsu->freq_offset / 1000), |
| (int) abs(rsu->freq_offset % 1000), PERE_IS_OVERSAMPLING(dfs)); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "\n ################################### \n"); |
| } |
| |
| /* |
| * Parse the radar summary frame. |
| * |
| * The frame contents _minus_ the TLV are passed in. |
| */ |
| static void |
| radar_summary_parse(struct ath_dfs *dfs, const char *buf, size_t len, |
| struct rx_radar_status *rsu) |
| { |
| uint32_t rs[2]; |
| int freq_centre, freq; |
| |
| /* Drop out if we have < 2 DWORDs available */ |
| if (len < sizeof(rs)) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR | |
| ATH_DEBUG_DFS_PHYERR_SUM, |
| "%s: len (%zu) < expected (%zu)!", |
| __func__, |
| len, |
| sizeof(rs)); |
| } |
| |
| /* |
| * Since the TLVs may be unaligned for some reason |
| * we take a private copy into aligned memory. |
| * This enables us to use the HAL-like accessor macros |
| * into the DWORDs to access sub-DWORD fields. |
| */ |
| OS_MEMCPY(rs, buf, sizeof(rs)); |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: two 32 bit values are: %08x %08x", __func__, rs[0], rs[1]); |
| // DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s (p=%pK):", __func__, buf); |
| |
| /* Populate the fields from the summary report */ |
| rsu->tsf_offset = |
| MS(rs[RADAR_REPORT_PULSE_REG_2], RADAR_REPORT_PULSE_TSF_OFFSET); |
| rsu->pulse_duration = |
| MS(rs[RADAR_REPORT_PULSE_REG_2], RADAR_REPORT_PULSE_DUR); |
| rsu->is_chirp = |
| MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_PULSE_IS_CHIRP); |
| rsu->sidx = sign_extend_32( |
| MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_PULSE_SIDX), |
| 10); |
| rsu->freq_offset = |
| calc_freq_offset(rsu->sidx, PERE_IS_OVERSAMPLING(dfs)); |
| |
| /* These are only relevant if the pulse is a chirp */ |
| rsu->delta_peak = sign_extend_32( |
| MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_PULSE_DELTA_PEAK), |
| 6); |
| rsu->delta_diff = |
| MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_PULSE_DELTA_DIFF); |
| |
| /* WAR for FCC Type 4*/ |
| /* |
| * HW is giving longer pulse duration (in case of VHT80, with traffic) |
| * which fails to detect FCC type4 radar pulses. Added a work around to |
| * fix the pulse duration and duration delta. |
| * |
| * IF VHT80 |
| * && (primary_channel==30MHz || primary_channel== -30MHz) |
| * && -4 <= pulse_index <= 4 |
| * && !chirp |
| * && pulse duration > 20 us |
| * THEN |
| * Set pulse duration to 20 us |
| */ |
| |
| adf_os_spin_lock_bh(&dfs->ic->chan_lock); |
| freq = ieee80211_chan2freq(dfs->ic, dfs->ic->ic_curchan); |
| freq_centre = dfs->ic->ic_curchan->ic_vhtop_ch_freq_seg1; |
| |
| if ((IEEE80211_IS_CHAN_11AC_VHT80(dfs->ic->ic_curchan) && |
| (abs(freq - freq_centre) == 30) && |
| !rsu->is_chirp && |
| abs(rsu->sidx) <= 4 && |
| rsu->pulse_duration > 20)){ |
| rsu->pulse_duration = 20; |
| } |
| |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| } |
| |
| static void |
| radar_fft_search_report_parse(struct ath_dfs *dfs, const char *buf, size_t len, |
| struct rx_search_fft_report *rsfr) |
| { |
| uint32_t rs[2]; |
| |
| /* Drop out if we have < 2 DWORDs available */ |
| if (len < sizeof(rs)) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR | |
| ATH_DEBUG_DFS_PHYERR_SUM, |
| "%s: len (%zu) < expected (%zu)!", |
| __func__, |
| len, |
| sizeof(rs)); |
| } |
| |
| /* |
| * Since the TLVs may be unaligned for some reason |
| * we take a private copy into aligned memory. |
| * This enables us to use the HAL-like accessor macros |
| * into the DWORDs to access sub-DWORD fields. |
| */ |
| OS_MEMCPY(rs, buf, sizeof(rs)); |
| rsfr->total_gain_db = MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_TOTAL_GAIN_DB); |
| rsfr->base_pwr_db = MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_BASE_PWR_DB); |
| rsfr->fft_chn_idx = MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_FFT_CHN_IDX); |
| rsfr->peak_sidx = sign_extend_32(MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_PEAK_SIDX), 12); |
| rsfr->relpwr_db = MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_RELPWR_DB); |
| rsfr->avgpwr_db = MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_AVGPWR_DB); |
| rsfr->peak_mag = MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_PEAK_MAG); |
| rsfr->num_str_bins_ib = MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_NUM_STR_BINS_IB); |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: two 32 bit values are: %08x %08x", __func__, rs[0], rs[1]); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: rsfr->total_gain_db = %d", __func__, rsfr->total_gain_db); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: rsfr->base_pwr_db = %d", __func__, rsfr->base_pwr_db); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: rsfr->fft_chn_idx = %d", __func__, rsfr->fft_chn_idx); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: rsfr->peak_sidx = %d", __func__, rsfr->peak_sidx); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: rsfr->relpwr_db = %d", __func__, rsfr->relpwr_db); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: rsfr->avgpwr_db = %d", __func__, rsfr->avgpwr_db); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: rsfr->peak_mag = %d", __func__, rsfr->peak_mag); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,"%s: rsfr->num_str_bins_ib = %d", __func__, rsfr->num_str_bins_ib); |
| } |
| |
| /* |
| * Parse a Peregrine BB TLV frame. |
| * |
| * This routine parses each TLV, prints out what's going on and |
| * calls an appropriate sub-function. |
| * |
| * Since the TLV format doesn't _specify_ all TLV components are |
| * DWORD aligned, we must treat them as not and access the fields |
| * appropriately. |
| */ |
| static int |
| tlv_parse_frame(struct ath_dfs *dfs, struct rx_radar_status *rs, |
| struct rx_search_fft_report *rsfr, const char *buf, size_t len, u_int8_t rssi) |
| { |
| int i = 0; |
| uint32_t tlv_hdr[1]; |
| bool first_tlv = true; |
| bool false_detect = false; |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, |
| "%s: total length = %zu bytes", __func__, len); |
| while ((i < len ) && (false_detect == false)) { |
| /* Ensure we at least have four bytes */ |
| if ((len - i) < sizeof(tlv_hdr)) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR | |
| ATH_DEBUG_DFS_PHYERR_SUM, |
| "%s: ran out of bytes, len=%zu, i=%d", |
| __func__, len, i); |
| return (0); |
| } |
| |
| /* |
| * Copy the offset into the header, |
| * so the DWORD style access macros |
| * can be used. |
| */ |
| OS_MEMCPY(&tlv_hdr, buf + i, sizeof(tlv_hdr)); |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, |
| "%s: HDR: TLV SIG=0x%x, TAG=0x%x, LEN=%d bytes", |
| __func__, |
| MS(tlv_hdr[TLV_REG], TLV_SIG), |
| MS(tlv_hdr[TLV_REG], TLV_TAG), |
| MS(tlv_hdr[TLV_REG], TLV_LEN)); |
| |
| /* |
| * Sanity check the length field is available in |
| * the remaining frame. Drop out if this isn't |
| * the case - we can't trust the rest of the TLV |
| * entries. |
| */ |
| if (MS(tlv_hdr[TLV_REG], TLV_LEN) + i >= len) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, |
| "%s: TLV oversize: TLV LEN=%d, available=%zu, " |
| "i=%d", |
| __func__, |
| MS(tlv_hdr[TLV_REG], TLV_LEN), |
| len, |
| i); |
| break; |
| } |
| |
| /* Skip the TLV header - one DWORD */ |
| i += sizeof(tlv_hdr); |
| |
| /* Handle the payload */ |
| /* XXX TODO! */ |
| switch (MS(tlv_hdr[TLV_REG], TLV_SIG)) { |
| case TAG_ID_RADAR_PULSE_SUMMARY: /* Radar pulse summary */ |
| /* XXX TODO verify return value */ |
| /* XXX TODO validate only seeing one of these */ |
| radar_summary_parse(dfs, buf + i, |
| MS(tlv_hdr[TLV_REG], TLV_LEN), rs); |
| break; |
| case TAG_ID_SEARCH_FFT_REPORT: |
| radar_fft_search_report_parse(dfs, buf + i, |
| MS(tlv_hdr[TLV_REG], TLV_LEN), rsfr); |
| /* |
| we examine search FFT report and make the following assumption |
| as per algorithms group's input: |
| (1) There may be multiple TLV |
| (2) We make false detection decison solely based on the first TLV |
| (3) If the first TLV is a serch FFT report then we check the peak_mag value. |
| When RSSI is equal to dfs->ath_dfs_false_rssI_thres (default 50) |
| and peak_mag is less than 2 * dfs->ath_dfs_peak_mag (default 40) |
| we treat it as false detect. |
| Please note that 50 is not a true RSSI estimate, but value indicated |
| by HW for RF saturation event. |
| */ |
| |
| if ((first_tlv == true) && |
| (rssi == dfs->ath_dfs_false_rssi_thres) && |
| (rsfr->peak_mag < (2 * dfs->ath_dfs_peak_mag))) { |
| false_detect = true; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: setting false_detect to TRUE", __func__); |
| } |
| |
| break; |
| default: |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, |
| "%s: unknown entry, SIG=0x%02x", |
| __func__, |
| MS(tlv_hdr[TLV_REG], TLV_SIG)); |
| } |
| |
| /* Skip the payload */ |
| i += MS(tlv_hdr[TLV_REG], TLV_LEN); |
| first_tlv = false; |
| } |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: done", __func__); |
| |
| return (false_detect? 0 : 1); |
| } |
| |
| /* |
| * Calculate the channel centre in MHz. |
| */ |
| static int |
| tlv_calc_freq_info(struct ath_dfs *dfs, struct rx_radar_status *rs) |
| { |
| uint32_t chan_centre; |
| uint32_t chan_width; |
| int chan_offset; |
| |
| /* |
| * For now, just handle up to VHT80 correctly. |
| */ |
| if (dfs->ic == NULL || dfs->ic->ic_curchan == NULL) { |
| DFS_PRINTK("%s: dfs->ic=%pK, that or curchan is null?", |
| __func__, dfs->ic); |
| return (0); |
| } |
| |
| adf_os_spin_lock_bh(&dfs->ic->chan_lock); |
| /* |
| * For now, the only 11ac channel with freq1/freq2 setup is |
| * VHT80. |
| * |
| * XXX should have a flag macro to check this! |
| */ |
| if (IEEE80211_IS_CHAN_11AC_VHT80(dfs->ic->ic_curchan)) { |
| /* 11AC, so cfreq1/cfreq2 are setup */ |
| |
| /* |
| * XXX if it's 80+80 this won't work - need to use seg |
| * appropriately! |
| */ |
| |
| chan_centre = dfs->ic->ic_curchan->ic_vhtop_ch_freq_seg1; |
| } else { |
| /* HT20/HT40 */ |
| |
| /* |
| * XXX this is hard-coded - it should be 5 or 10 for |
| * half/quarter appropriately. |
| */ |
| chan_width = 20; |
| |
| /* Grab default channel centre */ |
| chan_centre = ieee80211_chan2freq(dfs->ic, |
| dfs->ic->ic_curchan); |
| |
| /* Calculate offset based on HT40U/HT40D and VHT40U/VHT40D */ |
| if (IEEE80211_IS_CHAN_11N_HT40PLUS(dfs->ic->ic_curchan) || |
| dfs->ic->ic_curchan->ic_flags & IEEE80211_CHAN_VHT40PLUS) |
| chan_offset = chan_width; |
| else if (IEEE80211_IS_CHAN_11N_HT40MINUS(dfs->ic->ic_curchan) || |
| dfs->ic->ic_curchan->ic_flags & IEEE80211_CHAN_VHT40MINUS) |
| chan_offset = -chan_width; |
| else |
| chan_offset = 0; |
| |
| /* Calculate new _real_ channel centre */ |
| chan_centre += (chan_offset / 2); |
| } |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| |
| /* |
| * XXX half/quarter rate support! |
| */ |
| |
| /* Return ev_chan_centre in MHz */ |
| return (chan_centre); |
| } |
| |
| /* |
| * Calculate the centre frequency and low/high range for a radar pulse event. |
| * |
| * XXX TODO: Handle half/quarter rates correctly! |
| * XXX TODO: handle VHT160 correctly! |
| * XXX TODO: handle VHT80+80 correctly! |
| */ |
| static int |
| tlv_calc_event_freq_pulse(struct ath_dfs *dfs, struct rx_radar_status *rs, |
| uint32_t *freq_centre, uint32_t *freq_lo, uint32_t *freq_hi) |
| { |
| int chan_width; |
| int chan_centre; |
| |
| /* Fetch the channel centre frequency in MHz */ |
| chan_centre = tlv_calc_freq_info(dfs, rs); |
| |
| /* Convert to KHz */ |
| chan_centre *= 1000; |
| |
| /* |
| * XXX hard-code event width to be 2 * bin size for now; |
| * XXX this needs to take into account the core clock speed |
| * XXX for half/quarter rate mode. |
| */ |
| if (PERE_IS_OVERSAMPLING(dfs)) |
| chan_width = (44000 * 2 / 128); |
| else |
| chan_width = (40000 * 2 / 128); |
| |
| /* XXX adjust chan_width for half/quarter rate! */ |
| |
| /* |
| * Now we can do the math to figure out the correct channel range. |
| */ |
| (*freq_centre) = (uint32_t) (chan_centre + rs->freq_offset); |
| (*freq_lo) = (uint32_t) ((chan_centre + rs->freq_offset) |
| - chan_width); |
| (*freq_hi) = (uint32_t) ((chan_centre + rs->freq_offset) |
| + chan_width); |
| |
| return (1); |
| } |
| |
| /* |
| * The chirp bandwidth in KHz is defined as: |
| * |
| * totalBW(KHz) = delta_peak(mean) |
| * * [ (bin resolution in KHz) / (radar_fft_long_period in uS) ] |
| * * pulse_duration (us) |
| * |
| * The bin resolution depends upon oversampling. |
| * |
| * For now, we treat the radar_fft_long_period as a hard-coded 8uS. |
| */ |
| static int |
| tlv_calc_event_freq_chirp(struct ath_dfs *dfs, struct rx_radar_status *rs, |
| uint32_t *freq_centre, uint32_t *freq_lo, uint32_t *freq_hi) |
| { |
| int32_t bin_resolution; /* KHz * 100 */ |
| int32_t radar_fft_long_period = 8; /* microseconds */ |
| int32_t delta_peak; |
| int32_t pulse_duration; |
| int32_t total_bw; |
| int32_t chan_centre; |
| int32_t freq_1, freq_2; |
| |
| /* |
| * KHz isn't enough resolution here! |
| * So treat it as deci-hertz (10Hz) and convert back to KHz |
| * later. |
| */ |
| if (PERE_IS_OVERSAMPLING(dfs)) |
| bin_resolution = (44000 * 100) / 128; |
| else |
| bin_resolution = (40000 * 100) / 128; |
| |
| delta_peak = rs->delta_peak; |
| pulse_duration = rs->pulse_duration; |
| |
| total_bw = delta_peak * (bin_resolution / radar_fft_long_period) * |
| pulse_duration; |
| #if(LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)) |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR | ATH_DEBUG_DFS_PHYERR_SUM, |
| "%s: delta_peak=%d, pulse_duration=%d, bin_resolution=%d.%dKHz, " |
| "radar_fft_long_period=%d, total_bw=%d.%dKHz", |
| __func__, |
| delta_peak, |
| pulse_duration, |
| bin_resolution / 1000, |
| bin_resolution % 1000, |
| radar_fft_long_period, |
| total_bw / 100, |
| abs(total_bw % 100)); |
| #else |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR | ATH_DEBUG_DFS_PHYERR_SUM, |
| "%s: delta_peak=%d, pulse_duration=%d, bin_resolution=%d.%dKHz, " |
| "radar_fft_long_period=%d, total_bw=%d.%ldKHz", |
| __func__, |
| delta_peak, |
| pulse_duration, |
| bin_resolution / 1000, |
| bin_resolution % 1000, |
| radar_fft_long_period, |
| total_bw / 100, |
| (long int)abs(total_bw % 100)); |
| #endif |
| total_bw /= 100; /* back to KHz */ |
| |
| /* Grab the channel centre frequency in MHz */ |
| chan_centre = tlv_calc_freq_info(dfs, rs); |
| |
| /* Early abort! */ |
| if (chan_centre == 0) { |
| (*freq_centre) = 0; |
| return (0); |
| } |
| |
| /* Convert to KHz */ |
| chan_centre *= 1000; |
| |
| /* |
| * sidx is the starting frequency; total_bw is a signed value and |
| * for negative chirps (ie, moving down in frequency rather than up) |
| * the end frequency may be less than the start frequency. |
| */ |
| if (total_bw > 0) { |
| freq_1 = chan_centre + rs->freq_offset; |
| freq_2 = chan_centre + rs->freq_offset + total_bw; |
| } else { |
| freq_1 = chan_centre + rs->freq_offset + total_bw; |
| freq_2 = chan_centre + rs->freq_offset; |
| } |
| |
| (*freq_lo) = (uint32_t)(freq_1); |
| (*freq_hi) = (uint32_t)(freq_2); |
| (*freq_centre) = (uint32_t) (freq_1 + (abs(total_bw) / 2)); |
| |
| return (1); |
| } |
| |
| /* |
| * Calculate the centre and band edge frequencies of the given radar |
| * event. |
| */ |
| static int |
| tlv_calc_event_freq(struct ath_dfs *dfs, struct rx_radar_status *rs, |
| uint32_t *freq_centre, uint32_t *freq_lo, uint32_t *freq_hi) |
| { |
| if (rs->is_chirp) |
| return tlv_calc_event_freq_chirp(dfs, rs, freq_centre, |
| freq_lo, freq_hi); |
| else |
| return tlv_calc_event_freq_pulse(dfs, rs, freq_centre, |
| freq_lo, freq_hi); |
| } |
| |
| /* |
| * This is the public facing function which parses the PHY error |
| * and populates the dfs_phy_err struct. |
| */ |
| int |
| dfs_process_phyerr_bb_tlv(struct ath_dfs *dfs, void *buf, u_int16_t datalen, |
| u_int8_t rssi, u_int8_t ext_rssi, u_int32_t rs_tstamp, |
| u_int64_t fulltsf, struct dfs_phy_err *e, |
| bool enable_log) |
| { |
| struct rx_radar_status rs; |
| struct rx_search_fft_report rsfr; |
| static int invalid_phyerr_count = 0; |
| |
| OS_MEMZERO(&rs, sizeof(rs)); |
| |
| /* |
| * Add the ppdu_start/ppdu_end fields given to us by the upper |
| * layers. The firmware gives us a summary set of parameters rather |
| * than the whole PPDU_START/PPDU_END descriptor contenst. |
| */ |
| rs.rssi = rssi; |
| rs.raw_tsf = rs_tstamp; |
| /* |
| * Try parsing the TLV set. |
| */ |
| if (! tlv_parse_frame(dfs, &rs, &rsfr, buf, datalen, rssi)){ |
| invalid_phyerr_count++; |
| /* |
| * Print only at every 2 power times |
| * to avoid flushing of the kernel |
| * logs, since the frequency of |
| * invalid phyerrors is very high |
| * in noisy environments. |
| */ |
| if ( !(invalid_phyerr_count & 0xFF) ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_DEBUG, |
| "%s[%d]:DFS-tlv parse failed invalid phyerror count = %d", |
| __func__,__LINE__, invalid_phyerr_count); |
| } |
| return (0); |
| } |
| /* For debugging, print what we have parsed */ |
| radar_summary_print(dfs, &rs, enable_log); |
| |
| /* Populate dfs_phy_err from rs */ |
| OS_MEMSET(e, 0, sizeof(*e)); |
| e->rssi = rs.rssi; |
| e->dur = rs.pulse_duration; |
| e->sidx = rs.sidx; |
| e->is_pri = 1; /* XXX always PRI for now */ |
| e->is_ext = 0; |
| e->is_dc = 0; |
| e->is_early = 0; |
| e->pulse_delta_peak = rs.delta_peak; |
| e->pulse_delta_diff = rs.delta_diff; |
| /* |
| * XXX TODO: add a "chirp detection enabled" capability or config |
| * bit somewhere, in case for some reason the hardware chirp |
| * detection AND FFTs are disabled. |
| */ |
| /* For now, assume this hardware always does chirp detection */ |
| e->do_check_chirp = 1; |
| e->is_hw_chirp = !! (rs.is_chirp); |
| e->is_sw_chirp = 0; /* We don't yet do software chirp checking */ |
| |
| e->fulltsf = fulltsf; |
| e->rs_tstamp = rs.raw_tsf - rs.tsf_offset; |
| |
| /* XXX error check */ |
| (void) tlv_calc_event_freq(dfs, &rs, &e->freq, &e->freq_lo, |
| &e->freq_hi); |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR_SUM, |
| "%s: 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", |
| __func__, |
| rs.sidx, |
| (int) (rs.freq_offset / 1000), |
| (int) abs(rs.freq_offset % 1000), |
| rs.raw_tsf, |
| rs.tsf_offset, |
| e->rs_tstamp, |
| rs.rssi, |
| rs.pulse_duration, |
| (int) rs.is_chirp, |
| (unsigned long long) fulltsf, |
| (int) e->freq / 1000, |
| (int) abs(e->freq) % 1000, |
| (int) e->freq_lo / 1000, |
| (int) abs(e->freq_lo) % 1000, |
| (int) e->freq_hi / 1000, |
| (int) abs(e->freq_hi) % 1000); |
| |
| return (1); |
| } |