blob: c0249a9a11225d039520524e205be8777530539a [file] [log] [blame]
/*
* Copyright (c) 2002-2013, 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_bindetects.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 removing
#include <ieee80211_var.h>
*/
#ifdef ATH_SUPPORT_DFS
int
dfs_bin_fixedpattern_check(struct ath_dfs *dfs, struct dfs_filter *rf, u_int32_t dur, int ext_chan_flag)
{
struct dfs_pulseline *pl = dfs->pulses;
int i, n, refpri, primargin, numpulses=0;
u_int64_t start_ts, end_ts, event_ts, prev_event_ts, next_event_ts, window_start, window_end;
u_int32_t index, next_index, deltadur;
/* For fixed pattern types, rf->rf_patterntype=1*/
primargin = dfs_get_pri_margin(dfs, ext_chan_flag, (rf->rf_patterntype==1));
refpri = (rf->rf_minpri + rf->rf_maxpri)/2;
index = pl->pl_lastelem;
end_ts = pl->pl_elems[index].p_time;
start_ts = end_ts - (refpri*rf->rf_numpulses);
DFS_DPRINTK(dfs, ATH_DEBUG_DFS3,
"lastelem ts=%llu start_ts=%llu, end_ts=%llu\n",
(unsigned long long) pl->pl_elems[index].p_time,
(unsigned long long) start_ts,
(unsigned long long) end_ts);
/* find the index of first element in our window of interest */
for(i=0;i<pl->pl_numelems;i++) {
index = (index-1) & DFS_MAX_PULSE_BUFFER_MASK;
if(pl->pl_elems[index].p_time >= start_ts )
continue;
else {
index = (index) & DFS_MAX_PULSE_BUFFER_MASK;
break;
}
}
for (n=0;n<=rf->rf_numpulses; n++) {
window_start = (start_ts + (refpri*n))-(primargin+n);
window_end = window_start + 2*(primargin+n);
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"window_start %u window_end %u \n",
(u_int32_t)window_start, (u_int32_t)window_end);
for(i=0;i<pl->pl_numelems;i++) {
prev_event_ts = pl->pl_elems[index].p_time;
index = (index+1) & DFS_MAX_PULSE_BUFFER_MASK;
event_ts = pl->pl_elems[index].p_time;
next_index = (index+1) & DFS_MAX_PULSE_BUFFER_MASK;
next_event_ts = pl->pl_elems[next_index].p_time;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"ts %u \n", (u_int32_t)event_ts);
if( (event_ts <= window_end) && (event_ts >= window_start)){
deltadur = DFS_DIFF(pl->pl_elems[index].p_dur, dur);
if( (pl->pl_elems[index].p_dur == 1) ||
((dur != 1) && (deltadur <= 2))) {
numpulses++;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"numpulses %u \n", numpulses);
break;
}
}
else if( event_ts > window_end) {
index = (index-1) & DFS_MAX_PULSE_BUFFER_MASK;
break;
}
else if( event_ts == prev_event_ts) {
if( ((next_event_ts - event_ts) > refpri) ||
((next_event_ts - event_ts) == 0)) {
deltadur = DFS_DIFF(pl->pl_elems[index].p_dur, dur);
if( (pl->pl_elems[index].p_dur == 1) ||
((pl->pl_elems[index].p_dur != 1) && (deltadur <= 2))) {
numpulses++;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"zero PRI: numpulses %u \n", numpulses);
break;
}
}
}
}
}
if (numpulses >= dfs_get_filter_threshold(dfs, rf, ext_chan_flag)) {
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, "%s FOUND filterID=%u numpulses=%d unadj thresh=%d\n", __func__, rf->rf_pulseid, numpulses, rf->rf_threshold);
return 1;
}
else
return 0;
}
void
dfs_add_pulse(struct ath_dfs *dfs, struct dfs_filter *rf, struct dfs_event *re,
u_int32_t deltaT, u_int64_t this_ts)
{
u_int32_t index,n, window;
struct dfs_delayline *dl;
dl = &rf->rf_dl;
/* Circular buffer of size 2^n */
index = (dl->dl_lastelem + 1) & DFS_MAX_DL_MASK;
//if ((dl->dl_numelems+1) == DFS_MAX_DL_SIZE)
if ((dl->dl_numelems) == DFS_MAX_DL_SIZE)
dl->dl_firstelem = (dl->dl_firstelem + 1) & DFS_MAX_DL_MASK;
else
dl->dl_numelems++;
dl->dl_lastelem = index;
dl->dl_elems[index].de_time = deltaT;
dl->dl_elems[index].de_ts = this_ts;
window = deltaT;
dl->dl_elems[index].de_dur = re->re_dur;
dl->dl_elems[index].de_rssi = re->re_rssi;
dl->dl_elems[index].de_seg_id = re->re_seg_id;
dl->dl_elems[index].de_sidx = re->sidx;
dl->dl_elems[index].de_delta_peak = re->re_delta_peak;
dl->dl_elems[index].de_seq_num = dfs->dfs_seq_num;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"%s: adding: filter id %d, dur=%d, rssi=%d, ts=%llu\n",
__func__,
rf->rf_pulseid,
re->re_dur,
re->re_rssi,
(unsigned long long int) this_ts);
for (n=0;n<dl->dl_numelems-1; n++) {
index = (index-1) & DFS_MAX_DL_MASK;
/*
* calculate window based on full time stamp instead of deltaT
* deltaT (de_time) may result in incorrect window value
*/
window = (u_int32_t) (this_ts - dl->dl_elems[index].de_ts);
if (window > rf->rf_filterlen) {
dl->dl_firstelem = (index+1) & DFS_MAX_DL_MASK;
dl->dl_numelems = n+1;
}
}
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"dl firstElem = %d lastElem = %d\n",dl->dl_firstelem,
dl->dl_lastelem);
}
int
dfs_bin_check(struct ath_dfs *dfs, struct dfs_filter *rf, u_int32_t deltaT,
u_int32_t width, int ext_chan_flag)
{
u_int32_t refpri, refdur, searchpri, deltapri,deltapri_2,deltapri_3, averagerefpri;
u_int32_t n, i, primargin, durmargin, highscore, highscoreindex;
int score[DFS_MAX_DL_SIZE], delayindex, dindex, found=0;
struct dfs_delayline *dl;
u_int32_t scoreindex, lowpriindex= 0, lowpri = 0xffff;
int numpulses=0;
int lowprichk=3, pri_match=0;
dl = &rf->rf_dl;
if( dl->dl_numelems < (rf->rf_threshold-1)) {
return 0;
}
if( deltaT > rf->rf_filterlen)
return 0;
primargin = dfs_get_pri_margin(dfs, ext_chan_flag, (rf->rf_patterntype==1));
if(rf->rf_maxdur < 10) {
durmargin = 4;
}
else {
durmargin = 6;
}
if( rf->rf_patterntype == 1 ){
found = dfs_bin_fixedpattern_check(dfs, rf, width, ext_chan_flag);
if(found) {
dl->dl_numelems = 0;
}
return found;
}
OS_MEMZERO(score, sizeof(int)*DFS_MAX_DL_SIZE);
/* find out the lowest pri */
for (n=0;n<dl->dl_numelems; n++) {
delayindex = (dl->dl_firstelem + n) & DFS_MAX_DL_MASK;
refpri = dl->dl_elems[delayindex].de_time;
if( refpri == 0)
continue;
else if(refpri < lowpri) {
lowpri = dl->dl_elems[delayindex].de_time;
lowpriindex = n;
}
}
/* find out the each delay element's pri score */
for (n=0;n<dl->dl_numelems; n++) {
delayindex = (dl->dl_firstelem + n) & DFS_MAX_DL_MASK;
refpri = dl->dl_elems[delayindex].de_time;
if( refpri == 0)
continue;
if (refpri < rf->rf_maxpri) { // use only valid PRI range for high score
for (i=0;i<dl->dl_numelems; i++) {
dindex = (dl->dl_firstelem + i) & DFS_MAX_DL_MASK;
searchpri = dl->dl_elems[dindex].de_time;
deltapri = DFS_DIFF(searchpri, refpri);
deltapri_2 = DFS_DIFF(searchpri, 2*refpri);
deltapri_3 = DFS_DIFF(searchpri, 3*refpri);
if (rf->rf_ignore_pri_window==2) {
pri_match = ((deltapri < primargin) || (deltapri_2 < primargin) || (deltapri_3 < primargin));
} else {
pri_match = (deltapri < primargin);
}
if (pri_match)
score[n]++;
}
} else {
score[n] = 0;
}
if( score[n] > rf->rf_threshold) {
/* we got the most possible candidate,
* no need to continue further */
break;
}
}
/* find out the high scorer */
highscore = 0;
highscoreindex = 0;
for (n=0;n<dl->dl_numelems; n++) {
if( score[n] > highscore) {
highscore = score[n];
highscoreindex = n;
}
else if( score[n] == highscore ) {
/*more than one pri has highscore take the least pri */
delayindex = (dl->dl_firstelem + highscoreindex) & DFS_MAX_DL_MASK;
dindex = (dl->dl_firstelem + n) & DFS_MAX_DL_MASK;
if( dl->dl_elems[dindex].de_time <=
dl->dl_elems[delayindex].de_time ) {
highscoreindex = n;
}
}
}
/* find the average pri of pulses around the pri of highscore or
* the pulses around the lowest pri */
if (rf->rf_ignore_pri_window > 0) {
lowprichk = (rf->rf_threshold >> 1)+1;
} else {
lowprichk = 3;
}
if( highscore < lowprichk) {
scoreindex = lowpriindex;
}
else {
scoreindex = highscoreindex;
}
/* We got the possible pri, save its parameters as reference */
delayindex = (dl->dl_firstelem + scoreindex) & DFS_MAX_DL_MASK;
refdur = dl->dl_elems[delayindex].de_dur;
refpri = dl->dl_elems[delayindex].de_time;
averagerefpri = 0;
if (rf->rf_fixed_pri_radar_pulse) {
refpri = (rf->rf_minpri + rf->rf_maxpri)/2;
}
numpulses = dfs_bin_pri_check(dfs, rf, dl, score[scoreindex], refpri,
refdur, ext_chan_flag, refpri);
if (numpulses >= dfs_get_filter_threshold(dfs, rf, ext_chan_flag)) {
found = 1;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, "ext_flag=%d MATCH filter=%u numpulses=%u thresh=%u refdur=%d refpri=%d primargin=%d\n", ext_chan_flag, rf->rf_pulseid, numpulses,rf->rf_threshold, refdur, refpri, primargin);
dfs_print_delayline(dfs, &rf->rf_dl);
dfs_print_filter(dfs, rf);
}
return found;
}
int
dfs_bin_pri_check(struct ath_dfs *dfs, struct dfs_filter *rf,
struct dfs_delayline *dl, u_int32_t score, u_int32_t refpri,
u_int32_t refdur, int ext_chan_flag, int fundamentalpri)
{
u_int32_t searchpri, searchdur, searchrssi, deltapri = 0,deltapri1 = 0, deltapri2 = 0, deltadur, averagerefpri=0,MatchCount = 0;
u_int32_t delta_ts_variance, delta_time_stamps, prev_good_timestamp=0;
int delayindex, dindex;
u_int32_t i, j=0, primargin, durmargin, highscore=score, highscoreindex=0;
int numpulses=1; //first pulse in the burst is most likely being filtered out based on maxfilterlen
int priscorechk=1,numpulsetochk=2,primatch=0;
int32_t sidx_min = DFS_BIG_SIDX;
int32_t sidx_max = -DFS_BIG_SIDX;
u_int8_t delta_peak_match_count = 1;
//Use the adjusted PRI margin to reduce false alarms
/* For non fixed pattern types, rf->rf_patterntype=0*/
primargin = dfs_get_pri_margin(dfs, ext_chan_flag, (rf->rf_patterntype==1));
if ( (refpri > rf->rf_maxpri) || (refpri < rf->rf_minpri) ) {
numpulses = 0;
return numpulses;
}
if(rf->rf_maxdur < 10) {
durmargin = 4;
} else {
durmargin = 6;
}
if ((!rf->rf_fixed_pri_radar_pulse)) {
if (rf->rf_ignore_pri_window==1) {
priscorechk = (rf->rf_threshold >> 1);
} else {
priscorechk = 1;
}
MatchCount = 0;
if( score > priscorechk) {
for (i=0;i<dl->dl_numelems; i++) {
dindex = (dl->dl_firstelem + i) & DFS_MAX_DL_MASK;
searchpri = dl->dl_elems[dindex].de_time;
deltapri = DFS_DIFF(searchpri, refpri);
if( deltapri < primargin) {
averagerefpri += searchpri;
MatchCount++;
}
}
if (rf->rf_patterntype != 2) {
if (MatchCount > 0)
refpri = (averagerefpri/MatchCount); //average
} else {
refpri = (averagerefpri/score);
}
}
}
/* Note: Following primultiple calculation should be done once per filter
* during initialization stage (dfs_attach) and stored in its array
* atleast for fixed frequency types like FCC Bin1 to save some CPU cycles.
* multiplication, devide operators in the following code are left as it is
* for readability hoping the complier will use left/right shifts wherever possible
*/
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"refpri = %d high score = %d index = %d numpulses = %d\n",
refpri, highscore, highscoreindex, numpulses);
/* Count the other delay elements that have pri and dur with in the
* acceptable range from the reference one */
for (i=0; i<dl->dl_numelems; i++) {
delayindex = (dl->dl_firstelem + i) & DFS_MAX_DL_MASK;
searchpri = dl->dl_elems[delayindex].de_time;
if( searchpri == 0) {
/* This events PRI is zero, take it as a
* valid pulse but decrement next event's PRI by refpri
*/
dindex = (delayindex+1)& DFS_MAX_DL_MASK;
dl->dl_elems[dindex].de_time -= refpri;
searchpri = refpri;
}
searchdur = dl->dl_elems[delayindex].de_dur;
searchrssi = dl->dl_elems[delayindex].de_rssi;
deltadur = DFS_DIFF(searchdur, refdur);
deltapri = DFS_DIFF(searchpri, refpri);
//deltapri3 = DFS_DIFF(searchpri, 3 * refpri);
primatch=0;
if ((rf->rf_ignore_pri_window>0) && (rf->rf_patterntype!=2)) {
for (j=0;j<rf->rf_numpulses;j++){
deltapri1 = DFS_DIFF(searchpri, (j+1)*refpri);
if (deltapri1 < (2*primargin)) {
primatch = 1;
break;
}
}
} else {
if (( deltapri1 < primargin) || ( deltapri2 < primargin)) {
primatch = 1;
}
}
if ( primatch && ( deltadur < durmargin) ) {
if ( (numpulses == 1) ) {
dl->dl_seq_num_second =
dl->dl_elems[delayindex].de_seq_num;
/* update sidx min/max for false detection check later*/
numpulses++;
if (sidx_min > dl->dl_elems[delayindex].de_sidx)
sidx_min = dl->dl_elems[delayindex].de_sidx;
if (sidx_max < dl->dl_elems[delayindex].de_sidx)
sidx_max = dl->dl_elems[delayindex].de_sidx;
if ((rf->rf_check_delta_peak) &&
(dl->dl_elems[delayindex].de_delta_peak != 0))
delta_peak_match_count++;
} else {
delta_time_stamps = dl->dl_elems[delayindex].de_ts -
prev_good_timestamp;
if ((rf->rf_ignore_pri_window>0)) {
numpulsetochk = rf->rf_numpulses;
if ((rf->rf_patterntype==2) && (fundamentalpri<refpri+100)) {
numpulsetochk = 4;
}
} else {
numpulsetochk = 4;
}
for (j = 0; j < numpulsetochk; j++){
delta_ts_variance = DFS_DIFF(delta_time_stamps, ((j+1)*fundamentalpri));
if ( delta_ts_variance < (2*(j+1)*primargin) ) {
dl->dl_seq_num_stop =
dl->dl_elems[delayindex].de_seq_num;
numpulses++;
/**
* update sidx min/max for false detection check
* later
*/
if (sidx_min > dl->dl_elems[delayindex].de_sidx)
sidx_min = dl->dl_elems[delayindex].de_sidx;
if (sidx_max < dl->dl_elems[delayindex].de_sidx)
sidx_max = dl->dl_elems[delayindex].de_sidx;
if ((rf->rf_check_delta_peak) &&
(dl->dl_elems[delayindex].de_delta_peak
!= 0))
delta_peak_match_count++;
if (rf->rf_ignore_pri_window>0) {
break;
}
}
}
}
prev_good_timestamp = dl->dl_elems[delayindex].de_ts;
dl->dl_search_pri = searchpri;
dl->dl_min_sidx = sidx_min;
dl->dl_max_sidx = sidx_max;
dl->dl_delta_peak_match_count = delta_peak_match_count;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"rf->minpri=%d rf->maxpri=%d searchpri = %d index = %d numpulses = %d deltapri=%d j=%d\n",
rf->rf_minpri, rf->rf_maxpri, searchpri, i, numpulses, deltapri, j);
}
}
return numpulses;
}
#endif /* ATH_SUPPORT_DFS */