| /* |
| * Copyright (c) 2002-2014, 2016-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_radarevent.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> |
| */ |
| #ifdef ATH_SUPPORT_DFS |
| |
| #define FREQ_5500_MHZ 5500 |
| #define FREQ_5500_MHZ 5500 |
| |
| #define DFS_MAX_FREQ_SPREAD 1375 * 1 |
| #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; |
| |
| /* |
| * 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.) |
| */ |
| static inline u_int8_t |
| dfs_process_pulse_dur(struct ath_dfs *dfs, u_int8_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.ath_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 (u_int8_t)dfs_round((int32_t)((dfs->dur_multiplier)*re_dur)); |
| } |
| |
| |
| /** |
| * dfs_confirm_radar() - more rigid check for radar detection |
| * check for jitter in frequency (sidx) to be within certain limit |
| * introduce a fractional PRI check |
| * add a check to look for chirp in ETSI type 4 radar |
| * @dfs: pointer to dfs structure |
| * |
| * Return: max dur difference in sidx1_sidx2 pulse line |
| */ |
| static int dfs_confirm_radar(struct ath_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; |
| u_int64_t target_ts = 0; |
| struct dfs_pulseline *pl; |
| int start_index = 0, current_index, next_index; |
| unsigned char scores[FRAC_PRI_SCORE_ARRAY_SIZE]; |
| u_int32_t pri_margin; |
| u_int64_t this_diff_ts; |
| u_int32_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 & ATH_DEBUG_DFS2) { |
| dfs_print_delayline(dfs, &rf->rf_dl); |
| |
| /* print pulse line */ |
| DFS_DPRINTK(dfs, ATH_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_DPRINTK(dfs, ATH_DEBUG_DFS2, |
| "Elem %u: ts=%llu dur=%u, seq_num=%d, delta_peak=%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); |
| } |
| } |
| |
| /** |
| * 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; |
| /* save for future use */ |
| start_index = index; |
| } |
| } |
| |
| DFS_DPRINTK(dfs, ATH_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) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s, Rejecting Radar since Fractional PRI detected: searchpri=%d, threshold=%d, fractional PRI=%d, Fractional PRI score=%d\n", |
| __func__, dl->dl_search_pri, scores[0], |
| dl->dl_search_pri/(max_score_index + 1), max_score); |
| DFS_PRINTK("%s: Rejecting Radar since Fractional PRI detected: searchpri=%d, threshold=%d, fractional PRI=%d, Fractional PRI score=%d\n", |
| __func__, 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) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Rejecting Radar since frequency spread is too large : min_sidx=%d, max_sidx=%d, rf_sidx_spread=%d\n", |
| __func__, dl->dl_min_sidx, dl->dl_max_sidx, |
| rf->rf_sidx_spread); |
| DFS_PRINTK("%s: Rejecting Radar since frequency spread is too large : min_sidx=%d, max_sidx=%d, rf_sidx_spread=%d\n", |
| __func__, 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) < rf->rf_threshold)) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s: Rejecting Radar since delta peak values are invalid : dl_delta_peak_match_count=%d, rf_threshold=%d\n", |
| __func__, dl->dl_delta_peak_match_count, |
| rf->rf_threshold); |
| |
| DFS_PRINTK("%s: Rejecting Radar since delta peak values are invalid : dl_delta_peak_match_count=%d, rf_threshold=%d\n", |
| __func__, dl->dl_delta_peak_match_count, |
| rf->rf_threshold); |
| return 0; |
| } |
| return 1; |
| } |
| |
| /* |
| * dfs_process_dc_pulse: process dc pulses |
| * @dfs: pointer to dfs structure |
| * @event: dfs event |
| * @retval: current found status |
| * @this_ts: event's ts |
| * |
| * Return: None |
| */ |
| static void dfs_process_dc_pulse(struct ath_dfs *dfs, struct dfs_event *event, |
| int *retval, uint64_t this_ts, int *false_radar_found) |
| { |
| struct dfs_event re; |
| struct dfs_state *rs=NULL; |
| struct dfs_filtertype *ft; |
| struct dfs_filter *rf = NULL; |
| int found, p, empty; |
| int min_pri, miss_pulse_number = 0, deviation = 0; |
| u_int32_t tabledepth = 0; |
| u_int64_t deltaT; |
| int ext_chan_event_flag = 0; |
| int i; |
| |
| OS_MEMCPY(&re, event, sizeof(*event)); |
| if (re.re_chanindex < DFS_NUM_RADAR_STATES) |
| rs = &dfs->dfs_radar[re.re_chanindex]; |
| |
| while ((tabledepth < DFS_MAX_RADAR_OVERLAP) && |
| ((dfs->dfs_dc_radartable[re.re_dur])[tabledepth] != -1) && |
| (!*retval) && (!*false_radar_found)) { |
| ft = dfs->dfs_dc_radarf[((dfs->dfs_dc_radartable |
| [re.re_dur])[tabledepth])]; |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| FL("** RD (%d): ts %x dur %u rssi %u"), |
| rs->rs_chan.ic_freq, |
| re.re_ts, re.re_dur, re.re_rssi); |
| |
| deltaT = this_ts - ft->ft_last_ts; |
| if (re.re_rssi < ft->ft_rssithresh && re.re_dur > 4) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| FL("Rejecting on rssi rssi=%u thresh=%u depth=%d"), |
| re.re_rssi, ft->ft_rssithresh, |
| tabledepth); |
| tabledepth++; |
| dfs_reset_filter_delaylines(ft); |
| ATH_DFSQ_LOCK(dfs); |
| empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| ATH_DFSQ_UNLOCK(dfs); |
| continue; |
| } |
| 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. */ |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| FL("Rejecting on pri pri=%lld minpri=%u depth=%d"), |
| (unsigned long long)deltaT, |
| ft->ft_minpri, tabledepth); |
| dfs_reset_filter_delaylines(ft); |
| tabledepth++; |
| continue; |
| } |
| 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)) { |
| 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 ((deltaT < rf->rf_minpri) && (deltaT != 0)) { |
| /* Second line of PRI filtering. */ |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| FL("filterID=%d :: Rejected and cleared individual filter min PRI deltaT=%lld rf->rf_minpri=%u"), |
| rf->rf_pulseid, |
| (unsigned long long)deltaT, rf->rf_minpri); |
| dfs_reset_delayline(&rf->rf_dl); |
| rf->rf_dl.dl_last_ts = this_ts; |
| continue; |
| } |
| |
| if (rf->rf_ignore_pri_window > 0) { |
| if (deltaT < rf->rf_minpri) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| FL("filterID=%d :: Rejected and cleared on individual filter max PRI deltaT=%lld rf->rf_minpri=%u"), |
| rf->rf_pulseid, |
| (unsigned long long)deltaT, rf->rf_minpri); |
| dfs_reset_delayline(&rf->rf_dl); |
| rf->rf_dl.dl_last_ts = this_ts; |
| continue; |
| } |
| } else { |
| if (deltaT < rf->rf_minpri) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| FL("filterID=%d :: Rejected and cleared on individual filter max PRI deltaT=%lld rf->rf_minpri=%u"), |
| rf->rf_pulseid, |
| (unsigned long long)deltaT, rf->rf_minpri); |
| dfs_reset_delayline(&rf->rf_dl); |
| rf->rf_dl.dl_last_ts = this_ts; |
| continue; |
| } |
| } |
| dfs_add_pulse(dfs, rf, &re, deltaT, this_ts); |
| |
| /* extra WAR */ |
| if ((dfs->dfsdomain == DFS_FCC_DOMAIN && |
| dfs->dfsdomain == DFS_MKK4_DOMAIN) && |
| ((rf->rf_pulseid != 31) && (rf->rf_pulseid != 32))) { |
| |
| min_pri = 0xffff; |
| for (i = 0; i < rf->rf_dl.dl_numelems; i++) { |
| if (rf->rf_dl.dl_elems[i].de_time < min_pri) |
| min_pri = rf->rf_dl.dl_elems[i].de_time; |
| } |
| |
| for (i=0; i < rf->rf_dl.dl_numelems; i++) { |
| miss_pulse_number = vos_round_div( |
| (rf->rf_dl.dl_elems[i].de_time), min_pri); |
| deviation = __adf_os_abs(min_pri * |
| miss_pulse_number - |
| rf->rf_dl.dl_elems[i].de_time); |
| if (deviation > miss_pulse_number*3) { |
| dfs_reset_delayline(&rf->rf_dl); |
| VOS_TRACE(VOS_MODULE_ID_SAP, |
| VOS_TRACE_LEVEL_INFO, |
| FL("filterID=%d :: cleared individual deleyline min_pri =%d miss_pulse_number =%d deviation =%d"), |
| rf->rf_pulseid, |
| min_pri, miss_pulse_number, deviation); |
| } |
| } |
| } |
| |
| /* 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 { |
| if (dfs_bin_check(dfs, rf, (uint32_t) deltaT, |
| re.re_dur, ext_chan_event_flag)) { |
| found = 1; |
| /** |
| * do additioal check to conirm radar except for the |
| * following staggered, chirp FCC Bin 5, frequency |
| * hopping indicated by rf_patterntype == 1 |
| */ |
| if (rf->rf_patterntype != 1) { |
| found = dfs_confirm_radar(dfs, rf, |
| ext_chan_event_flag); |
| *false_radar_found = (found == 1)? 0 : 1; |
| } |
| } |
| } |
| |
| if (dfs->dfs_debug_mask & ATH_DEBUG_DFS2) |
| dfs_print_delayline(dfs, &rf->rf_dl); |
| |
| rf->rf_dl.dl_last_ts = this_ts; |
| } else { |
| /* if we are rejecting this, clear the queue */ |
| dfs_reset_delayline(&rf->rf_dl); |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| FL("filterID= %d :: cleared individual deleyline"), |
| rf->rf_pulseid); |
| } |
| } |
| ft->ft_last_ts = this_ts; |
| *retval |= found; |
| if (found) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| FL(":## Radar Found minDur=%d, filterId=%d ##"), |
| ft->ft_mindur, |
| rf != NULL ? rf->rf_pulseid : -1); |
| } |
| tabledepth++; |
| } |
| } |
| |
| /** |
| * dfs_cal_sidx1_sidx2_dur_diff() - cal dur difference in sidx1_sidx2 |
| * pluse line |
| * @dfs: pointer to dfs structure |
| * |
| * Return: max dur difference in sidx1_sidx2 pulse line |
| */ |
| static int dfs_cal_sidx1_sidx2_dur_diff(struct ath_dfs *dfs) |
| { |
| u_int32_t index, loop; |
| u_int32_t lowdur, highdur; |
| struct dfs_sidx1_sidx2_pulse_line *sidx1_sidx2_p; |
| struct dfs_pulseline *pl; |
| |
| if (dfs == NULL) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, |
| "%s[%d]: dfs is NULL", __func__, __LINE__); |
| return 0; |
| } |
| pl = dfs->pulses; |
| sidx1_sidx2_p = &dfs->sidx1_sidx2_elems; |
| lowdur = highdur = |
| sidx1_sidx2_p->pl_elems[sidx1_sidx2_p->pl_lastelem].p_dur; |
| for (loop = 0; loop < sidx1_sidx2_p->pl_numelems; loop++) { |
| index = sidx1_sidx2_p->pl_firstelem + loop; |
| index &= DFS_SIDX1_SIDX2_MASK; |
| if (sidx1_sidx2_p->pl_elems[index].p_time > |
| pl->pl_elems[pl->pl_lastelem].p_time - |
| DFS_SIDX1_SIDX2_TIME_WINDOW) { |
| if (sidx1_sidx2_p->pl_elems[index].p_dur < lowdur) |
| lowdur = sidx1_sidx2_p->pl_elems[index].p_dur; |
| if (sidx1_sidx2_p->pl_elems[index].p_dur > highdur) |
| highdur = sidx1_sidx2_p->pl_elems[index].p_dur; |
| } |
| } |
| return highdur - lowdur; |
| } |
| |
| /* |
| * Process a radar event. |
| * |
| * If a radar event is found, return 1. Otherwise, return 0. |
| * |
| * There is currently no way to specify that a radar event has occured on |
| * a specific channel, so the current methodology is to mark both the pri |
| * and ext channels as being unavailable. This should be fixed for 802.11ac |
| * or we'll quickly run out of valid channels to use. |
| */ |
| int |
| dfs_process_radarevent(struct ath_dfs *dfs, struct ieee80211_channel *chan) |
| { |
| struct dfs_event re,*event; |
| struct dfs_state *rs=NULL; |
| struct dfs_filtertype *ft; |
| struct dfs_filter *rf; |
| int found, retval = 0, p, empty; |
| int events_processed = 0; |
| u_int32_t tabledepth, index, sidx1_sidx2_index; |
| u_int64_t deltafull_ts = 0, this_ts, deltaT; |
| struct ieee80211_channel *thischan; |
| struct dfs_pulseline *pl; |
| struct dfs_sidx1_sidx2_pulse_line *sidx1_sidx2_p; |
| static u_int32_t test_ts = 0; |
| static u_int32_t diff_ts = 0; |
| int ext_chan_event_flag = 0; |
| #if 0 |
| int pri_multiplier = 2; |
| #endif |
| int i; |
| int false_radar_found = 0; |
| |
| if (dfs == NULL) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, |
| "%s[%d]: dfs is NULL", __func__, __LINE__); |
| return 0; |
| } |
| pl = dfs->pulses; |
| sidx1_sidx2_p = &dfs->sidx1_sidx2_elems; |
| adf_os_spin_lock_bh(&dfs->ic->chan_lock); |
| if ( !(IEEE80211_IS_CHAN_DFS(dfs->ic->ic_curchan))) { |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS2, "%s: radar event on non-DFS chan", |
| __func__); |
| dfs_reset_radarq(dfs); |
| dfs_reset_alldelaylines(dfs); |
| return 0; |
| } |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| #ifndef ATH_DFS_RADAR_DETECTION_ONLY |
| /* TEST : Simulate radar bang, make sure we add the channel to NOL (bug 29968) */ |
| if (dfs->dfs_bangradar) { |
| /* bangradar will always simulate radar found on the primary channel */ |
| rs = &dfs->dfs_radar[dfs->dfs_curchan_radindex]; |
| dfs->dfs_bangradar = 0; /* reset */ |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "%s: bangradar", __func__); |
| retval = 1; |
| goto dfsfound; |
| } |
| #endif |
| |
| /* |
| 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 ((dfs->dfsdomain == DFS_MKK4_DOMAIN) && |
| (dfs->dfs_caps.ath_chip_is_bb_tlv) && |
| (chan->ic_freq < FREQ_5500_MHZ)) { |
| |
| dfs->dfs_pri_multiplier = dfs->dfs_pri_multiplier_ini; |
| |
| /* do not process W53 pulses, |
| unless we have a minimum number of them |
| */ |
| if (dfs->dfs_phyerr_w53_counter >= 5) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, |
| "%s: w53_counter=%d, freq_max=%d, " |
| "freq_min=%d, pri_multiplier=%d", |
| __func__, |
| 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; |
| } else { |
| return 0; |
| } |
| } |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, |
| "%s: pri_multiplier=%d", |
| __func__, |
| dfs->dfs_pri_multiplier); |
| |
| ATH_DFSQ_LOCK(dfs); |
| empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| ATH_DFSQ_UNLOCK(dfs); |
| |
| while ((!empty) && (!retval) && (events_processed < MAX_EVENTS) && |
| (!false_radar_found)) { |
| ATH_DFSQ_LOCK(dfs); |
| event = STAILQ_FIRST(&(dfs->dfs_radarq)); |
| if (event != NULL) |
| STAILQ_REMOVE_HEAD(&(dfs->dfs_radarq), re_list); |
| ATH_DFSQ_UNLOCK(dfs); |
| |
| if (event == NULL) { |
| empty = 1; |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, "%s[%d]: event is NULL ",__func__,__LINE__); |
| break; |
| } |
| events_processed++; |
| re = *event; |
| |
| OS_MEMZERO(event, sizeof(struct dfs_event)); |
| ATH_DFSEVENTQ_LOCK(dfs); |
| STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), event, re_list); |
| ATH_DFSEVENTQ_UNLOCK(dfs); |
| |
| found = 0; |
| |
| adf_os_spin_lock_bh(&dfs->ic->chan_lock); |
| if (dfs->ic->disable_phy_err_processing) { |
| ATH_DFSQ_LOCK(dfs); |
| empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| ATH_DFSQ_UNLOCK(dfs); |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| continue; |
| } |
| |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| |
| if (re.re_chanindex < DFS_NUM_RADAR_STATES) |
| rs = &dfs->dfs_radar[re.re_chanindex]; |
| else { |
| ATH_DFSQ_LOCK(dfs); |
| empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| ATH_DFSQ_UNLOCK(dfs); |
| continue; |
| } |
| if (rs->rs_chan.ic_flagext & CHANNEL_INTERFERENCE) { |
| ATH_DFSQ_LOCK(dfs); |
| empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| ATH_DFSQ_UNLOCK(dfs); |
| continue; |
| } |
| |
| 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_DPRINTK(dfs, ATH_DEBUG_DFS1, " %s deltaT is 0 ", __func__); |
| } 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; |
| } |
| |
| |
| if (re.re_ts <= dfs->dfs_rinfo.rn_last_ts) { |
| dfs->dfs_rinfo.rn_ts_prefix += |
| (((u_int64_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; |
| if (deltafull_ts > ((u_int64_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; |
| 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 > (u_int64_t) DFS_TSMASK) { |
| deltafull_ts = deltafull_ts >> DFS_TSSHIFT; |
| dfs->dfs_rinfo.rn_ts_prefix += |
| ((deltafull_ts - 1) << DFS_TSSHIFT); |
| } |
| } |
| } |
| /* |
| * 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 | ((u_int64_t) re.re_ts); |
| dfs->dfs_rinfo.rn_lastfull_ts = re.re_full_ts; |
| dfs->dfs_rinfo.rn_last_ts = re.re_ts; |
| |
| /* |
| * 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.. |
| */ |
| re.re_dur = dfs_process_pulse_dur(dfs, re.re_dur); |
| |
| /* |
| * 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.ath_chip_is_bb_tlv) && re.re_dur != 1) { |
| this_ts -= re.re_dur; |
| } |
| |
| /* 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.sidx; |
| pl->pl_elems[index].p_delta_peak = re.re_delta_peak; |
| if (dfs->dfs_enable_radar_war && |
| (re.sidx == 1 || re.sidx == 2)) { |
| sidx1_sidx2_index = (sidx1_sidx2_p->pl_lastelem + 1) & |
| DFS_SIDX1_SIDX2_MASK; |
| if (sidx1_sidx2_p->pl_numelems == DFS_SIDX1_SIDX2_SIZE) |
| sidx1_sidx2_p->pl_firstelem = |
| (sidx1_sidx2_p->pl_firstelem + 1) & |
| DFS_SIDX1_SIDX2_MASK; |
| else |
| sidx1_sidx2_p->pl_numelems++; |
| sidx1_sidx2_p->pl_lastelem = sidx1_sidx2_index; |
| sidx1_sidx2_p->pl_elems[sidx1_sidx2_index].p_time = |
| this_ts; |
| sidx1_sidx2_p->pl_elems[sidx1_sidx2_index].p_dur = |
| re.re_dur; |
| } |
| diff_ts = (u_int32_t)this_ts - test_ts; |
| test_ts = (u_int32_t)this_ts; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,"ts%u %u %u diff %u pl->pl_lastelem.p_time=%llu",(u_int32_t)this_ts, re.re_dur, re.re_rssi, diff_ts, (unsigned long long)pl->pl_elems[index].p_time); |
| 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->dfs_event_log_count++; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s[%d]:xxxxx ts =%u re.re_dur=%u re.re_rssi =%u diff =%u sidx = %u flags = %x pl->pl_lastelem.p_time=%llu xxxxx", |
| __func__, __LINE__, (u_int32_t)this_ts, |
| re.re_dur, re.re_rssi, diff_ts, re.sidx, re.re_flags, |
| (unsigned long long)pl->pl_elems[index].p_time); |
| dfs->dfs_seq_num++; |
| pl->pl_elems[index].p_seq_num = dfs->dfs_seq_num; |
| |
| /* 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.sidx; |
| pl->pl_elems[index].p_delta_peak = re.re_delta_peak; |
| dfs->dfs_seq_num++; |
| pl->pl_elems[index].p_seq_num = dfs->dfs_seq_num; |
| } |
| |
| found = 0; |
| |
| /* |
| * Use this fix only when device is not in test mode, as |
| * it drops some valid phyerrors. |
| * In FCC or JAPAN domain,if the follwing signature matches |
| * its likely that this is a false radar pulse pattern |
| * so process the next pulse in the queue. |
| */ |
| if ((dfs->disable_dfs_ch_switch == VOS_FALSE) && |
| (DFS_FCC_DOMAIN == dfs->dfsdomain || |
| DFS_MKK4_DOMAIN == dfs->dfsdomain) && |
| (re.re_dur >= 11 && re.re_dur <= 20) && |
| (diff_ts > 500 || diff_ts <= 305) && |
| (re.sidx == -4)) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "\n%s: Rejecting on Peak Index = %d,re.re_dur = %d,diff_ts = %d\n", |
| __func__,re.sidx, re.re_dur, diff_ts); |
| |
| ATH_DFSQ_LOCK(dfs); |
| empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| ATH_DFSQ_UNLOCK(dfs); |
| continue; |
| } |
| |
| /* |
| * Modifying the pulse duration for FCC Type 4 |
| * or JAPAN W56 Type 6 radar pulses when the |
| * following condition is reported in radar |
| * summary report. |
| */ |
| if ((DFS_FCC_DOMAIN == dfs->dfsdomain || |
| DFS_MKK4_DOMAIN == dfs->dfsdomain) && |
| ((chan->ic_flags & IEEE80211_CHAN_VHT80) == |
| IEEE80211_CHAN_VHT80) && |
| (chan->ic_pri_freq_center_freq_mhz_separation == |
| DFS_WAR_PLUS_30_MHZ_SEPARATION || |
| chan->ic_pri_freq_center_freq_mhz_separation == |
| DFS_WAR_MINUS_30_MHZ_SEPARATION) && |
| (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)) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "\n%s:chan->ic_flags=0x%x, Pri Chan MHz Separation=%d\n", |
| __func__, chan->ic_flags, |
| chan->ic_pri_freq_center_freq_mhz_separation); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "\n%s: Reported Peak Index = %d,re.re_dur = %d,diff_ts = %d\n", |
| __func__, re.sidx, re.re_dur, diff_ts); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "\n%s: Modifying the pulse duration to fit the valid range \n", |
| __func__); |
| |
| re.re_dur = DFS_TYPE4_WAR_VALID_PULSE_DURATION; |
| } |
| |
| /* |
| * Modifying the pulse duration for ETSI Type 2 |
| * and ETSI type 3 radar pulses when the following |
| * condition is reported in radar summary report. |
| */ |
| if ((DFS_ETSI_DOMAIN == dfs->dfsdomain) && |
| ((chan->ic_flags & IEEE80211_CHAN_VHT80) == |
| IEEE80211_CHAN_VHT80) && |
| (chan->ic_pri_freq_center_freq_mhz_separation == |
| DFS_WAR_PLUS_30_MHZ_SEPARATION || |
| chan->ic_pri_freq_center_freq_mhz_separation == |
| DFS_WAR_MINUS_30_MHZ_SEPARATION) && |
| (re.sidx == DFS_WAR_PEAK_INDEX_ZERO) && |
| (re.re_dur > DFS_ETSI_TYPE2_TYPE3_WAR_PULSE_DUR_LOWER_LIMIT && |
| re.re_dur < DFS_ETSI_TYPE2_TYPE3_WAR_PULSE_DUR_UPPER_LIMIT) && |
| ((diff_ts > DFS_ETSI_TYPE2_WAR_PRI_LOWER_LIMIT && |
| diff_ts < DFS_ETSI_TYPE2_WAR_PRI_UPPER_LIMIT) || |
| (diff_ts > DFS_ETSI_TYPE3_WAR_PRI_LOWER_LIMIT && |
| diff_ts < DFS_ETSI_TYPE3_WAR_PRI_UPPER_LIMIT ))) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "\n%s:chan->ic_flags=0x%x, Pri Chan MHz Separation=%d\n", |
| __func__, chan->ic_flags, |
| chan->ic_pri_freq_center_freq_mhz_separation); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "\n%s: Reported Peak Index = %d,re.re_dur = %d,diff_ts = %d\n", |
| __func__, re.sidx, re.re_dur, diff_ts); |
| |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "\n%s: Modifying the ETSI pulse dur to fit the valid range \n", |
| __func__); |
| |
| re.re_dur = DFS_ETSI_WAR_VALID_PULSE_DURATION; |
| } |
| |
| /* 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]); |
| 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_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_PULSE, |
| "%s not a BIN5 pulse (dur=%d)", |
| __func__, re.re_dur); |
| } |
| } |
| } |
| |
| if (found) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "%s: Found bin5 radar", __func__); |
| retval |= found; |
| goto dfsfound; |
| } |
| |
| tabledepth = 0; |
| rf = NULL; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS1," *** chan freq (%d): ts %llu dur %u rssi %u", |
| rs->rs_chan.ic_freq, (unsigned long long)this_ts, re.re_dur, re.re_rssi); |
| |
| |
| /* |
| * DC pulses processing will be done in this seperate block since some |
| * device may generate pulse which may cause false detection. |
| * This processing is similar kind as of normal pulse processing with |
| * some exception such as: |
| * 1. Clear the queue if pulse doesn't belong to it |
| * 2. Remove chan load optimization(can cause some valid pulses to drop) |
| * 3. Drop pulses on basis of mean deviation for some filters |
| */ |
| if (((re.sidx == 0) && DFS_EVENT_NOTCHIRP(&re)) && |
| ((dfs->dfsdomain == DFS_FCC_DOMAIN) || |
| (dfs->dfsdomain == DFS_MKK4_DOMAIN) || |
| (dfs->dfsdomain == DFS_ETSI_DOMAIN && re.re_dur < 18))) { |
| dfs_process_dc_pulse(dfs, &re, &retval, this_ts, &false_radar_found); |
| } |
| |
| /* Pulse not at DC position */ |
| else { |
| while ((tabledepth < DFS_MAX_RADAR_OVERLAP) && |
| ((dfs->dfs_radartable[re.re_dur])[tabledepth] != -1) && |
| (!retval) && (!false_radar_found)) { |
| ft = dfs->dfs_radarf[((dfs->dfs_radartable[re.re_dur])[tabledepth])]; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS2," ** RD (%d): ts %x dur %u rssi %u", |
| rs->rs_chan.ic_freq, |
| re.re_ts, re.re_dur, re.re_rssi); |
| |
| if (re.re_rssi < ft->ft_rssithresh && re.re_dur > 4) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,"%s : Rejecting on rssi rssi=%u thresh=%u", __func__, re.re_rssi, ft->ft_rssithresh); |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, "%s[%d]: Rejecting on rssi rssi=%u thresh=%u",__func__,__LINE__,re.re_rssi, ft->ft_rssithresh); |
| tabledepth++; |
| ATH_DFSQ_LOCK(dfs); |
| empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| ATH_DFSQ_UNLOCK(dfs); |
| continue; |
| } |
| deltaT = this_ts - ft->ft_last_ts; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,"deltaT = %lld (ts: 0x%llx) (last ts: 0x%llx)",(unsigned long long)deltaT, (unsigned long long)this_ts, (unsigned long long)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_DPRINTK(dfs, ATH_DEBUG_DFS2, "%s: Rejecting on pri pri=%lld minpri=%u", __func__, (unsigned long long)deltaT, ft->ft_minpri); |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, "%s[%d]:Rejecting on pri pri=%lld minpri=%u",__func__,__LINE__,(unsigned long long)deltaT,ft->ft_minpri); |
| tabledepth++; |
| continue; |
| } |
| 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 ((deltaT < rf->rf_minpri) && (deltaT != 0)) { |
| /* Second line of PRI filtering. */ |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS2, |
| "filterID %d : Rejecting on individual filter min PRI deltaT=%lld rf->rf_minpri=%u", |
| rf->rf_pulseid, (unsigned long long)deltaT, rf->rf_minpri); |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, "%s[%d]:filterID= %d::Rejecting on individual filter min PRI deltaT=%lld rf->rf_minpri=%u",__func__,__LINE__,rf->rf_pulseid, (unsigned long long)deltaT, rf->rf_minpri); |
| continue; |
| } |
| |
| if (rf->rf_ignore_pri_window > 0) { |
| if (deltaT < rf->rf_minpri) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS2, |
| "filterID %d : Rejecting on individual filter max PRI deltaT=%lld rf->rf_minpri=%u", |
| rf->rf_pulseid, (unsigned long long)deltaT, rf->rf_minpri); |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, "%s[%d]:filterID= %d :: Rejecting on individual filter max PRI deltaT=%lld rf->rf_minpri=%u",__func__,__LINE__,rf->rf_pulseid, (unsigned long long)deltaT, rf->rf_minpri); |
| /* But update the last time stamp */ |
| rf->rf_dl.dl_last_ts = this_ts; |
| continue; |
| } |
| } 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 > ((u_int64_t)dfs->dfs_pri_multiplier * rf->rf_maxpri) ) || (deltaT < rf->rf_minpri) ) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS2, |
| "filterID %d : Rejecting on individual filter max PRI deltaT=%lld rf->rf_minpri=%u", |
| rf->rf_pulseid, (unsigned long long)deltaT, rf->rf_minpri); |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, "%s[%d]:filterID= %d :: Rejecting on individual filter max PRI deltaT=%lld rf->rf_minpri=%u",__func__,__LINE__,rf->rf_pulseid, (unsigned long long)deltaT, rf->rf_minpri); |
| /* But update the last time stamp */ |
| rf->rf_dl.dl_last_ts = 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, (u_int32_t) deltaT, re.re_dur); |
| } else { |
| if (dfs_bin_check(dfs, rf, |
| (u_int32_t) deltaT, |
| re.re_dur, |
| ext_chan_event_flag)) { |
| found = 1; |
| /** |
| * do additioal check to conirm |
| * radar except for the |
| * following staggered, chirp |
| * FCC Bin 5, frequency hopping |
| * indicated by |
| * rf_patterntype == 1 |
| */ |
| if (rf->rf_patterntype != 1) { |
| found = dfs_confirm_radar( |
| dfs, rf, |
| ext_chan_event_flag); |
| false_radar_found = |
| (found == 1)? 0 : 1; |
| } |
| } |
| } |
| if (dfs->dfs_debug_mask & ATH_DEBUG_DFS2) { |
| if (rf->rf_patterntype != 1) |
| dfs_print_delayline(dfs, &rf->rf_dl); |
| } |
| rf->rf_dl.dl_last_ts = this_ts; |
| } |
| } |
| ft->ft_last_ts = this_ts; |
| retval |= found; |
| if (found) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS3, |
| "Found on channel minDur = %d, filterId = %d",ft->ft_mindur, |
| rf != NULL ? rf->rf_pulseid : -1); |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s[%d]:### Found on channel minDur = %d, filterId = %d ###", |
| __func__,__LINE__,ft->ft_mindur, |
| rf != NULL ? rf->rf_pulseid : -1); |
| } |
| tabledepth++; |
| } |
| } |
| ATH_DFSQ_LOCK(dfs); |
| empty = STAILQ_EMPTY(&(dfs->dfs_radarq)); |
| ATH_DFSQ_UNLOCK(dfs); |
| } |
| dfsfound: |
| if (retval) { |
| if (dfs->dfs_enable_radar_war && |
| (DFS_SIDX1_SIDX2_DR_LIM < dfs_cal_sidx1_sidx2_dur_diff(dfs))) { |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, |
| "%s [%d] false detection",__func__,__LINE__); |
| return 0; |
| } |
| |
| /* Collect stats */ |
| dfs->ath_dfs_stats.num_radar_detects++; |
| thischan = &rs->rs_chan; |
| VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, "%s[%d]: ### RADAR FOUND ON CHANNEL %d (%d MHz) ###",__func__,__LINE__,thischan->ic_ieee, |
| thischan->ic_freq); |
| DFS_PRINTK("Radar found on channel %d (%d MHz)", |
| thischan->ic_ieee, |
| thischan->ic_freq); |
| |
| #if 0 //UMACDFS : TODO |
| /* Disable radar for now */ |
| rfilt = ath_hal_getrxfilter(ah); |
| rfilt &= ~HAL_RX_FILTER_PHYRADAR; |
| ath_hal_setrxfilter(ah, rfilt); |
| #endif |
| dfs_reset_radarq(dfs); |
| dfs_reset_alldelaylines(dfs); |
| /* XXX Should we really enable again? Maybe not... */ |
| /* No reason to re-enable so far - Ajay*/ |
| #if 0 |
| pe.pe_firpwr = rs->rs_firpwr; |
| pe.pe_rrssi = rs->rs_radarrssi; |
| pe.pe_height = rs->rs_height; |
| pe.pe_prssi = rs->rs_pulserssi; |
| pe.pe_inband = rs->rs_inband; |
| /* 5413 specific */ |
| pe.pe_relpwr = rs->rs_relpwr; |
| pe.pe_relstep = rs->rs_relstep; |
| pe.pe_maxlen = rs->rs_maxlen; |
| |
| ath_hal_enabledfs(ah, &pe); |
| rfilt |= HAL_RX_FILTER_PHYRADAR; |
| ath_hal_setrxfilter(ah, rfilt); |
| #endif |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, |
| "Primary channel freq = %u flags=0x%x", |
| chan->ic_freq, chan->ic_flagext); |
| adf_os_spin_lock_bh(&dfs->ic->chan_lock); |
| if ((dfs->ic->ic_curchan->ic_freq!= thischan->ic_freq)) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, |
| "Ext channel freq = %u flags=0x%x", |
| thischan->ic_freq, thischan->ic_flagext); |
| } |
| |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| dfs->dfs_phyerr_freq_min = 0x7fffffff; |
| dfs->dfs_phyerr_freq_max = 0; |
| dfs->dfs_phyerr_w53_counter = 0; |
| } |
| //VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, "IN FUNC %s[%d]: retval = %d ",__func__,__LINE__,retval); |
| if (false_radar_found) { |
| 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; |
| } |
| return retval; |
| // return 1; |
| } |
| |
| #endif /* ATH_SUPPORT_DFS */ |