| /* |
| * Copyright (c) 2013, 2016-2020 The Linux Foundation. All rights reserved. |
| * Copyright (c) 2002-2010, Atheros Communications 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. |
| */ |
| |
| /* Suppress -Waddress-of-packed-member for new toolchain update. |
| * Bug: http://b/33566695 |
| */ |
| #if __clang_major__ >= 4 |
| #pragma clang diagnostic ignored "-Waddress-of-packed-member" |
| #endif |
| |
| /** |
| * DOC: This contains the functionality to process the radar event generated |
| * for a pulse. This will group together pulses and call various detection |
| * functions to figure out whether a valid radar has been detected. |
| */ |
| |
| #include "../dfs.h" |
| #include "../dfs_zero_cac.h" |
| #include "../dfs_channel.h" |
| #include "../dfs_internal.h" |
| #include "../dfs_process_radar_found_ind.h" |
| #include <wlan_objmgr_vdev_obj.h> |
| #include "wlan_dfs_utils_api.h" |
| #include "wlan_dfs_lmac_api.h" |
| #include "../dfs_partial_offload_radar.h" |
| |
| #ifdef DFS_FCC_TYPE4_DURATION_CHECK |
| #define DFS_WAR_30_MHZ_SEPARATION 30 |
| #define DFS_WAR_PEAK_INDEX_ZERO 0 |
| #define DFS_TYPE4_WAR_PULSE_DURATION_LOWER_LIMIT 11 |
| #define DFS_TYPE4_WAR_PULSE_DURATION_UPPER_LIMIT 33 |
| #define DFS_TYPE4_WAR_PRI_LOWER_LIMIT 200 |
| #define DFS_TYPE4_WAR_PRI_UPPER_LIMIT 500 |
| #define DFS_TYPE4_WAR_VALID_PULSE_DURATION 12 |
| #endif |
| |
| #define FREQ_5500_MHZ 5500 |
| #define FREQ_5500_MHZ 5500 |
| |
| #define DFS_MAX_FREQ_SPREAD (1375 * 1) |
| #define DFS_LARGE_PRI_MULTIPLIER 4 |
| #define DFS_W53_DEFAULT_PRI_MULTIPLIER 2 |
| #define DFS_INVALID_PRI_LIMIT 100 /* should we use 135? */ |
| #define DFS_BIG_SIDX 10000 |
| |
| #define FRAC_PRI_SCORE_ARRAY_SIZE 40 |
| |
| static char debug_dup[33]; |
| static int debug_dup_cnt; |
| |
| /** |
| * dfs_process_pulse_dur() - Process pulse duration. |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re_dur: Duration. |
| * |
| * Convert the hardware provided duration to TSF ticks (usecs) taking the clock |
| * (fast or normal) into account. Legacy (pre-11n, Owl, Sowl, Howl) operate |
| * 5GHz using a 40MHz clock. Later 11n chips (Merlin, Osprey, etc) operate |
| * 5GHz using a 44MHz clock, so the reported pulse durations are different. |
| * Peregrine reports the pulse duration in microseconds regardless of the |
| * operating mode. (XXX TODO: verify this, obviously.) |
| * |
| * The hardware returns the duration in a variety of formats, |
| * so it's converted from the hardware format to TSF (usec) |
| * values here. |
| * XXX TODO: this should really be done when the PHY error |
| * is processed, rather than way out here.. |
| * |
| * |
| * Return: Returns the duration. |
| */ |
| static inline uint8_t dfs_process_pulse_dur(struct wlan_dfs *dfs, |
| uint8_t re_dur) |
| { |
| /* |
| * Short pulses are sometimes returned as having a duration of 0, |
| * so round those up to 1. |
| * XXX This holds true for BB TLV chips too, right? |
| */ |
| if (re_dur == 0) |
| return 1; |
| |
| /* |
| * For BB TLV chips, the hardware always returns microsecond pulse |
| * durations. |
| */ |
| if (dfs->dfs_caps.wlan_chip_is_bb_tlv) |
| return re_dur; |
| |
| /* |
| * This is for 11n and legacy chips, which may or may not use the 5GHz |
| * fast clock mode. |
| */ |
| /* Convert 0.8us durations to TSF ticks (usecs) */ |
| return (uint8_t)dfs_round((int32_t)((dfs->dur_multiplier)*re_dur)); |
| } |
| |
| #ifdef DFS_FCC_TYPE4_DURATION_CHECK |
| /* |
| * dfs_dur_check() - Modify the pulse duration for FCC Type 4 and JAPAN W56 |
| * Type 8 radar pulses when the conditions mentioned in the |
| * function body are reported in the radar summary report. |
| * @dfs: Pointer to wlan_dfs structure. |
| * @chan: Current channel. |
| * @re: Pointer to dfs_event. |
| * @diff_ts: timestamp of current pulse - timestamp of last pulse. |
| * |
| * return: Void |
| */ |
| static inline void dfs_dur_check( |
| struct wlan_dfs *dfs, |
| struct dfs_channel *chan, |
| struct dfs_event *re, |
| uint32_t diff_ts) |
| { |
| if ((dfs->dfsdomain == DFS_FCC_DOMAIN || |
| dfs->dfsdomain == DFS_MKK4_DOMAIN) && |
| ((chan->dfs_ch_flags & WLAN_CHAN_VHT80) == WLAN_CHAN_VHT80) && |
| (DFS_DIFF(chan->dfs_ch_freq, chan->dfs_ch_mhz_freq_seg1) == |
| DFS_WAR_30_MHZ_SEPARATION) && |
| re->re_sidx == DFS_WAR_PEAK_INDEX_ZERO && |
| (re->re_dur > DFS_TYPE4_WAR_PULSE_DURATION_LOWER_LIMIT && |
| re->re_dur < DFS_TYPE4_WAR_PULSE_DURATION_UPPER_LIMIT) && |
| (diff_ts > DFS_TYPE4_WAR_PRI_LOWER_LIMIT && |
| diff_ts < DFS_TYPE4_WAR_PRI_UPPER_LIMIT)) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, |
| "chan flags=%llu, Pri Chan %d MHz center %d MHZ", |
| chan->dfs_ch_flags, |
| chan->dfs_ch_freq, chan->dfs_ch_mhz_freq_seg1); |
| |
| dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, |
| "Report Peak Index = %d,re.re_dur = %d,diff_ts = %d", |
| re->re_sidx, re->re_dur, diff_ts); |
| |
| re->re_dur = DFS_TYPE4_WAR_VALID_PULSE_DURATION; |
| dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, |
| "Modifying the pulse duration to %d", re->re_dur); |
| } |
| } |
| #else |
| static inline void dfs_dur_check( |
| struct wlan_dfs *dfs, |
| struct dfs_channel *chan, |
| struct dfs_event *re, |
| uint32_t diff_ts) |
| { |
| } |
| #endif |
| |
| /* |
| * dfs_print_radar_events() - Prints the Radar events. |
| * @dfs: Pointer to wlan_dfs structure. |
| */ |
| static void dfs_print_radar_events(struct wlan_dfs *dfs) |
| { |
| int i; |
| |
| dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "#Phyerr=%d, #false detect=%d, #queued=%d", |
| dfs->dfs_phyerr_count, dfs->dfs_phyerr_reject_count, |
| dfs->dfs_phyerr_queued_count); |
| |
| dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs_phyerr_freq_min=%d, dfs_phyerr_freq_max=%d", |
| dfs->dfs_phyerr_freq_min, dfs->dfs_phyerr_freq_max); |
| |
| dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, |
| "Total radar events detected=%d, entries in the radar queue follows:", |
| dfs->dfs_event_log_count); |
| |
| for (i = 0; (i < DFS_EVENT_LOG_SIZE) && (i < dfs->dfs_event_log_count); |
| i++) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS, |
| "ts=%llu diff_ts=%u rssi=%u dur=%u, is_chirp=%d, seg_id=%d, sidx=%d, freq_offset=%d.%dMHz, peak_mag=%d, total_gain=%d, mb_gain=%d, relpwr_db=%d, delta_diff=%d, delta_peak=%d, psidx_diff=%d", |
| dfs->radar_log[i].ts, dfs->radar_log[i].diff_ts, |
| dfs->radar_log[i].rssi, dfs->radar_log[i].dur, |
| dfs->radar_log[i].is_chirp, dfs->radar_log[i].seg_id, |
| dfs->radar_log[i].sidx, |
| (int)dfs->radar_log[i].freq_offset_khz/1000, |
| (int)abs(dfs->radar_log[i].freq_offset_khz)%1000, |
| dfs->radar_log[i].peak_mag, |
| dfs->radar_log[i].total_gain, |
| dfs->radar_log[i].mb_gain, |
| dfs->radar_log[i].relpwr_db, |
| dfs->radar_log[i].delta_diff, |
| dfs->radar_log[i].delta_peak, |
| dfs->radar_log[i].psidx_diff); |
| } |
| dfs->dfs_event_log_count = 0; |
| dfs->dfs_phyerr_count = 0; |
| dfs->dfs_phyerr_reject_count = 0; |
| dfs->dfs_phyerr_queued_count = 0; |
| dfs->dfs_phyerr_freq_min = 0x7fffffff; |
| dfs->dfs_phyerr_freq_max = 0; |
| } |
| |
| /** |
| * dfs_confirm_radar() - This function checks for fractional PRI and jitter in |
| * sidx index to determine if the radar is real or not. |
| * @dfs: Pointer to dfs structure. |
| * @rf: Pointer to dfs_filter structure. |
| * @ext_chan_flag: ext chan flags. |
| */ |
| static int dfs_confirm_radar(struct wlan_dfs *dfs, |
| struct dfs_filter *rf, |
| int ext_chan_flag) |
| { |
| int i = 0; |
| int index; |
| struct dfs_delayline *dl = &rf->rf_dl; |
| struct dfs_delayelem *de; |
| uint64_t target_ts = 0; |
| struct dfs_pulseline *pl; |
| int start_index = 0, current_index, next_index; |
| unsigned char scores[FRAC_PRI_SCORE_ARRAY_SIZE]; |
| uint32_t pri_margin; |
| uint64_t this_diff_ts; |
| uint32_t search_bin; |
| |
| unsigned char max_score = 0; |
| int max_score_index = 0; |
| |
| pl = dfs->pulses; |
| |
| OS_MEMZERO(scores, sizeof(scores)); |
| scores[0] = rf->rf_threshold; |
| |
| pri_margin = dfs_get_pri_margin(dfs, ext_chan_flag, |
| (rf->rf_patterntype == 1)); |
| |
| /* |
| * Look for the entry that matches dl_seq_num_second. |
| * we need the time stamp and diff_ts from there. |
| */ |
| |
| for (i = 0; i < dl->dl_numelems; i++) { |
| index = (dl->dl_firstelem + i) & DFS_MAX_DL_MASK; |
| de = &dl->dl_elems[index]; |
| if (dl->dl_seq_num_second == de->de_seq_num) |
| target_ts = de->de_ts - de->de_time; |
| } |
| |
| if (dfs->dfs_debug_mask & WLAN_DEBUG_DFS2) { |
| dfs_print_delayline(dfs, &rf->rf_dl); |
| |
| /* print pulse line */ |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "%s: Pulse Line\n", __func__); |
| for (i = 0; i < pl->pl_numelems; i++) { |
| index = (pl->pl_firstelem + i) & |
| DFS_MAX_PULSE_BUFFER_MASK; |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "Elem %u: ts=%llu dur=%u, seq_num=%d, delta_peak=%d, psidx_diff=%d\n", |
| i, pl->pl_elems[index].p_time, |
| pl->pl_elems[index].p_dur, |
| pl->pl_elems[index].p_seq_num, |
| pl->pl_elems[index].p_delta_peak, |
| pl->pl_elems[index].p_psidx_diff); |
| } |
| } |
| |
| /* |
| * Walk through the pulse line and find pulse with target_ts. |
| * Then continue until we find entry with seq_number dl_seq_num_stop. |
| */ |
| |
| for (i = 0; i < pl->pl_numelems; i++) { |
| index = (pl->pl_firstelem + i) & DFS_MAX_PULSE_BUFFER_MASK; |
| if (pl->pl_elems[index].p_time == target_ts) { |
| dl->dl_seq_num_start = pl->pl_elems[index].p_seq_num; |
| start_index = index; /* save for future use */ |
| } |
| } |
| |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "%s: target_ts=%llu, dl_seq_num_start=%d, dl_seq_num_second=%d, dl_seq_num_stop=%d\n", |
| __func__, target_ts, dl->dl_seq_num_start, |
| dl->dl_seq_num_second, dl->dl_seq_num_stop); |
| |
| current_index = start_index; |
| while (pl->pl_elems[current_index].p_seq_num < dl->dl_seq_num_stop) { |
| next_index = (current_index + 1) & DFS_MAX_PULSE_BUFFER_MASK; |
| this_diff_ts = pl->pl_elems[next_index].p_time - |
| pl->pl_elems[current_index].p_time; |
| |
| /* Now update the score for this diff_ts */ |
| for (i = 1; i < FRAC_PRI_SCORE_ARRAY_SIZE; i++) { |
| search_bin = dl->dl_search_pri / (i + 1); |
| |
| /* |
| * We do not give score to PRI that is lower then the |
| * limit. |
| */ |
| if (search_bin < DFS_INVALID_PRI_LIMIT) |
| break; |
| |
| /* |
| * Increment the score if this_diff_ts belongs to this |
| * search_bin +/- margin. |
| */ |
| if ((this_diff_ts >= (search_bin - pri_margin)) && |
| (this_diff_ts <= |
| (search_bin + pri_margin))) { |
| /*increment score */ |
| scores[i]++; |
| } |
| } |
| current_index = next_index; |
| } |
| |
| for (i = 0; i < FRAC_PRI_SCORE_ARRAY_SIZE; i++) |
| if (scores[i] > max_score) { |
| max_score = scores[i]; |
| max_score_index = i; |
| } |
| |
| if (max_score_index != 0) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, |
| "Rejecting Radar since Fractional PRI detected: searchpri=%d, threshold=%d, fractional PRI=%d, Fractional PRI score=%d", |
| dl->dl_search_pri, scores[0], |
| dl->dl_search_pri/(max_score_index + 1), |
| max_score); |
| return 0; |
| } |
| |
| |
| /* Check for frequency spread */ |
| if (dl->dl_min_sidx > pl->pl_elems[start_index].p_sidx) |
| dl->dl_min_sidx = pl->pl_elems[start_index].p_sidx; |
| |
| if (dl->dl_max_sidx < pl->pl_elems[start_index].p_sidx) |
| dl->dl_max_sidx = pl->pl_elems[start_index].p_sidx; |
| |
| if ((dl->dl_max_sidx - dl->dl_min_sidx) > rf->rf_sidx_spread) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, |
| "Rejecting Radar since frequency spread is too large : min_sidx=%d, max_sidx=%d, rf_sidx_spread=%d", |
| dl->dl_min_sidx, dl->dl_max_sidx, |
| rf->rf_sidx_spread); |
| return 0; |
| } |
| |
| if ((rf->rf_check_delta_peak) && |
| ((dl->dl_delta_peak_match_count + |
| dl->dl_psidx_diff_match_count - 1) < |
| rf->rf_threshold)) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, |
| "Rejecting Radar since delta peak values are invalid : dl_delta_peak_match_count=%d, dl_psidx_diff_match_count=%d, rf_threshold=%d", |
| dl->dl_delta_peak_match_count, |
| dl->dl_psidx_diff_match_count, |
| rf->rf_threshold); |
| return 0; |
| } |
| dfs_debug(dfs, WLAN_DEBUG_DFS_FALSE_DET, "%s : dl->dl_min_sidx: %d , dl->dl_max_sidx :%d", |
| __func__, dl->dl_min_sidx, dl->dl_max_sidx); |
| |
| dfs->dfs_freq_offset = DFS_SIDX_TO_FREQ_OFFSET((dl->dl_min_sidx + |
| dl->dl_max_sidx) / 2); |
| return 1; |
| } |
| |
| /* |
| * dfs_reject_on_pri() - Rejecting on individual filter based on min PRI . |
| * @dfs: Pointer to wlan_dfs structure. |
| * @rf: Pointer to dfs_filter structure. |
| * @deltaT: deltaT value. |
| * @this_ts: Timestamp. |
| */ |
| static inline bool dfs_reject_on_pri( |
| struct wlan_dfs *dfs, |
| struct dfs_filter *rf, |
| uint64_t deltaT, |
| uint64_t this_ts) |
| { |
| if ((deltaT < rf->rf_minpri) && (deltaT != 0)) { |
| /* Second line of PRI filtering. */ |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "filterID %d : Rejecting on individual filter min PRI deltaT=%lld rf->rf_minpri=%u", |
| rf->rf_pulseid, (uint64_t)deltaT, |
| rf->rf_minpri); |
| return 1; |
| } |
| |
| if (rf->rf_ignore_pri_window > 0) { |
| if (deltaT < rf->rf_minpri) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "filterID %d : Rejecting on individual filter max PRI deltaT=%lld rf->rf_minpri=%u", |
| rf->rf_pulseid, (uint64_t)deltaT, |
| rf->rf_minpri); |
| /* But update the last time stamp. */ |
| rf->rf_dl.dl_last_ts = this_ts; |
| return 1; |
| } |
| } else { |
| /* |
| * The HW may miss some pulses especially with |
| * high channel loading. This is true for Japan |
| * W53 where channel loaoding is 50%. Also for |
| * ETSI where channel loading is 30% this can |
| * be an issue too. To take care of missing |
| * pulses, we introduce pri_margin multiplie. |
| * This is normally 2 but can be higher for W53. |
| */ |
| |
| if ((deltaT > (dfs->dfs_pri_multiplier * rf->rf_maxpri)) || |
| (deltaT < rf->rf_minpri)) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "filterID %d : Rejecting on individual filter max PRI deltaT=%lld rf->rf_minpri=%u", |
| rf->rf_pulseid, (uint64_t) deltaT, |
| rf->rf_minpri); |
| /* But update the last time stamp. */ |
| rf->rf_dl.dl_last_ts = this_ts; |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * dfs_confirm_radar_check() - Do additioal check to conirm radar except for |
| * the staggered, chirp FCC Bin 5, frequency hopping indicated by |
| * rf_patterntype == 1. |
| * @dfs: Pointer to wlan_dfs structure. |
| * @rf: Pointer to dfs_filter structure. |
| * @ext_chan_event_flag: Extension channel event flag |
| * @found: Pointer to radar found flag (return value). |
| * @false_radar_found: Pointer to false radar found (return value). |
| */ |
| |
| static inline void dfs_confirm_radar_check( |
| struct wlan_dfs *dfs, |
| struct dfs_filter *rf, |
| int ext_chan_event_flag, |
| int *found, |
| int *false_radar_found) |
| { |
| if (rf->rf_patterntype != 1) { |
| *found = dfs_confirm_radar(dfs, rf, ext_chan_event_flag); |
| *false_radar_found = (*found == 1) ? 0 : 1; |
| } |
| } |
| |
| void __dfs_process_radarevent(struct wlan_dfs *dfs, |
| struct dfs_filtertype *ft, |
| struct dfs_event *re, |
| uint64_t this_ts, |
| int *found, |
| int *false_radar_found) |
| { |
| int p; |
| uint64_t deltaT = 0; |
| int ext_chan_event_flag = 0; |
| struct dfs_filter *rf = NULL; |
| int8_t ori_rf_check_delta_peak = 0; |
| |
| for (p = 0, *found = 0; (p < ft->ft_numfilters) && |
| (!(*found)) && !(*false_radar_found); p++) { |
| rf = ft->ft_filters[p]; |
| if ((re->re_dur >= rf->rf_mindur) && |
| (re->re_dur <= rf->rf_maxdur)) { |
| /* The above check is probably not necessary. */ |
| deltaT = (this_ts < rf->rf_dl.dl_last_ts) ? |
| (int64_t)((DFS_TSF_WRAP - rf->rf_dl.dl_last_ts) + |
| this_ts + 1) : |
| this_ts - rf->rf_dl.dl_last_ts; |
| |
| if (dfs_reject_on_pri(dfs, rf, deltaT, this_ts)) |
| continue; |
| |
| dfs_add_pulse(dfs, rf, re, deltaT, this_ts); |
| |
| /* |
| * If this is an extension channel event, flag it for |
| * false alarm reduction. |
| */ |
| if (re->re_chanindex == dfs->dfs_extchan_radindex) |
| ext_chan_event_flag = 1; |
| |
| if (rf->rf_patterntype == 2) { |
| *found = dfs_staggered_check(dfs, rf, |
| (uint32_t) deltaT, re->re_dur); |
| } else { |
| *found = dfs_bin_check(dfs, rf, |
| (uint32_t) deltaT, re->re_dur, |
| ext_chan_event_flag); |
| |
| if (*found && |
| (utils_get_dfsdomain(dfs->dfs_pdev_obj) != |
| DFS_CN_DOMAIN)) { |
| ori_rf_check_delta_peak = |
| rf->rf_check_delta_peak; |
| /* |
| * If FW does not send valid psidx_diff |
| * Do not do chirp check. |
| */ |
| if (rf->rf_check_delta_peak && |
| (!(re->re_flags & |
| DFS_EVENT_VALID_PSIDX_DIFF))) |
| rf->rf_check_delta_peak = false; |
| dfs_confirm_radar_check(dfs, |
| rf, ext_chan_event_flag, |
| found, |
| false_radar_found); |
| rf->rf_check_delta_peak = |
| ori_rf_check_delta_peak; |
| } |
| } |
| |
| if (dfs->dfs_debug_mask & WLAN_DEBUG_DFS2) |
| if (rf->rf_patterntype != |
| WLAN_DFS_RF_PATTERN_TYPE_1) |
| dfs_print_delayline(dfs, &rf->rf_dl); |
| |
| rf->rf_dl.dl_last_ts = this_ts; |
| } |
| } |
| |
| if (*found) { |
| dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, |
| "Found on channel minDur = %d, filterId = %d", |
| ft->ft_mindur, |
| rf ? rf->rf_pulseid : -1); |
| } |
| |
| return; |
| } |
| |
| /** |
| * dfs_cal_average_radar_parameters() - Calculate the average radar parameters. |
| * @dfs: Pointer to wlan_dfs structure. |
| */ |
| #if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(HOST_DFS_SPOOF_TEST) |
| static void dfs_cal_average_radar_parameters(struct wlan_dfs *dfs) |
| { |
| int i, count = 0; |
| u_int32_t total_pri = 0; |
| u_int32_t total_duration = 0; |
| u_int32_t total_sidx = 0; |
| |
| /* Calculating average PRI, Duration, SIDX from |
| * the 2nd pulse, ignoring the 1st pulse (radar_log[0]). |
| * This is because for the first pulse, the diff_ts will be |
| * (0 - current_ts) which will be a huge value. |
| * Average PRI computation will be wrong. FW returns a |
| * failure test result as PRI does not match their expected |
| * value. |
| */ |
| |
| for (i = 1; (i < DFS_EVENT_LOG_SIZE) && (i < dfs->dfs_event_log_count); |
| i++) { |
| total_pri += dfs->radar_log[i].diff_ts; |
| total_duration += dfs->radar_log[i].dur; |
| total_sidx += dfs->radar_log[i].sidx; |
| count++; |
| } |
| |
| if (count > 0) { |
| dfs->dfs_average_pri = total_pri / count; |
| dfs->dfs_average_duration = total_duration / count; |
| dfs->dfs_average_sidx = total_sidx / count; |
| |
| dfs_info(dfs, WLAN_DEBUG_DFS2, |
| "Avg.PRI =%u, Avg.duration =%u Avg.sidx =%u", |
| dfs->dfs_average_pri, |
| dfs->dfs_average_duration, |
| dfs->dfs_average_sidx); |
| } |
| } |
| #else |
| static void dfs_cal_average_radar_parameters(struct wlan_dfs *dfs) |
| { |
| } |
| #endif |
| |
| /** |
| * dfs_radarfound_reset_vars() - Reset dfs variables after radar found |
| * @dfs: Pointer to wlan_dfs structure. |
| * @rs: Pointer to dfs_state. |
| * @chan: Current channel. |
| * @seg_id: Segment id. |
| */ |
| static inline void dfs_radarfound_reset_vars( |
| struct wlan_dfs *dfs, |
| struct dfs_state *rs, |
| struct dfs_channel *chan, |
| uint8_t seg_id) |
| { |
| struct dfs_channel *thischan; |
| |
| /* |
| * TODO: Instead of discarding the radar, create a workqueue |
| * if the channel change is happenning through userspace and |
| * process the radar event once the channel change is completed. |
| */ |
| |
| /* Collect stats */ |
| dfs->wlan_dfs_stats.num_radar_detects++; |
| thischan = &rs->rs_chan; |
| if ((seg_id == SEG_ID_SECONDARY) && |
| (dfs_is_precac_timer_running(dfs))) |
| dfs->is_radar_during_precac = 1; |
| |
| /* |
| * If event log is on then dump the radar event queue on |
| * filter match. This can be used to collect information |
| * on false radar detection. |
| */ |
| if (dfs->dfs_event_log_on) { |
| dfs_cal_average_radar_parameters(dfs); |
| dfs_print_radar_events(dfs); |
| } |
| |
| dfs_reset_radarq(dfs); |
| dfs_reset_alldelaylines(dfs); |
| |
| dfs_debug(dfs, WLAN_DEBUG_DFS1, |
| "Primary channel freq = %u flags=0x%x", |
| chan->dfs_ch_freq, chan->dfs_ch_flagext); |
| |
| if (chan->dfs_ch_freq != thischan->dfs_ch_freq) |
| dfs_debug(dfs, WLAN_DEBUG_DFS1, |
| "Ext channel freq = %u flags=0x%x", |
| thischan->dfs_ch_freq, |
| thischan->dfs_ch_flagext); |
| |
| dfs->dfs_phyerr_freq_min = 0x7fffffff; |
| dfs->dfs_phyerr_freq_max = 0; |
| dfs->dfs_phyerr_w53_counter = 0; |
| |
| if (seg_id == SEG_ID_SECONDARY) { |
| dfs->wlan_dfs_stats.num_seg_two_radar_detects++; |
| dfs->is_radar_found_on_secondary_seg = 1; |
| } |
| } |
| |
| /* |
| * dfs_print_radar_found_freq() - Print radar found frequency. |
| * @dfs: Pointer to wlan_dfs. |
| */ |
| #ifdef CONFIG_CHAN_FREQ_API |
| static void dfs_print_radar_found_freq(struct wlan_dfs *dfs) |
| { |
| dfs_debug(dfs, WLAN_DEBUG_DFS, |
| "bangradar on 2nd segment cfreq = %u", |
| dfs->dfs_precac_secondary_freq_mhz); |
| } |
| #else |
| #ifdef CONFIG_CHAN_NUM_API |
| static void dfs_print_radar_found_freq(struct wlan_dfs *dfs) |
| { |
| dfs_debug(dfs, WLAN_DEBUG_DFS, |
| "bangradar on 2nd segment cfreq = %u", |
| dfs->dfs_precac_secondary_freq); |
| } |
| #endif |
| #endif |
| |
| /** |
| * dfs_handle_bangradar - Handle the case of bangradar |
| * @dfs: Pointer to wlan_dfs structure. |
| * @chan: Current channel. |
| * @rs: Pointer to dfs_state. |
| * Return: if bangradar then return 1. Otherwise, return 0. |
| */ |
| static inline int dfs_handle_bangradar( |
| struct wlan_dfs *dfs, |
| struct dfs_channel *chan, |
| struct dfs_state **rs, |
| uint8_t *seg_id, |
| int *retval) |
| { |
| |
| if (dfs->dfs_bangradar_type) { |
| if (dfs->dfs_bangradar_type >= DFS_INVALID_BANGRADAR_TYPE) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS, |
| "Invalid bangradar type"); |
| return 1; |
| } |
| /* All bangradars are processed similarly. |
| * arguments for the bangradar are already stored in |
| * respective dfs structures. |
| */ |
| |
| *rs = &dfs->dfs_radar[dfs->dfs_curchan_radindex]; |
| if (dfs->dfs_seg_id == SEG_ID_SECONDARY) { |
| if (dfs_is_precac_timer_running(dfs) || |
| WLAN_IS_CHAN_11AC_VHT160(chan) || |
| WLAN_IS_CHAN_11AC_VHT80_80(chan)) { |
| dfs->is_radar_found_on_secondary_seg = 1; |
| dfs_print_radar_found_freq(dfs); |
| } else { |
| dfs_debug(dfs, WLAN_DEBUG_DFS, |
| "No second segment"); |
| return 1; |
| } |
| } |
| *seg_id = dfs->dfs_seg_id; |
| dfs_debug(dfs, WLAN_DEBUG_DFS, "bangradar %d", |
| dfs->dfs_bangradar_type); |
| *retval = 1; |
| return 1; |
| } |
| return 0; |
| } |
| |
| /** |
| * dfs_process_w53_pulses() - Prrocess w53 pulses |
| * @dfs: Pointer to wlan_dfs structure. |
| * |
| * For chips that support frequency information, we can relax PRI |
| * restriction if the frequency spread is narrow. |
| */ |
| static inline void dfs_process_w53_pulses( |
| struct wlan_dfs *dfs) |
| { |
| if ((dfs->dfs_phyerr_freq_max - dfs->dfs_phyerr_freq_min) < |
| DFS_MAX_FREQ_SPREAD) |
| dfs->dfs_pri_multiplier = DFS_LARGE_PRI_MULTIPLIER; |
| |
| dfs_debug(dfs, WLAN_DEBUG_DFS1, |
| "w53_counter=%d, freq_max=%d, freq_min=%d, pri_multiplier=%d", |
| dfs->dfs_phyerr_w53_counter, |
| dfs->dfs_phyerr_freq_max, dfs->dfs_phyerr_freq_min, |
| dfs->dfs_pri_multiplier); |
| |
| dfs->dfs_phyerr_freq_min = 0x7fffffff; |
| dfs->dfs_phyerr_freq_max = 0; |
| } |
| |
| /** |
| * dfs_handle_missing_pulses - Handle the case of missing pulses |
| * @dfs: Pointer to wlan_dfs structure. |
| * @chan: Current channel. |
| * |
| * The HW may miss some pulses especially with high channel loading. |
| * This is true for Japan W53 where channel loaoding is 50%. Also |
| * for ETSI where channel loading is 30% this can be an issue too. |
| * To take care of missing pulses, we introduce pri_margin multiplie. |
| * This is normally 2 but can be higher for W53. |
| * Return: If not enough pulses return 0. Otherwise, return 1. |
| */ |
| static inline int dfs_handle_missing_pulses( |
| struct wlan_dfs *dfs, |
| struct dfs_channel *chan) |
| { |
| if ((dfs->dfsdomain == DFS_MKK4_DOMAIN) && |
| (dfs->dfs_caps.wlan_chip_is_bb_tlv) && |
| (chan->dfs_ch_freq < FREQ_5500_MHZ)) { |
| dfs->dfs_pri_multiplier = DFS_W53_DEFAULT_PRI_MULTIPLIER; |
| /* |
| * Do not process W53 pulses unless we have a minimum number |
| * of them. |
| */ |
| if (dfs->dfs_phyerr_w53_counter >= 5) |
| dfs_process_w53_pulses(dfs); |
| else |
| return 0; |
| } |
| |
| dfs_debug(dfs, WLAN_DEBUG_DFS1, "pri_multiplier=%d", |
| dfs->dfs_pri_multiplier); |
| |
| return 1; |
| } |
| |
| /** |
| * dfs_is_radarq_empty - check if radarq is empty |
| * @dfs: Pointer to wlan_dfs structure. |
| * @empty: Pointer to empty |
| */ |
| static inline void dfs_is_radarq_empty( |
| struct wlan_dfs *dfs, |
| int *empty) |
| { |
| WLAN_DFSQ_LOCK(dfs); |
| *empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| WLAN_DFSQ_UNLOCK(dfs); |
| } |
| |
| /** |
| * dfs_remove_event_from_radarq - remove event from radarq |
| * @dfs: Pointer to wlan_dfs structure. |
| * @event: Double pointer to the event structure |
| */ |
| static inline void dfs_remove_event_from_radarq( |
| struct wlan_dfs *dfs, |
| struct dfs_event **event) |
| { |
| WLAN_DFSQ_LOCK(dfs); |
| *event = STAILQ_FIRST(&(dfs->dfs_radarq)); |
| if (*event) |
| STAILQ_REMOVE_HEAD(&(dfs->dfs_radarq), re_list); |
| WLAN_DFSQ_UNLOCK(dfs); |
| } |
| |
| /** |
| * dfs_return_event_to_eventq - return event to eventq |
| * @dfs: Pointer to wlan_dfs structure. |
| * @event: Pointer to the event structure |
| */ |
| static inline void dfs_return_event_to_eventq( |
| struct wlan_dfs *dfs, |
| struct dfs_event *event) |
| { |
| qdf_mem_zero(event, sizeof(struct dfs_event)); |
| WLAN_DFSEVENTQ_LOCK(dfs); |
| STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), event, re_list); |
| WLAN_DFSEVENTQ_UNLOCK(dfs); |
| } |
| |
| /** |
| * dfs_log_event - log dfs event |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to dfs_event re |
| * @this_ts: Current time stamp 64bit |
| * @diff_ts: Difference between 2 timestamps 32bit |
| * @index: Index value. |
| */ |
| static inline void dfs_log_event( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re, |
| uint64_t this_ts, |
| uint32_t diff_ts, |
| uint32_t index) |
| { |
| uint8_t i; |
| struct dfs_pulseline *pl = dfs->pulses; |
| |
| if (dfs->dfs_event_log_on) { |
| i = dfs->dfs_event_log_count % DFS_EVENT_LOG_SIZE; |
| dfs->radar_log[i].ts = this_ts; |
| dfs->radar_log[i].diff_ts = diff_ts; |
| dfs->radar_log[i].rssi = (*re).re_rssi; |
| dfs->radar_log[i].dur = (*re).re_dur; |
| dfs->radar_log[i].seg_id = (*re).re_seg_id; |
| dfs->radar_log[i].sidx = (*re).re_sidx; |
| dfs->radar_log[i].freq_offset_khz = |
| (*re).re_freq_offset_khz; |
| dfs->radar_log[i].peak_mag = (*re).re_peak_mag; |
| dfs->radar_log[i].total_gain = (*re).re_total_gain; |
| dfs->radar_log[i].mb_gain = (*re).re_mb_gain; |
| dfs->radar_log[i].relpwr_db = (*re).re_relpwr_db; |
| dfs->radar_log[i].delta_diff = (*re).re_delta_diff; |
| dfs->radar_log[i].delta_peak = (*re).re_delta_peak; |
| dfs->radar_log[i].psidx_diff = (*re).re_psidx_diff; |
| dfs->radar_log[i].is_chirp = DFS_EVENT_NOTCHIRP(re) ? |
| 0 : 1; |
| dfs->dfs_event_log_count++; |
| } |
| |
| dfs->dfs_seq_num++; |
| pl->pl_elems[index].p_seq_num = dfs->dfs_seq_num; |
| } |
| |
| /** |
| * dfs_check_if_nonbin5 - Check if radar, other than bin5, is found |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to re (radar event) |
| * @rs: Double Pointer to rs (radar state) |
| * @this_ts: Current time stamp 64bit |
| * @diff_ts: Difference between 2 timestamps 32bit |
| * @found: Pointer to found. If radar found or not. |
| * @retval: Pointer to retval(return value). |
| * @false_radar_found: Pointer to false_radar_found(return value). |
| */ |
| static inline void dfs_check_if_nonbin5( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re, |
| struct dfs_state **rs, |
| uint64_t this_ts, |
| uint32_t diff_ts, |
| int *found, |
| int *retval, |
| int *false_radar_found) |
| { |
| |
| uint32_t tabledepth = 0; |
| struct dfs_filtertype *ft; |
| uint64_t deltaT; |
| |
| dfs_debug(dfs, WLAN_DEBUG_DFS1, |
| " *** chan freq (%d): ts %llu dur %u rssi %u", |
| (*rs)->rs_chan.dfs_ch_freq, (uint64_t)this_ts, |
| (*re).re_dur, (*re).re_rssi); |
| |
| while ((tabledepth < DFS_MAX_RADAR_OVERLAP) && |
| ((dfs->dfs_ftindextable[(*re).re_dur])[tabledepth] != |
| -1) && (!*retval) && !(*false_radar_found)) { |
| ft = dfs->dfs_radarf[((dfs->dfs_ftindextable[(*re).re_dur]) |
| [tabledepth])]; |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| " ** RD (%d): ts %x dur %u rssi %u", |
| (*rs)->rs_chan.dfs_ch_freq, (*re).re_ts, |
| (*re).re_dur, (*re).re_rssi); |
| |
| if ((*re).re_rssi < ft->ft_rssithresh && |
| (*re).re_dur > MAX_DUR_FOR_LOW_RSSI) { |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "Rejecting on rssi rssi=%u thresh=%u", |
| (*re).re_rssi, |
| ft->ft_rssithresh); |
| tabledepth++; |
| continue; |
| } |
| deltaT = this_ts - ft->ft_last_ts; |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "deltaT = %lld (ts: 0x%llx) (last ts: 0x%llx)", |
| (uint64_t)deltaT, (uint64_t)this_ts, |
| (uint64_t)ft->ft_last_ts); |
| |
| if ((deltaT < ft->ft_minpri) && (deltaT != 0)) { |
| /* |
| * This check is for the whole filter type. |
| * Individual filters will check this again. |
| * This is first line of filtering. |
| */ |
| dfs_debug(dfs, WLAN_DEBUG_DFS2, |
| "Rejecting on pri pri=%lld minpri=%u", |
| (uint64_t)deltaT, ft->ft_minpri); |
| tabledepth++; |
| continue; |
| } |
| |
| __dfs_process_radarevent(dfs, ft, re, this_ts, found, |
| false_radar_found); |
| |
| ft->ft_last_ts = this_ts; |
| *retval |= *found; |
| tabledepth++; |
| } |
| } |
| |
| /** |
| * dfs_check_each_b5radar() - Check each bin5 radar |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to re(radar event). |
| * @br: Pointer to dfs_bin5radars structure. |
| * @this_ts: Current time stamp 64bit. |
| * @diff_ts: Difference between 2 timestamps 32bit. |
| * @found: Pointer to found. If radar found or not. |
| */ |
| static inline void dfs_check_each_b5radar( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re, |
| struct dfs_bin5radars *br, |
| uint64_t this_ts, |
| uint32_t diff_ts, |
| int *found) |
| { |
| if (dfs_bin5_check_pulse(dfs, re, br)) { |
| /* |
| * This is a valid Bin5 pulse, check if it belongs to a |
| * burst. |
| */ |
| (*re).re_dur = dfs_retain_bin5_burst_pattern(dfs, diff_ts, |
| (*re).re_dur); |
| /* |
| * Remember our computed duration for the next pulse in the |
| * burst (if needed). |
| */ |
| dfs->dfs_rinfo.dfs_bin5_chirp_ts = this_ts; |
| dfs->dfs_rinfo.dfs_last_bin5_dur = (*re).re_dur; |
| |
| if (dfs_bin5_addpulse(dfs, br, re, this_ts)) |
| *found |= dfs_bin5_check(dfs); |
| } else { |
| dfs_debug(dfs, WLAN_DEBUG_DFS_BIN5_PULSE, |
| "not a BIN5 pulse (dur=%d)", (*re).re_dur); |
| } |
| } |
| |
| /** |
| * dfs_check_if_bin5() - Check if bin5 radar is found |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to re(radar event). |
| * @this_ts: Current time stamp 64bit. |
| * @diff_ts: Difference between 2 timestamps 32bit. |
| * @found: Pointer to found. If radar found or not. |
| */ |
| static inline void dfs_check_if_bin5( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re, |
| uint64_t this_ts, |
| uint32_t diff_ts, |
| int *found) |
| { |
| int p; |
| |
| /* BIN5 pulses are FCC and Japan specific. */ |
| if ((dfs->dfsdomain == DFS_FCC_DOMAIN) || |
| (dfs->dfsdomain == DFS_MKK4_DOMAIN)) { |
| for (p = 0; (p < dfs->dfs_rinfo.rn_numbin5radars) && (!*found); |
| p++) { |
| struct dfs_bin5radars *br; |
| |
| br = &(dfs->dfs_b5radars[p]); |
| dfs_check_each_b5radar(dfs, re, br, this_ts, diff_ts, |
| found); |
| } |
| } |
| |
| if (*found) |
| dfs_debug(dfs, WLAN_DEBUG_DFS, "Found bin5 radar"); |
| } |
| |
| /** |
| * dfs_skip_the_event() - Skip the Radar event |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to re(radar event). |
| * @rs: Pointer to dfs_state. |
| */ |
| static inline bool dfs_skip_the_event( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re, |
| struct dfs_state **rs) |
| { |
| if ((*re).re_chanindex < DFS_NUM_RADAR_STATES) |
| (*rs) = &dfs->dfs_radar[(*re).re_chanindex]; |
| else |
| return 1; |
| |
| if ((*rs)->rs_chan.dfs_ch_flagext & CHANNEL_INTERFERENCE) |
| return 1; |
| |
| return 0; |
| } |
| |
| /** |
| * dfs_check_ts_wrap() - dfs check for timestamp wrap. |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to re(radar event). |
| * @deltafull_ts: Deltafull ts. |
| * |
| * Return: Deltafull ts. |
| */ |
| static inline uint64_t dfs_check_ts_wrap( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re, |
| uint64_t deltafull_ts) |
| { |
| if (deltafull_ts > |
| ((uint64_t)((DFS_TSMASK - |
| dfs->dfs_rinfo.rn_last_ts) + |
| 1 + (*re).re_ts))) |
| deltafull_ts -= |
| (DFS_TSMASK - dfs->dfs_rinfo.rn_last_ts) + |
| 1 + (*re).re_ts; |
| |
| return deltafull_ts; |
| } |
| |
| /** |
| * dfs_calculate_ts_prefix() - Calculate deltafull ts value. |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to re(radar event). |
| */ |
| static inline void dfs_calculate_ts_prefix( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re) |
| { |
| uint64_t deltafull_ts; |
| |
| if ((*re).re_ts <= dfs->dfs_rinfo.rn_last_ts) { |
| dfs->dfs_rinfo.rn_ts_prefix += (((uint64_t) 1) << DFS_TSSHIFT); |
| /* Now, see if it's been more than 1 wrap */ |
| deltafull_ts = (*re).re_full_ts - dfs->dfs_rinfo.rn_lastfull_ts; |
| deltafull_ts = dfs_check_ts_wrap(dfs, re, deltafull_ts); |
| deltafull_ts >>= DFS_TSSHIFT; |
| |
| if (deltafull_ts > 1) |
| dfs->dfs_rinfo.rn_ts_prefix += |
| ((deltafull_ts - 1) << DFS_TSSHIFT); |
| } else { |
| deltafull_ts = (*re).re_full_ts - |
| dfs->dfs_rinfo.rn_lastfull_ts; |
| if (deltafull_ts > (uint64_t) DFS_TSMASK) { |
| deltafull_ts >>= DFS_TSSHIFT; |
| dfs->dfs_rinfo.rn_ts_prefix += |
| ((deltafull_ts - 1) << DFS_TSSHIFT); |
| } |
| } |
| } |
| |
| /** |
| * dfs_calculate_timestamps() - Calculate various timestamps |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to re(radar event) |
| * @this_ts : Pointer to this_ts (this timestamp) |
| */ |
| |
| static inline void dfs_calculate_timestamps( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re, |
| uint64_t *this_ts) |
| { |
| if (dfs->dfs_rinfo.rn_lastfull_ts == 0) { |
| /* |
| * Either not started, or 64-bit rollover exactly to |
| * zero Just prepend zeros to the 15-bit ts. |
| */ |
| dfs->dfs_rinfo.rn_ts_prefix = 0; |
| } else { |
| /* WAR 23031- patch duplicate ts on very short pulses. |
| * This pacth has two problems in linux environment. |
| * 1)The time stamp created and hence PRI depends |
| * entirely on the latency. If the latency is high, it |
| * possibly can split two consecutive pulses in the |
| * same burst so far away (the same amount of latency) |
| * that make them look like they are from differenct |
| * bursts. It is observed to happen too often. It sure |
| * makes the detection fail. |
| * 2)Even if the latency is not that bad, it simply |
| * shifts the duplicate timestamps to a new duplicate |
| * timestamp based on how they are processed. |
| * This is not worse but not good either. |
| * Take this pulse as a good one and create a probable |
| * PRI later. |
| */ |
| if ((*re).re_dur == 0 && (*re).re_ts == |
| dfs->dfs_rinfo.rn_last_unique_ts) { |
| debug_dup[debug_dup_cnt++] = '1'; |
| dfs_debug(dfs, WLAN_DEBUG_DFS1, "deltaT is 0"); |
| } else { |
| dfs->dfs_rinfo.rn_last_unique_ts = (*re).re_ts; |
| debug_dup[debug_dup_cnt++] = '0'; |
| } |
| |
| if (debug_dup_cnt >= 32) |
| debug_dup_cnt = 0; |
| |
| dfs_calculate_ts_prefix(dfs, re); |
| } |
| |
| /* |
| * At this stage rn_ts_prefix has either been blanked or |
| * calculated, so it's safe to use. |
| */ |
| *this_ts = dfs->dfs_rinfo.rn_ts_prefix | ((uint64_t) (*re).re_ts); |
| dfs->dfs_rinfo.rn_lastfull_ts = (*re).re_full_ts; |
| dfs->dfs_rinfo.rn_last_ts = (*re).re_ts; |
| } |
| |
| /** |
| * dfs_add_to_pulseline - Extract necessary items from dfs_event and |
| * add it as pulse in the pulseline |
| * @dfs: Pointer to wlan_dfs structure. |
| * @re: Pointer to re(radar event) |
| * @this_ts: Pointer to this_ts (this timestamp) |
| * @diff_ts: Diff ts. |
| * @index: Pointer to get index value. |
| */ |
| static inline void dfs_add_to_pulseline( |
| struct wlan_dfs *dfs, |
| struct dfs_event *re, |
| uint64_t *this_ts, |
| uint32_t *test_ts, |
| uint32_t *diff_ts, |
| uint32_t *index) |
| { |
| struct dfs_pulseline *pl; |
| |
| /* |
| * Calculate the start of the radar pulse. |
| * |
| * The TSF is stamped by the MAC upon reception of the event, |
| * which is (typically?) at the end of the event. But the |
| * pattern matching code expects the event timestamps to be at |
| * the start of the event. So to fake it, we subtract the pulse |
| * duration from the given TSF. This is done after the 64-bit |
| * timestamp has been calculated so long pulses correctly |
| * under-wrap the counter. Ie, if this was done on the 32 |
| * (or 15!) bit TSF when the TSF value is closed to 0, it will |
| * underflow to 0xfffffXX, which would mess up the logical "OR" |
| * operation done above. |
| * This isn't valid for Peregrine as the hardware gives us the |
| * actual TSF offset of the radar event, not just the MAC TSF |
| * of the completed receive. |
| * |
| * XXX TODO: ensure that the TLV PHY error processing code will |
| * correctly calculate the TSF to be the start of the radar |
| * pulse. |
| * |
| * XXX TODO TODO: modify the TLV parsing code to subtract the |
| * duration from the TSF, based on the current fast clock value. |
| */ |
| if ((!dfs->dfs_caps.wlan_chip_is_bb_tlv) && (*re).re_dur != 1) |
| *this_ts -= (*re).re_dur; |
| |
| pl = dfs->pulses; |
| /* Save the pulse parameters in the pulse buffer(pulse line). */ |
| *index = (pl->pl_lastelem + 1) & DFS_MAX_PULSE_BUFFER_MASK; |
| |
| if (pl->pl_numelems == DFS_MAX_PULSE_BUFFER_SIZE) |
| pl->pl_firstelem = (pl->pl_firstelem+1) & |
| DFS_MAX_PULSE_BUFFER_MASK; |
| else |
| pl->pl_numelems++; |
| |
| pl->pl_lastelem = *index; |
| pl->pl_elems[*index].p_time = *this_ts; |
| pl->pl_elems[*index].p_dur = (*re).re_dur; |
| pl->pl_elems[*index].p_rssi = (*re).re_rssi; |
| pl->pl_elems[*index].p_sidx = (*re).re_sidx; |
| pl->pl_elems[*index].p_delta_peak = (*re).re_delta_peak; |
| pl->pl_elems[*index].p_psidx_diff = (*re).re_psidx_diff; |
| *diff_ts = (uint32_t)*this_ts - *test_ts; |
| *test_ts = (uint32_t)*this_ts; |
| |
| dfs_debug(dfs, WLAN_DEBUG_DFS1, |
| "ts%u %u %u diff %u pl->pl_lastelem.p_time=%llu", |
| (uint32_t)*this_ts, (*re).re_dur, |
| (*re).re_rssi, *diff_ts, |
| (uint64_t)pl->pl_elems[*index].p_time); |
| } |
| |
| /** |
| * dfs_conditional_clear_delaylines - Clear delay lines to remove the |
| * false pulses. |
| * @dfs: Pointer to wlan_dfs structure. |
| * @diff_ts: diff between timerstamps. |
| * @this_ts: this timestamp value. |
| * @re: Pointer to dfs_event structure. |
| */ |
| static inline void dfs_conditional_clear_delaylines( |
| struct wlan_dfs *dfs, |
| uint32_t diff_ts, |
| uint64_t this_ts, |
| struct dfs_event re) |
| { |
| struct dfs_pulseline *pl = dfs->pulses; |
| uint32_t index; |
| |
| /* If diff_ts is very small, we might be getting false pulse |
| * detects due to heavy interference. We might be getting |
| * spectral splatter from adjacent channel. In order to prevent |
| * false alarms we clear the delay-lines. This might impact |
| * positive detections under harsh environments, but helps with |
| * false detects. |
| */ |
| |
| if (diff_ts < DFS_INVALID_PRI_LIMIT) { |
| dfs->dfs_seq_num = 0; |
| dfs_reset_alldelaylines(dfs); |
| dfs_reset_radarq(dfs); |
| |
| index = (pl->pl_lastelem + 1) & DFS_MAX_PULSE_BUFFER_MASK; |
| if (pl->pl_numelems == DFS_MAX_PULSE_BUFFER_SIZE) |
| pl->pl_firstelem = (pl->pl_firstelem+1) & |
| DFS_MAX_PULSE_BUFFER_MASK; |
| else |
| pl->pl_numelems++; |
| |
| pl->pl_lastelem = index; |
| pl->pl_elems[index].p_time = this_ts; |
| pl->pl_elems[index].p_dur = re.re_dur; |
| pl->pl_elems[index].p_rssi = re.re_rssi; |
| pl->pl_elems[index].p_sidx = re.re_sidx; |
| pl->pl_elems[index].p_delta_peak = re.re_delta_peak; |
| pl->pl_elems[index].p_psidx_diff = re.re_psidx_diff; |
| dfs->dfs_seq_num++; |
| pl->pl_elems[index].p_seq_num = dfs->dfs_seq_num; |
| } |
| } |
| |
| /** |
| * dfs_process_each_radarevent - remove each event from the dfs radar queue |
| * and process it. |
| * @dfs: Pointer to wlan_dfs structure. |
| * @chan: Pointer to DFS current channel. |
| * @rs: Pointer to dfs_state structure. |
| * @seg_id: segment id. |
| * @retval: pointer to retval. |
| * @false_radar_found: pointer to false radar found. |
| * |
| * Return: If radar found then return 1 else return 0. |
| */ |
| static inline int dfs_process_each_radarevent( |
| struct wlan_dfs *dfs, |
| struct dfs_channel *chan, |
| struct dfs_state **rs, |
| uint8_t *seg_id, |
| int *retval, |
| int *false_radar_found) |
| { |
| struct dfs_event re, *event; |
| int found, empty; |
| int events_processed = 0; |
| uint64_t this_ts; |
| static uint32_t test_ts; |
| static uint32_t diff_ts; |
| uint32_t index; |
| |
| dfs_is_radarq_empty(dfs, &empty); |
| |
| while ((!empty) && (!*retval) && !(*false_radar_found) && |
| (events_processed < MAX_EVENTS)) { |
| dfs_remove_event_from_radarq(dfs, &event); |
| if (!event) { |
| empty = 1; |
| break; |
| } |
| events_processed++; |
| re = *event; |
| |
| dfs_return_event_to_eventq(dfs, event); |
| |
| *seg_id = re.re_seg_id; |
| found = 0; |
| if (dfs_skip_the_event(dfs, &re, rs)) { |
| dfs_is_radarq_empty(dfs, &empty); |
| continue; |
| } |
| |
| dfs_calculate_timestamps(dfs, &re, &this_ts); |
| |
| re.re_dur = dfs_process_pulse_dur(dfs, re.re_dur); |
| |
| dfs_add_to_pulseline(dfs, &re, &this_ts, &test_ts, &diff_ts, |
| &index); |
| |
| dfs_dur_check(dfs, chan, &re, diff_ts); |
| |
| dfs_log_event(dfs, &re, this_ts, diff_ts, index); |
| |
| dfs_conditional_clear_delaylines(dfs, diff_ts, this_ts, re); |
| |
| found = 0; |
| if (events_processed == 1) { |
| dfs->dfs_min_sidx = (re).re_sidx; |
| dfs->dfs_max_sidx = (re).re_sidx; |
| } |
| |
| dfs_check_if_bin5(dfs, &re, this_ts, diff_ts, &found); |
| if (found) { |
| *retval |= found; |
| dfs->dfs_freq_offset = DFS_SIDX_TO_FREQ_OFFSET( |
| (dfs->dfs_min_sidx + dfs->dfs_max_sidx) / 2); |
| return 1; |
| } |
| |
| dfs_check_if_nonbin5(dfs, &re, rs, this_ts, diff_ts, &found, |
| retval, false_radar_found); |
| |
| dfs_is_radarq_empty(dfs, &empty); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * dfs_false_radarfound_reset_vars () - Reset dfs variables after false radar |
| * found. |
| * @dfs: Pointer to wlan_dfs structure. |
| */ |
| void dfs_false_radarfound_reset_vars( |
| struct wlan_dfs *dfs) |
| { |
| dfs->dfs_seq_num = 0; |
| dfs_reset_radarq(dfs); |
| dfs_reset_alldelaylines(dfs); |
| dfs->dfs_phyerr_freq_min = 0x7fffffff; |
| dfs->dfs_phyerr_freq_max = 0; |
| dfs->dfs_phyerr_w53_counter = 0; |
| dfs->dfs_event_log_count = 0; |
| dfs->dfs_phyerr_count = 0; |
| dfs->dfs_phyerr_reject_count = 0; |
| dfs->dfs_phyerr_queued_count = 0; |
| } |
| |
| void dfs_radarfound_action_generic(struct wlan_dfs *dfs, uint8_t seg_id) |
| { |
| struct radar_found_info *radar_found; |
| |
| radar_found = qdf_mem_malloc(sizeof(*radar_found)); |
| if (!radar_found) |
| return; |
| |
| qdf_mem_zero(radar_found, sizeof(*radar_found)); |
| radar_found->segment_id = seg_id; |
| dfs->dfs_seg_id = seg_id; |
| radar_found->pdev_id = |
| wlan_objmgr_pdev_get_pdev_id(dfs->dfs_pdev_obj); |
| |
| dfs_process_radar_ind(dfs, radar_found); |
| qdf_mem_free(radar_found); |
| } |
| |
| void dfs_radar_found_action(struct wlan_dfs *dfs, |
| bool bangradar, |
| uint8_t seg_id) |
| { |
| /* If Host DFS confirmation is supported, save the curchan as |
| * radar found chan, send radar found indication along with |
| * average radar parameters to FW and start the host status |
| * wait timer. |
| */ |
| if (!bangradar && |
| (utils_get_dfsdomain(dfs->dfs_pdev_obj) == DFS_FCC_DOMAIN) && |
| lmac_is_host_dfs_check_support_enabled(dfs->dfs_pdev_obj) && |
| (dfs->dfs_spoof_test_done ? dfs->dfs_use_nol : 1)) { |
| dfs_radarfound_action_fcc(dfs, seg_id); |
| } else { |
| dfs_radarfound_action_generic(dfs, seg_id); |
| } |
| } |
| |
| void dfs_process_radarevent( |
| struct wlan_dfs *dfs, |
| struct dfs_channel *chan) |
| { |
| struct dfs_state *rs = NULL; |
| uint8_t seg_id = 0; |
| int retval = 0; |
| int false_radar_found = 0; |
| bool bangradar = false; |
| |
| if (!dfs_radarevent_basic_sanity(dfs, chan)) |
| return; |
| /* |
| * TEST : Simulate radar bang, make sure we add the channel to NOL |
| * (bug 29968) |
| */ |
| if (dfs_handle_bangradar(dfs, chan, &rs, &seg_id, &retval)) { |
| if (retval) |
| bangradar = true; |
| goto dfsfound; |
| } |
| |
| if (!dfs_handle_missing_pulses(dfs, chan)) |
| return; |
| |
| dfs_process_each_radarevent(dfs, chan, &rs, &seg_id, &retval, |
| &false_radar_found); |
| |
| dfsfound: |
| if (retval) { |
| dfs_radarfound_reset_vars(dfs, rs, chan, seg_id); |
| dfs_radar_found_action(dfs, bangradar, seg_id); |
| } |
| |
| if (false_radar_found) |
| dfs_false_radarfound_reset_vars(dfs); |
| } |