blob: 7c48309942ca9b33caa4538f35b97a14beb28d9e [file] [log] [blame]
/*
* Copyright (c) 2002-2014 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,
uint32_t dur, int ext_chan_flag)
{
struct dfs_pulseline *pl = dfs->pulses;
int i, n, refpri, primargin, numpulses = 0;
uint64_t start_ts, end_ts, event_ts, prev_event_ts, next_event_ts,
window_start, window_end;
uint32_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",
(uint32_t) window_start, (uint32_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", (uint32_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,
uint32_t deltaT, uint64_t this_ts)
{
uint32_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;
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 = (uint32_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, uint32_t deltaT,
uint32_t width, int ext_chan_flag)
{
uint32_t refpri, refdur, searchpri, deltapri, deltapri_2, deltapri_3,
averagerefpri;
uint32_t n, i, primargin, durmargin, highscore, highscoreindex;
int score[DFS_MAX_DL_SIZE], delayindex, dindex, found = 0;
struct dfs_delayline *dl;
uint32_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, uint32_t score, uint32_t refpri,
uint32_t refdur, int ext_chan_flag, int fundamentalpri)
{
uint32_t searchpri, searchdur, searchrssi, deltapri = 0, deltapri1 =
0, deltapri2 = 0, deltadur, averagerefpri = 0, MatchCount = 0;
uint32_t delta_ts_variance, delta_time_stamps, prev_good_timestamp = 0;
int delayindex, dindex;
uint32_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;
/* 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)) {
numpulses++;
} 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)) {
numpulses++;
if (rf->rf_ignore_pri_window >
0) {
break;
}
}
}
}
prev_good_timestamp = dl->dl_elems[delayindex].de_ts;
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 */