qcacld-3.0: Initial snapshot of ihelium wlan driver
qcacld-3.0: Initial snapshot of ihelium wlan driver to match code-scanned SU Release 5.0.0.139. This is open-source version of wlan for next Android release. Change-Id: Icf598ca97da74f84bea607e4e902d1889806f507
This commit is contained in:
257
core/sap/dfs/inc/ath_dfs_structs.h
Normal file
257
core/sap/dfs/inc/ath_dfs_structs.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2011-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.
|
||||
*/
|
||||
|
||||
/*===========================================================================
|
||||
|
||||
ath_dfs_structs.h
|
||||
|
||||
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
|
||||
---------- --- --------------------------------------------------------
|
||||
|
||||
===========================================================================*/
|
||||
|
||||
#ifndef _DFS__STRUCTS_H_
|
||||
#define _DFS__STRUCTS_H_
|
||||
#include <cdf_memory.h>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <linux/string.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For the dfs_nol_clist_update() method - this is the
|
||||
* update command.
|
||||
*/
|
||||
enum {
|
||||
DFS_NOL_CLIST_CMD_NONE = 0x0,
|
||||
DFS_NOL_CLIST_CMD_UPDATE = 0x1,
|
||||
};
|
||||
|
||||
struct ath_dfs_caps {
|
||||
uint32_t ath_dfs_ext_chan_ok:1,
|
||||
/* Can radar be detected on the extension chan? */
|
||||
ath_dfs_combined_rssi_ok:1,
|
||||
/* Can use combined radar RSSI?
|
||||
* the following flag is used to indicate if radar detection
|
||||
* scheme should use enhanced chirping detection algorithm.
|
||||
* This flag also determines if certain radar data should be
|
||||
* discarded to minimize false detection of radar.
|
||||
*/
|
||||
ath_dfs_use_enhancement:1,
|
||||
ath_strong_signal_diversiry:1,
|
||||
ath_chip_is_bb_tlv:1;
|
||||
|
||||
/*
|
||||
* goes with ath_strong_signal_diversiry:
|
||||
* If we have fast diversity capability, read off
|
||||
* Strong Signal fast diversity count set in the ini
|
||||
* file, and store so we can restore the value when
|
||||
* radar is disabled
|
||||
*/
|
||||
uint32_t ath_fastdiv_val;
|
||||
};
|
||||
|
||||
/*
|
||||
* These are defined in the HAL for now, and must be migrated outside
|
||||
* of there in order to be used by the new partial offload data path.
|
||||
*/
|
||||
|
||||
struct dfs_pulse {
|
||||
uint32_t rp_numpulses; /* Num of pulses in radar burst */
|
||||
uint32_t rp_pulsedur; /* Duration of each pulse in usecs */
|
||||
uint32_t rp_pulsefreq; /* Frequency of pulses in burst */
|
||||
uint32_t rp_max_pulsefreq; /* Frequency of pulses in burst */
|
||||
uint32_t rp_patterntype; /* fixed or variable pattern type */
|
||||
uint32_t rp_pulsevar; /* Time variation of pulse duration for
|
||||
matched filter (single-sided) in usecs */
|
||||
uint32_t rp_threshold; /* Threshold for MF output to indicateC
|
||||
radar match */
|
||||
uint32_t rp_mindur; /* Min pulse duration to be considered for
|
||||
this pulse type */
|
||||
uint32_t rp_maxdur; /* Max pusle duration to be considered for
|
||||
this pulse type */
|
||||
uint32_t rp_rssithresh; /* Min rssi to be considered a radar pulse */
|
||||
uint32_t rp_meanoffset; /* Offset for timing adjustment */
|
||||
int32_t rp_rssimargin; /* rssi threshold margin. In Turbo Mode HW
|
||||
* reports rssi 3dBm lower than in non TURBO
|
||||
* mode. This will be used to offset that
|
||||
* diff.
|
||||
*/
|
||||
uint32_t rp_ignore_pri_window;
|
||||
uint32_t rp_pulseid; /* Unique ID for identifying filter */
|
||||
};
|
||||
|
||||
struct dfs_staggered_pulse {
|
||||
uint32_t rp_numpulses; /* Num of pulses in radar burst */
|
||||
uint32_t rp_pulsedur; /* Duration of each pulse in usecs */
|
||||
uint32_t rp_min_pulsefreq; /* Frequency of pulses in burst */
|
||||
uint32_t rp_max_pulsefreq; /* Frequency of pulses in burst */
|
||||
uint32_t rp_patterntype; /* fixed or variable pattern type */
|
||||
uint32_t rp_pulsevar; /* Time variation of pulse duration for
|
||||
matched filter (single-sided) in usecs */
|
||||
uint32_t rp_threshold; /* Thershold for MF output to indicateC
|
||||
radar match */
|
||||
uint32_t rp_mindur; /* Min pulse duration to be considered for
|
||||
this pulse type */
|
||||
uint32_t rp_maxdur; /* Max pusle duration to be considered for
|
||||
this pulse type */
|
||||
uint32_t rp_rssithresh; /* Min rssi to be considered a radar pulse */
|
||||
uint32_t rp_meanoffset; /* Offset for timing adjustment */
|
||||
int32_t rp_rssimargin; /* rssi threshold margin. In Turbo Mode HW
|
||||
reports rssi 3dBm lower than in non TURBO
|
||||
mode. This will be used to offset that
|
||||
diff. */
|
||||
uint32_t rp_pulseid; /* Unique ID for identifying filter */
|
||||
};
|
||||
|
||||
struct dfs_bin5pulse {
|
||||
uint32_t b5_threshold; /* Num of bin5 pulses to indicate detection */
|
||||
uint32_t b5_mindur; /* Min duration for a bin5 pulse */
|
||||
uint32_t b5_maxdur; /* Max duration for a bin5 pulse */
|
||||
uint32_t b5_timewindow; /* Window over which to count bin5 pulses */
|
||||
uint32_t b5_rssithresh; /* Min rssi to be considered a pulse */
|
||||
uint32_t b5_rssimargin; /* rssi threshold margin. In Turbo Mode HW
|
||||
* reports rssi 3dB
|
||||
*/
|
||||
};
|
||||
|
||||
/*
|
||||
* DFS NOL representation.
|
||||
*
|
||||
* This is used to represent the DFS NOL information between the
|
||||
* NOL code in lmac/dfs/dfs_nol.c and any driver layer wishing
|
||||
* to use it.
|
||||
*/
|
||||
struct dfs_nol_chan_entry {
|
||||
uint32_t nol_chfreq; /* Centre frequency, MHz */
|
||||
uint32_t nol_chwidth; /* Width, MHz */
|
||||
unsigned long nol_start_ticks; /* start ticks, OS specific */
|
||||
uint32_t nol_timeout_ms; /* timeout, mS */
|
||||
};
|
||||
|
||||
/* HAL_PHYERR_PARAM; */
|
||||
|
||||
/*
|
||||
* This represents the general case of the radar PHY configuration,
|
||||
* across all chips.
|
||||
*
|
||||
* It's then up to each chip layer to translate to/from this
|
||||
* (eg to HAL_PHYERR_PARAM for the HAL case.)
|
||||
*/
|
||||
|
||||
#define ATH_DFS_PHYERR_PARAM_NOVAL 0xFFFF
|
||||
#define ATH_DFS_PHYERR_PARAM_ENABLE 0x8000
|
||||
|
||||
struct ath_dfs_phyerr_param {
|
||||
int32_t pe_firpwr; /* FIR pwr out threshold */
|
||||
int32_t pe_rrssi; /* Radar rssi thresh */
|
||||
int32_t pe_height; /* Pulse height thresh */
|
||||
int32_t pe_prssi; /* Pulse rssi thresh */
|
||||
int32_t pe_inband; /* Inband thresh */
|
||||
|
||||
/* The following params are only for AR5413 and later */
|
||||
/*
|
||||
* Relative power threshold in 0.5dB steps
|
||||
*/
|
||||
uint32_t pe_relpwr;
|
||||
|
||||
/*
|
||||
* Pulse Relative step threshold in 0.5dB steps
|
||||
*/
|
||||
uint32_t pe_relstep;
|
||||
|
||||
/*
|
||||
* Max length of radar sign in 0.8us units
|
||||
*/
|
||||
uint32_t pe_maxlen;
|
||||
|
||||
/*
|
||||
* Use the average in-band power measured over 128 cycles
|
||||
*/
|
||||
bool pe_usefir128;
|
||||
|
||||
/*
|
||||
* Enable to block radar check if pkt detect is done via OFDM
|
||||
* weak signal detect or pkt is detected immediately after tx
|
||||
* to rx transition
|
||||
*/
|
||||
bool pe_blockradar;
|
||||
|
||||
/*
|
||||
* Enable to use the max rssi instead of the last rssi during
|
||||
* fine gain changes for radar detection
|
||||
*/
|
||||
bool pe_enmaxrssi;
|
||||
};
|
||||
|
||||
static inline void ath_dfs_phyerr_param_copy(struct ath_dfs_phyerr_param *dst,
|
||||
struct ath_dfs_phyerr_param *src)
|
||||
{
|
||||
cdf_mem_copy(dst, src, sizeof(*dst));
|
||||
}
|
||||
|
||||
static inline void ath_dfs_phyerr_init_noval(struct ath_dfs_phyerr_param *pe)
|
||||
{
|
||||
pe->pe_firpwr = ATH_DFS_PHYERR_PARAM_NOVAL;
|
||||
pe->pe_rrssi = ATH_DFS_PHYERR_PARAM_NOVAL;
|
||||
pe->pe_height = ATH_DFS_PHYERR_PARAM_NOVAL;
|
||||
pe->pe_prssi = ATH_DFS_PHYERR_PARAM_NOVAL;
|
||||
pe->pe_inband = ATH_DFS_PHYERR_PARAM_NOVAL;
|
||||
pe->pe_relpwr = ATH_DFS_PHYERR_PARAM_NOVAL;
|
||||
pe->pe_relstep = ATH_DFS_PHYERR_PARAM_NOVAL;
|
||||
pe->pe_maxlen = ATH_DFS_PHYERR_PARAM_NOVAL;
|
||||
|
||||
/* XXX what about usefir128, blockradar, enmaxrssi? */
|
||||
}
|
||||
|
||||
struct ath_dfs_radar_tab_info {
|
||||
uint32_t dfsdomain;
|
||||
int numradars;
|
||||
struct dfs_pulse *dfs_radars;
|
||||
int numb5radars;
|
||||
struct dfs_bin5pulse *b5pulses;
|
||||
struct ath_dfs_phyerr_param dfs_defaultparams;
|
||||
int dfs_pri_multiplier;
|
||||
};
|
||||
#endif /* _DFS__STRUCTS_H_ */
|
824
core/sap/dfs/inc/dfs.h
Normal file
824
core/sap/dfs/inc/dfs.h
Normal file
@@ -0,0 +1,824 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2015 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.h
|
||||
|
||||
OVERVIEW:
|
||||
|
||||
Source code borrowed from QCA_MAIN DFS module
|
||||
|
||||
DEPENDENCIES:
|
||||
|
||||
Are listed for each API below.
|
||||
|
||||
===========================================================================*/
|
||||
|
||||
#ifndef _DFS_H_
|
||||
#define _DFS_H_
|
||||
|
||||
/*
|
||||
*TO DO DFS- Need to include this file later on
|
||||
*#include "ath_internal.h"
|
||||
*/
|
||||
/*DFS New Include Start*/
|
||||
|
||||
#include <cdf_net_types.h> /* CDF_NBUF_EXEMPT_NO_EXEMPTION, etc. */
|
||||
#include <cdf_nbuf.h> /* cdf_nbuf_t, etc. */
|
||||
#include <cdf_util.h> /* cdf_assert */
|
||||
#include <cdf_lock.h> /* cdf_spinlock */
|
||||
#include <cds_queue.h> /* TAILQ */
|
||||
#include <cdf_time.h>
|
||||
#include <cdf_softirq_timer.h>
|
||||
#include <cdf_memory.h>
|
||||
#include <osdep.h>
|
||||
/*DFS Utility Include END*/
|
||||
|
||||
/* From wlan_modules/include/ */
|
||||
#include "ath_dfs_structs.h"
|
||||
/*DFS - Newly added File to interface cld UMAC and dfs data structures*/
|
||||
#include <wma_dfs_interface.h>
|
||||
/*
|
||||
*TO DO DFS- Need to include this file later on
|
||||
#include "ah.h"
|
||||
*/
|
||||
/* #include "ah_desc.h" */
|
||||
#include "dfs_ioctl.h"
|
||||
#include "dfs_ioctl_private.h"
|
||||
#include "dfs_interface.h"
|
||||
#include "cds_ieee80211_common.h"
|
||||
#include "cds_api.h"
|
||||
|
||||
#define ATH_SUPPORT_DFS 1
|
||||
#define CHANNEL_TURBO 0x00010
|
||||
#define DFS_PRINTK(_fmt, ...) printk((_fmt), __VA_ARGS__)
|
||||
#define DFS_DPRINTK(dfs, _m, _fmt, ...) do { \
|
||||
if (((dfs) == NULL) || \
|
||||
((dfs) != NULL && \
|
||||
((_m) & (dfs)->dfs_debug_mask))) { \
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_DEBUG, \
|
||||
_fmt, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DFS_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define DFS_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define DFS_DIFF(a, b) (DFS_MAX(a, b) - DFS_MIN(a, b))
|
||||
/*
|
||||
* Maximum number of radar events to be processed in a single iteration.
|
||||
* Allows soft watchdog to run.
|
||||
*/
|
||||
#define MAX_EVENTS 100
|
||||
|
||||
#define DFS_STATUS_SUCCESS 0
|
||||
#define DFS_STATUS_FAIL 1
|
||||
|
||||
/*
|
||||
* Constants to use for chirping detection.
|
||||
*
|
||||
* All are unconverted as HW reports them.
|
||||
*
|
||||
* XXX Are these constants with or without fast clock 5GHz operation?
|
||||
* XXX Peregrine reports pulses in microseconds, not hardware clocks!
|
||||
*/
|
||||
#define MIN_BIN5_DUR 63 /* 50 * 1.25 */
|
||||
#define MIN_BIN5_DUR_MICROSEC 50
|
||||
#define MAYBE_BIN5_DUR 35 /* 28 * 1.25 */
|
||||
#define MAYBE_BIN5_DUR_MICROSEC 28
|
||||
/* #define MAX_BIN5_DUR 131 / * 105 * 1.25* / */
|
||||
/* use 145 for osprey conversion is already done using dfs->dur_multiplier */
|
||||
#define MAX_BIN5_DUR 145
|
||||
#define MAX_BIN5_DUR_MICROSEC 105
|
||||
|
||||
#define DFS_MARGIN_EQUAL(a, b, margin) ((DFS_DIFF(a, b)) <= margin)
|
||||
#define DFS_MAX_STAGGERED_BURSTS 3
|
||||
|
||||
/*
|
||||
* All filter thresholds in the radar filter tables
|
||||
* are effective at a 50% channel loading
|
||||
*/
|
||||
#define DFS_CHAN_LOADING_THRESH 50
|
||||
#define DFS_EXT_CHAN_LOADING_THRESH 30
|
||||
#define DFS_DEFAULT_PRI_MARGIN 6
|
||||
#define DFS_DEFAULT_FIXEDPATTERN_PRI_MARGIN 4
|
||||
#define ATH_DFSQ_LOCK(_dfs) cdf_spin_lock_bh((&(_dfs)->dfs_radarqlock))
|
||||
#define ATH_DFSQ_UNLOCK(_dfs) cdf_spin_unlock_bh((&(_dfs)->dfs_radarqlock))
|
||||
#define ATH_DFSQ_LOCK_INIT(_dfs) cdf_spinlock_init(&(_dfs)->dfs_radarqlock)
|
||||
|
||||
#define ATH_ARQ_LOCK(_dfs) cdf_spin_lock_bh((&(_dfs)->dfs_arqlock))
|
||||
#define ATH_ARQ_UNLOCK(_dfs) cdf_spin_unlock_bh((&(_dfs)->dfs_arqlock))
|
||||
#define ATH_ARQ_LOCK_INIT(_dfs) cdf_spinlock_init(&(_dfs)->dfs_arqlock)
|
||||
|
||||
#define ATH_DFSEVENTQ_LOCK(_dfs) cdf_spin_lock_bh((&(_dfs)->dfs_eventqlock))
|
||||
#define ATH_DFSEVENTQ_UNLOCK(_dfs) cdf_spin_unlock_bh((&(_dfs)->dfs_eventqlock))
|
||||
#define ATH_DFSEVENTQ_LOCK_INIT(_dfs) \
|
||||
cdf_spinlock_init((&(_dfs)->dfs_eventqlock))
|
||||
/* Mask for time stamp from descriptor */
|
||||
#define DFS_TSMASK 0xFFFFFFFF
|
||||
/* Shift for time stamp from descriptor */
|
||||
#define DFS_TSSHIFT 32
|
||||
/* 64 bit TSF wrap value */
|
||||
#define DFS_TSF_WRAP 0xFFFFFFFFFFFFFFFFULL
|
||||
/* TS mask for 64 bit value */
|
||||
#define DFS_64BIT_TSFMASK 0x0000000000007FFFULL
|
||||
|
||||
#define DFS_AR_RADAR_RSSI_THR 5 /* in dB */
|
||||
#define DFS_AR_RADAR_RESET_INT 1 /* in secs */
|
||||
#define DFS_AR_RADAR_MAX_HISTORY 500
|
||||
#define DFS_AR_REGION_WIDTH 128
|
||||
#define DFS_AR_RSSI_THRESH_STRONG_PKTS 17 /* in dB */
|
||||
#define DFS_AR_RSSI_DOUBLE_THRESHOLD 15 /* in dB */
|
||||
#define DFS_AR_MAX_NUM_ACK_REGIONS 9
|
||||
#define DFS_AR_ACK_DETECT_PAR_THRESH 20
|
||||
#define DFS_AR_PKT_COUNT_THRESH 20
|
||||
|
||||
#define DFS_MAX_DL_SIZE 64
|
||||
#define DFS_MAX_DL_MASK 0x3F
|
||||
|
||||
#define DFS_NOL_TIME DFS_NOL_TIMEOUT_US
|
||||
/* 30 minutes in usecs */
|
||||
|
||||
#define DFS_WAIT_TIME (60 * 1000000) /* 1 minute in usecs */
|
||||
|
||||
#define DFS_DISABLE_TIME (3 * 60 * 1000000) /* 3 minutes in usecs */
|
||||
|
||||
#define DFS_MAX_B5_SIZE 128
|
||||
#define DFS_MAX_B5_MASK 0x0000007F /* 128 */
|
||||
|
||||
#define DFS_MAX_RADAR_OVERLAP 16 /* Max number of overlapping filters */
|
||||
/* Max number of dfs events which can be q'd */
|
||||
#define DFS_MAX_EVENTS 1024
|
||||
|
||||
#define DFS_RADAR_EN 0x80000000 /* Radar detect is capable */
|
||||
#define DFS_AR_EN 0x40000000 /* AR detect is capable */
|
||||
#define DFS_MAX_RSSI_VALUE 0x7fffffff /* Max rssi value */
|
||||
/* max num of pulses in a burst */
|
||||
#define DFS_BIN_MAX_PULSES 60
|
||||
#define DFS_BIN5_PRI_LOWER_LIMIT 990 /* us */
|
||||
|
||||
/* to cover the single pusle burst case, change from 2010 us to 2010000 us */
|
||||
|
||||
/*
|
||||
* this is reverted back to 2010 as larger value causes false
|
||||
* bin5 detect (EV76432, EV76320)
|
||||
*/
|
||||
#define DFS_BIN5_PRI_HIGHER_LIMIT 2010 /* us */
|
||||
|
||||
#define DFS_BIN5_WIDTH_MARGIN 4 /* us */
|
||||
#define DFS_BIN5_RSSI_MARGIN 5 /* dBm */
|
||||
/*Following threshold is not specified but should be okay statistically*/
|
||||
#define DFS_BIN5_BRI_LOWER_LIMIT 300000 /* us */
|
||||
#define DFS_BIN5_BRI_UPPER_LIMIT 12000000 /* us */
|
||||
/* Max number of pulses kept in buffer */
|
||||
#define DFS_MAX_PULSE_BUFFER_SIZE 1024
|
||||
#define DFS_MAX_PULSE_BUFFER_MASK 0x3ff
|
||||
|
||||
#define DFS_FAST_CLOCK_MULTIPLIER (800/11)
|
||||
#define DFS_NO_FAST_CLOCK_MULTIPLIER (80)
|
||||
|
||||
#define DFS_WAR_PLUS_30_MHZ_SEPARATION 30
|
||||
#define DFS_WAR_MINUS_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
|
||||
#define DFS_ETSI_TYPE2_TYPE3_WAR_PULSE_DUR_LOWER_LIMIT 15
|
||||
#define DFS_ETSI_TYPE2_TYPE3_WAR_PULSE_DUR_UPPER_LIMIT 33
|
||||
#define DFS_ETSI_TYPE2_WAR_PRI_LOWER_LIMIT 625
|
||||
#define DFS_ETSI_TYPE2_WAR_PRI_UPPER_LIMIT 5000
|
||||
#define DFS_ETSI_TYPE3_WAR_PRI_LOWER_LIMIT 250
|
||||
#define DFS_ETSI_TYPE3_WAR_PRI_UPPER_LIMIT 435
|
||||
#define DFS_ETSI_WAR_VALID_PULSE_DURATION 15
|
||||
|
||||
typedef cdf_spinlock_t dfsq_lock_t;
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(push, dfs_pulseparams, 1)
|
||||
#endif
|
||||
struct dfs_pulseparams {
|
||||
uint64_t p_time; /* time for start of pulse in usecs */
|
||||
uint8_t p_dur; /* Duration of pulse in usecs */
|
||||
uint8_t p_rssi; /* Duration of pulse in usecs */
|
||||
} cdf_packed;
|
||||
#ifdef WIN32
|
||||
#pragma pack(pop, dfs_pulseparams)
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(push, dfs_pulseline, 1)
|
||||
#endif
|
||||
struct dfs_pulseline {
|
||||
/* pl_elems - array of pulses in delay line */
|
||||
struct dfs_pulseparams pl_elems[DFS_MAX_PULSE_BUFFER_SIZE];
|
||||
uint32_t pl_firstelem; /* Index of the first element */
|
||||
uint32_t pl_lastelem; /* Index of the last element */
|
||||
uint32_t pl_numelems; /* Number of elements in the delay line */
|
||||
} cdf_packed;
|
||||
#ifdef WIN32
|
||||
#pragma pack(pop, dfs_pulseline)
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(push, dfs_event, 1)
|
||||
#endif
|
||||
|
||||
#define DFS_EVENT_CHECKCHIRP 0x01 /* Whether to check the chirp flag */
|
||||
#define DFS_EVENT_HW_CHIRP 0x02 /* hardware chirp */
|
||||
#define DFS_EVENT_SW_CHIRP 0x04 /* software chirp */
|
||||
|
||||
/*
|
||||
* Use this only if the event has CHECKCHIRP set.
|
||||
*/
|
||||
#define DFS_EVENT_ISCHIRP(e) \
|
||||
((e)->re_flags & (DFS_EVENT_HW_CHIRP | DFS_EVENT_SW_CHIRP))
|
||||
|
||||
/*
|
||||
* Check if the given event is to be rejected as not possibly
|
||||
* a chirp. This means:
|
||||
* (a) it's a hardware or software checked chirp, and
|
||||
* (b) the HW/SW chirp bits are both 0.
|
||||
*/
|
||||
#define DFS_EVENT_NOTCHIRP(e) \
|
||||
(((e)->re_flags & (DFS_EVENT_CHECKCHIRP)) && \
|
||||
(!DFS_EVENT_ISCHIRP((e))))
|
||||
|
||||
struct dfs_event {
|
||||
uint64_t re_full_ts; /* 64-bit full timestamp from interrupt time */
|
||||
uint32_t re_ts; /* Original 15 bit recv timestamp */
|
||||
uint8_t re_rssi; /* rssi of radar event */
|
||||
uint8_t re_dur; /* duration of radar pulse */
|
||||
uint8_t re_chanindex; /* Channel of event */
|
||||
uint8_t re_flags; /* Event flags */
|
||||
uint32_t re_freq; /* Centre frequency of event, KHz */
|
||||
uint32_t re_freq_lo; /* Lower bounds of frequency, KHz */
|
||||
uint32_t re_freq_hi; /* Upper bounds of frequency, KHz */
|
||||
int sidx; /* Pulse Index as in radar summary report */
|
||||
STAILQ_ENTRY(dfs_event) re_list; /* List of radar events */
|
||||
} cdf_packed;
|
||||
#ifdef WIN32
|
||||
#pragma pack(pop, dfs_event)
|
||||
#endif
|
||||
|
||||
#define DFS_AR_MAX_ACK_RADAR_DUR 511
|
||||
#define DFS_AR_MAX_NUM_PEAKS 3
|
||||
#define DFS_AR_ARQ_SIZE 2048 /* 8K AR events for buffer size */
|
||||
#define DFS_AR_ARQ_SEQSIZE 2049 /* Sequence counter wrap for AR */
|
||||
|
||||
#define DFS_RADARQ_SIZE 512 /* 1K radar events for buffer size */
|
||||
#define DFS_RADARQ_SEQSIZE 513 /* Sequence counter wrap for radar */
|
||||
/* Number of radar channels we keep state for */
|
||||
#define DFS_NUM_RADAR_STATES 64
|
||||
/* Max number radar filters for each type */
|
||||
#define DFS_MAX_NUM_RADAR_FILTERS 10
|
||||
/* Number of different radar types */
|
||||
#define DFS_MAX_RADAR_TYPES 32
|
||||
|
||||
struct dfs_ar_state {
|
||||
uint32_t ar_prevwidth;
|
||||
uint32_t ar_phyerrcount[DFS_AR_MAX_ACK_RADAR_DUR];
|
||||
uint32_t ar_acksum;
|
||||
uint32_t ar_packetthreshold; /* Thresh to determine traffic load */
|
||||
uint32_t ar_parthreshold; /* Thresh to determine peak */
|
||||
uint32_t ar_radarrssi; /* Rssi threshold for AR event */
|
||||
uint16_t ar_prevtimestamp;
|
||||
uint16_t ar_peaklist[DFS_AR_MAX_NUM_PEAKS];
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(push, dfs_delayelem, 1)
|
||||
#endif
|
||||
struct dfs_delayelem {
|
||||
/* Current "filter" time for start of pulse in usecs */
|
||||
uint32_t de_time;
|
||||
/* Duration of pulse in usecs */
|
||||
uint8_t de_dur;
|
||||
/* rssi of pulse in dB */
|
||||
uint8_t de_rssi;
|
||||
/* time stamp for this delay element */
|
||||
uint64_t de_ts;
|
||||
} cdf_packed;
|
||||
#ifdef WIN32
|
||||
#pragma pack(pop, dfs_delayelem)
|
||||
#endif
|
||||
|
||||
/* NB: The first element in the circular buffer is the oldest element */
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(push, dfs_delayline, 1)
|
||||
#endif
|
||||
struct dfs_delayline {
|
||||
/* Array of pulses in delay line */
|
||||
struct dfs_delayelem dl_elems[DFS_MAX_DL_SIZE];
|
||||
/* Last timestamp the delay line was used (in usecs) */
|
||||
uint64_t dl_last_ts;
|
||||
/* Index of the first element */
|
||||
uint32_t dl_firstelem;
|
||||
/* Index of the last element */
|
||||
uint32_t dl_lastelem;
|
||||
/* Number of elements in the delay line */
|
||||
uint32_t dl_numelems;
|
||||
} cdf_packed;
|
||||
#ifdef WIN32
|
||||
#pragma pack(pop, dfs_delayline)
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(push, dfs_filter, 1)
|
||||
#endif
|
||||
struct dfs_filter {
|
||||
/* Delay line of pulses for this filter */
|
||||
struct dfs_delayline rf_dl;
|
||||
/* Number of pulses in the filter */
|
||||
uint32_t rf_numpulses;
|
||||
/* min pri to be considered for this filter */
|
||||
uint32_t rf_minpri;
|
||||
/* max pri to be considered for this filter */
|
||||
uint32_t rf_maxpri;
|
||||
/* match filter output threshold for radar detect */
|
||||
uint32_t rf_threshold;
|
||||
/* Length (in usecs) of the filter */
|
||||
uint32_t rf_filterlen;
|
||||
/* fixed or variable pattern type */
|
||||
uint32_t rf_patterntype;
|
||||
/* indicates if it is a fixed pri pulse */
|
||||
uint32_t rf_fixed_pri_radar_pulse;
|
||||
/* Min duration for this radar filter */
|
||||
uint32_t rf_mindur;
|
||||
/* Max duration for this radar filter */
|
||||
uint32_t rf_maxdur;
|
||||
uint32_t rf_ignore_pri_window;
|
||||
/* Unique ID corresponding to the original filter ID */
|
||||
uint32_t rf_pulseid;
|
||||
} cdf_packed;
|
||||
#ifdef WIN32
|
||||
#pragma pack(pop, dfs_filter)
|
||||
#endif
|
||||
|
||||
struct dfs_filtertype {
|
||||
struct dfs_filter ft_filters[DFS_MAX_NUM_RADAR_FILTERS];
|
||||
/* Duration of pulse which specifies filter type */
|
||||
uint32_t ft_filterdur;
|
||||
/* Num filters of this type */
|
||||
uint32_t ft_numfilters;
|
||||
/* Last timestamp this filtertype was used(in usecs) */
|
||||
uint64_t ft_last_ts;
|
||||
/* min pulse duration to be considered for this filter type */
|
||||
uint32_t ft_mindur;
|
||||
/* max pulse duration to be considered for this filter type */
|
||||
uint32_t ft_maxdur;
|
||||
/* min rssi to be considered for this filter type */
|
||||
uint32_t ft_rssithresh;
|
||||
/* Num pulses in each filter of this type */
|
||||
uint32_t ft_numpulses;
|
||||
/* fixed or variable pattern type */
|
||||
uint32_t ft_patterntype;
|
||||
/* min pri to be considered for this type */
|
||||
uint32_t ft_minpri;
|
||||
/* rssi threshold margin. In Turbo Mode HW
|
||||
* reports rssi 3dB lower than in non TURBO
|
||||
* mode. This will offset that diff.
|
||||
*/
|
||||
uint32_t ft_rssimargin;
|
||||
};
|
||||
|
||||
struct dfs_state {
|
||||
struct ieee80211_channel rs_chan; /* Channel info */
|
||||
uint8_t rs_chanindex; /* Channel index in radar structure */
|
||||
uint32_t rs_numradarevents; /* Number of radar events */
|
||||
|
||||
struct ath_dfs_phyerr_param rs_param;
|
||||
};
|
||||
|
||||
/* 30 minutes in seconds */
|
||||
#define DFS_NOL_TIMEOUT_S (30*60)
|
||||
/* 5 minutes in seconds - debugging */
|
||||
/* #define DFS_NOL_TIMEOUT_S (5*60) */
|
||||
#define DFS_NOL_TIMEOUT_MS (DFS_NOL_TIMEOUT_S * 1000)
|
||||
#define DFS_NOL_TIMEOUT_US (DFS_NOL_TIMEOUT_MS * 1000)
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(push, dfs_nolelem, 1)
|
||||
#endif
|
||||
struct dfs_nolelem {
|
||||
uint32_t nol_freq; /* centre frequency */
|
||||
uint32_t nol_chwidth; /* event width (MHz) */
|
||||
unsigned long nol_start_ticks; /* NOL start time in OS ticks */
|
||||
uint32_t nol_timeout_ms; /* NOL timeout value in msec */
|
||||
os_timer_t nol_timer; /* per element NOL timer */
|
||||
struct dfs_nolelem *nol_next; /* next element pointer */
|
||||
} cdf_packed;
|
||||
#ifdef WIN32
|
||||
#pragma pack(pop, dfs_nolelem)
|
||||
#endif
|
||||
|
||||
/* Pass structure to DFS NOL timer */
|
||||
struct dfs_nol_timer_arg {
|
||||
struct ath_dfs *dfs;
|
||||
uint16_t delfreq;
|
||||
uint16_t delchwidth;
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma pack(push, dfs_info, 1)
|
||||
#endif
|
||||
struct dfs_info {
|
||||
/* Use the NOL when radar found (default: true) */
|
||||
int rn_use_nol;
|
||||
/* Number of different types of radars */
|
||||
uint32_t rn_numradars;
|
||||
/* Last 64 bit timstamp from recv interrupt */
|
||||
uint64_t rn_lastfull_ts;
|
||||
/* last 15 bit ts from recv descriptor */
|
||||
uint16_t rn_last_ts;
|
||||
/* last unique 32 bit ts from recv descriptor */
|
||||
uint32_t rn_last_unique_ts;
|
||||
/* Prefix to prepend to 15 bit recv ts */
|
||||
uint64_t rn_ts_prefix;
|
||||
/* Number of bin5 radar pulses to search for */
|
||||
uint32_t rn_numbin5radars;
|
||||
/* Value of fast diversity gc limit from init file */
|
||||
uint32_t rn_fastdivGCval;
|
||||
/* Min rssi for all radar types */
|
||||
int32_t rn_minrssithresh;
|
||||
/* Max pulse width in TSF ticks */
|
||||
uint32_t rn_maxpulsedur;
|
||||
|
||||
uint8_t dfs_ext_chan_busy;
|
||||
uint64_t ext_chan_busy_ts;
|
||||
|
||||
uint64_t dfs_bin5_chirp_ts;
|
||||
uint8_t dfs_last_bin5_dur;
|
||||
} cdf_packed;
|
||||
#ifdef WIN32
|
||||
#pragma pack(pop, dfs_info)
|
||||
#endif
|
||||
|
||||
struct dfs_bin5elem {
|
||||
uint64_t be_ts; /* Timestamp for the bin5 element */
|
||||
uint32_t be_rssi; /* Rssi for the bin5 element */
|
||||
uint32_t be_dur; /* Duration of bin5 element */
|
||||
};
|
||||
|
||||
struct dfs_bin5radars {
|
||||
/* List of bin5 elems that fall within the time window */
|
||||
struct dfs_bin5elem br_elems[DFS_MAX_B5_SIZE];
|
||||
/* Index of the first element */
|
||||
uint32_t br_firstelem;
|
||||
/* Index of the last element */
|
||||
uint32_t br_lastelem;
|
||||
/* Number of elements in the delay line */
|
||||
uint32_t br_numelems;
|
||||
/* Original info about bin5 pulse */
|
||||
struct dfs_bin5pulse br_pulse;
|
||||
};
|
||||
|
||||
struct dfs_stats {
|
||||
uint32_t num_radar_detects; /* total num. of radar detects */
|
||||
uint32_t total_phy_errors;
|
||||
uint32_t owl_phy_errors;
|
||||
uint32_t pri_phy_errors;
|
||||
uint32_t ext_phy_errors;
|
||||
uint32_t dc_phy_errors;
|
||||
uint32_t early_ext_phy_errors;
|
||||
uint32_t bwinfo_errors;
|
||||
uint32_t datalen_discards;
|
||||
uint32_t rssi_discards;
|
||||
uint64_t last_reset_tstamp;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is for debuggin DFS as console log interferes with (helps)
|
||||
* radar detection
|
||||
*/
|
||||
|
||||
#define DFS_EVENT_LOG_SIZE 256
|
||||
struct dfs_event_log {
|
||||
uint64_t ts; /* 64-bit full timestamp from interrupt time */
|
||||
uint32_t diff_ts; /* diff timestamp */
|
||||
uint8_t rssi; /* rssi of radar event */
|
||||
uint8_t dur; /* duration of radar pulse */
|
||||
};
|
||||
|
||||
#define ATH_DFS_RESET_TIME_S 7
|
||||
#define ATH_DFS_WAIT (60 + ATH_DFS_RESET_TIME_S) /* 60 seconds */
|
||||
#define ATH_DFS_WAIT_MS ((ATH_DFS_WAIT) * 1000) /*in MS */
|
||||
|
||||
#define ATH_DFS_WEATHER_CHANNEL_WAIT_MIN 10 /*10 minutes */
|
||||
#define ATH_DFS_WEATHER_CHANNEL_WAIT_S (ATH_DFS_WEATHER_CHANNEL_WAIT_MIN * 60)
|
||||
#define ATH_DFS_WEATHER_CHANNEL_WAIT_MS \
|
||||
((ATH_DFS_WEATHER_CHANNEL_WAIT_S) * 1000) /*in MS */
|
||||
|
||||
#define ATH_DFS_WAIT_POLL_PERIOD 2 /* 2 seconds */
|
||||
/*in MS */
|
||||
#define ATH_DFS_WAIT_POLL_PERIOD_MS ((ATH_DFS_WAIT_POLL_PERIOD) * 1000)
|
||||
#define ATH_DFS_TEST_RETURN_PERIOD 2 /* 2 seconds */
|
||||
/* n MS */
|
||||
#define ATH_DFS_TEST_RETURN_PERIOD_MS ((ATH_DFS_TEST_RETURN_PERIOD) * 1000)
|
||||
#define IS_CHANNEL_WEATHER_RADAR(chan) ((chan->ic_freq >= 5600) && \
|
||||
(chan->ic_freq <= 5650))
|
||||
|
||||
#define DFS_DEBUG_TIMEOUT_S 30 /* debug timeout is 30 seconds */
|
||||
#define DFS_DEBUG_TIMEOUT_MS (DFS_DEBUG_TIMEOUT_S * 1000)
|
||||
|
||||
#define RSSI_POSSIBLY_FALSE 50
|
||||
#define SEARCH_FFT_REPORT_PEAK_MAG_THRSH 40
|
||||
|
||||
struct ath_dfs {
|
||||
uint32_t dfs_debug_mask; /* current debug bitmask */
|
||||
int16_t dfs_curchan_radindex; /* cur. channel radar index */
|
||||
int16_t dfs_extchan_radindex; /* extension channel radar index */
|
||||
uint32_t dfsdomain; /* cur. DFS domain */
|
||||
uint32_t dfs_proc_phyerr; /* Flags for Phy Errs to process */
|
||||
struct ieee80211com *ic;
|
||||
STAILQ_HEAD(, dfs_event) dfs_eventq; /* Q of free dfs event objects */
|
||||
dfsq_lock_t dfs_eventqlock; /* Lock for free dfs event list */
|
||||
STAILQ_HEAD(, dfs_event) dfs_radarq; /* Q of radar events */
|
||||
dfsq_lock_t dfs_radarqlock; /* Lock for dfs q */
|
||||
STAILQ_HEAD(, dfs_event) dfs_arq; /* Q of AR events */
|
||||
dfsq_lock_t dfs_arqlock; /* Lock for AR q */
|
||||
|
||||
struct dfs_ar_state dfs_ar_state; /* AR state */
|
||||
|
||||
/* dfs_radar - Per-Channel Radar detector state */
|
||||
struct dfs_state dfs_radar[DFS_NUM_RADAR_STATES];
|
||||
|
||||
/* dfs_radarf - One filter for each radar pulse type */
|
||||
struct dfs_filtertype *dfs_radarf[DFS_MAX_RADAR_TYPES];
|
||||
|
||||
struct dfs_info dfs_rinfo; /* State vars for radar processing */
|
||||
struct dfs_bin5radars *dfs_b5radars; /* array of bin5 radar events */
|
||||
int8_t **dfs_radartable; /* map of radar durs to filter types */
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
struct dfs_nolelem *dfs_nol; /* Non occupancy list for radar */
|
||||
int dfs_nol_count; /* How many items? */
|
||||
#endif
|
||||
/* Default phy params per radar state */
|
||||
struct ath_dfs_phyerr_param dfs_defaultparams;
|
||||
struct dfs_stats ath_dfs_stats; /* DFS related stats */
|
||||
struct dfs_pulseline *pulses; /* pulse history */
|
||||
struct dfs_event *events; /* Events structure */
|
||||
|
||||
uint32_t ath_radar_tasksched:1, /* radar task is scheduled */
|
||||
ath_dfswait:1, /* waiting on channel for radar detect */
|
||||
ath_dfstest:1; /* Test timer in progress */
|
||||
struct ath_dfs_caps dfs_caps;
|
||||
/* IEEE chan num to return to after
|
||||
* a dfs mute test
|
||||
*/
|
||||
uint8_t ath_dfstest_ieeechan;
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
uint32_t ath_dfs_cac_time; /* CAC period */
|
||||
uint32_t ath_dfstesttime; /* Time to stay off chan during dfs test */
|
||||
os_timer_t ath_dfswaittimer; /* dfs wait timer */
|
||||
os_timer_t ath_dfstesttimer; /* dfs mute test timer */
|
||||
os_timer_t ath_dfs_debug_timer; /* dfs debug timer */
|
||||
uint8_t dfs_bangradar;
|
||||
#endif
|
||||
os_timer_t ath_dfs_task_timer; /* dfs wait timer */
|
||||
int dur_multiplier;
|
||||
|
||||
uint16_t ath_dfs_isdfsregdomain; /* true when we are DFS domain */
|
||||
int ath_dfs_false_rssi_thres;
|
||||
int ath_dfs_peak_mag;
|
||||
|
||||
struct dfs_event_log radar_log[DFS_EVENT_LOG_SIZE];
|
||||
int dfs_event_log_count;
|
||||
int dfs_event_log_on;
|
||||
int dfs_phyerr_count; /* same as number of PHY radar interrupts */
|
||||
/*
|
||||
* when TLV is supported, # of radar events ignored after TLV is parsed
|
||||
*/
|
||||
int dfs_phyerr_reject_count;
|
||||
/* number of radar events queued for matching the filters */
|
||||
int dfs_phyerr_queued_count;
|
||||
int dfs_phyerr_freq_min;
|
||||
int dfs_phyerr_freq_max;
|
||||
int dfs_phyerr_w53_counter;
|
||||
/* allow pulse if they are within multiple of PRI for the radar type */
|
||||
int dfs_pri_multiplier;
|
||||
int ath_dfs_nol_timeout;
|
||||
int dfs_pri_multiplier_ini; /* dfs pri configuration from ini */
|
||||
/*
|
||||
* Flag to indicate if DFS test mode is enabled and
|
||||
* channel switch is disabled.
|
||||
*/
|
||||
int8_t disable_dfs_ch_switch;
|
||||
};
|
||||
|
||||
/* This should match the table from if_ath.c */
|
||||
enum {
|
||||
ATH_DEBUG_DFS = 0x00000100, /* Minimal DFS debug */
|
||||
ATH_DEBUG_DFS1 = 0x00000200, /* Normal DFS debug */
|
||||
ATH_DEBUG_DFS2 = 0x00000400, /* Maximal DFS debug */
|
||||
ATH_DEBUG_DFS3 = 0x00000800, /* matched filterID display */
|
||||
|
||||
ATH_DEBUG_DFS_PHYERR = 0x00001000, /* phy error parsing */
|
||||
ATH_DEBUG_DFS_NOL = 0x00002000, /* NOL related entries */
|
||||
ATH_DEBUG_DFS_PHYERR_SUM = 0x00004000, /* PHY error summary */
|
||||
ATH_DEBUG_DFS_PHYERR_PKT = 0x00008000, /* PHY error payload */
|
||||
|
||||
ATH_DEBUG_DFS_BIN5 = 0x00010000, /* bin5 checks */
|
||||
ATH_DEBUG_DFS_BIN5_FFT = 0x00020000, /* bin5 FFT check */
|
||||
ATH_DEBUG_DFS_BIN5_PULSE = 0x00040000, /* bin5 pulse check */
|
||||
};
|
||||
|
||||
#define IS_CHAN_HT40(_c) IEEE80211_IS_CHAN_11N_HT40(_c)
|
||||
#define IS_CHAN_HT40_PLUS(_c) IEEE80211_IS_CHAN_11N_HT40PLUS(_c)
|
||||
#define IS_CHAN_HT40_MINUS(_c) IEEE80211_IS_CHAN_11N_HT40MINUS(_c)
|
||||
|
||||
/*
|
||||
* chirp notes!
|
||||
*
|
||||
* Pre-Sowl chips don't do FFT reports, so chirp pulses simply show up
|
||||
* as long duration pulses.
|
||||
*
|
||||
* The bin5 checking code would simply look for a chirp pulse of the correct
|
||||
* duration (within MIN_BIN5_DUR and MAX_BIN5_DUR) and add it to the "chirp"
|
||||
* pattern.
|
||||
*
|
||||
* For Sowl and later, an FFT was done on longer duration frames. If those
|
||||
* frames looked like a chirp, their duration was adjusted to fall within
|
||||
* the chirp duration limits. If the pulse failed the chirp test (it had
|
||||
* no FFT data or the FFT didn't meet the chirping requirements) then the
|
||||
* pulse duration was adjusted to be greater than MAX_BIN5_DUR, so it
|
||||
* would always fail chirp detection.
|
||||
*
|
||||
* This is pretty horrible.
|
||||
*
|
||||
* The eventual goal for chirp handling is thus:
|
||||
*
|
||||
* + In case someone ever wants to do chirp detection with this code on
|
||||
* chips that don't support chirp detection, you can still do it based
|
||||
* on pulse duration. That's your problem to solve.
|
||||
*
|
||||
* + For chips that do hardware chirp detection or FFT, the "do_check_chirp"
|
||||
* bit should be set.
|
||||
*
|
||||
* + Then, either is_hw_chirp or is_sw_chirp is set, indicating that
|
||||
* the hardware or software post-processing of the chirp event found
|
||||
* that indeed it was a chirp.
|
||||
*
|
||||
* + Finally, the bin5 code should just check whether the chirp bits are
|
||||
* set and behave appropriately, falling back onto the duration checks
|
||||
* if someone wishes to use this on older hardware (or with disabled
|
||||
* FFTs, for whatever reason.)
|
||||
*/
|
||||
/*
|
||||
* XXX TODO:
|
||||
*
|
||||
* + add duration in uS and raw duration, so the PHY error parsing
|
||||
* code is responsible for doing the duration calculation;
|
||||
* + add ts in raw and corrected, so the PHY error parsing
|
||||
* code is responsible for doing the offsetting, not the radar
|
||||
* event code.
|
||||
*/
|
||||
struct dfs_phy_err {
|
||||
uint64_t fulltsf; /* 64-bit TSF as read from MAC */
|
||||
|
||||
uint32_t is_pri:1, /* detected on primary channel */
|
||||
is_ext:1, /* detected on extension channel */
|
||||
is_dc:1, /* detected at DC */
|
||||
is_early:1, /* early detect */
|
||||
do_check_chirp:1, /* whether to check hw_chirp/sw_chirp */
|
||||
is_hw_chirp:1, /* hardware-detected chirp */
|
||||
is_sw_chirp:1; /* software detected chirp */
|
||||
|
||||
uint32_t rs_tstamp; /* 32 bit TSF from RX descriptor (event) */
|
||||
uint32_t freq; /* Centre frequency of event - KHz */
|
||||
uint32_t freq_lo; /* Lower bounds of frequency - KHz */
|
||||
uint32_t freq_hi; /* Upper bounds of frequency - KHz */
|
||||
|
||||
uint8_t rssi; /* pulse RSSI */
|
||||
uint8_t dur; /* pulse duration, raw (not uS) */
|
||||
int sidx; /* Pulse Index as in radar summary report */
|
||||
};
|
||||
|
||||
/* Attach, detach, handle ioctl prototypes */
|
||||
|
||||
int dfs_get_thresholds(struct ieee80211com *ic,
|
||||
struct ath_dfs_phyerr_param *param);
|
||||
int dfs_set_thresholds(struct ieee80211com *ic,
|
||||
const uint32_t threshtype, const uint32_t value);
|
||||
|
||||
/* PHY error and radar event handling */
|
||||
int dfs_process_radarevent(struct ath_dfs *dfs, struct ieee80211_channel *chan);
|
||||
|
||||
/* Non occupancy (NOL) handling prototypes */
|
||||
void dfs_nol_addchan(struct ath_dfs *dfs, struct ieee80211_channel *chan,
|
||||
uint32_t dfs_nol_timeout);
|
||||
void dfs_get_nol(struct ath_dfs *dfs, struct dfsreq_nolelem *dfs_nol,
|
||||
int *nchan);
|
||||
void dfs_set_nol(struct ath_dfs *dfs, struct dfsreq_nolelem *dfs_nol,
|
||||
int nchan);
|
||||
void dfs_nol_update(struct ath_dfs *dfs);
|
||||
void dfs_nol_timer_cleanup(struct ath_dfs *dfs);
|
||||
|
||||
/* FCC Bin5 detection prototypes */
|
||||
int dfs_bin5_check_pulse(struct ath_dfs *dfs, struct dfs_event *re,
|
||||
struct dfs_bin5radars *br);
|
||||
int dfs_bin5_addpulse(struct ath_dfs *dfs, struct dfs_bin5radars *br,
|
||||
struct dfs_event *re, uint64_t thists);
|
||||
int dfs_bin5_check(struct ath_dfs *dfs);
|
||||
int dfs_check_chirping(struct ath_dfs *dfs, void *buf,
|
||||
uint16_t datalen, int is_ctl,
|
||||
int is_ext, int *slope, int *is_dc);
|
||||
uint8_t dfs_retain_bin5_burst_pattern(struct ath_dfs *dfs, uint32_t diff_ts,
|
||||
uint8_t old_dur);
|
||||
uint8_t dfs_retain_bin5_burst_pattern(struct ath_dfs *dfs, uint32_t diff_ts,
|
||||
uint8_t old_dur);
|
||||
int dfs_get_random_bin5_dur(struct ath_dfs *dfs, uint64_t tstamp);
|
||||
|
||||
/* Debug prototypes */
|
||||
void dfs_print_delayline(struct ath_dfs *dfs, struct dfs_delayline *dl);
|
||||
void dfs_print_nol(struct ath_dfs *dfs);
|
||||
void dfs_print_filters(struct ath_dfs *dfs);
|
||||
void dfs_print_activity(struct ath_dfs *dfs);
|
||||
os_timer_func(dfs_debug_timeout);
|
||||
void dfs_print_filter(struct ath_dfs *dfs, struct dfs_filter *rf);
|
||||
|
||||
/* Misc prototypes */
|
||||
uint32_t dfs_round(int32_t val);
|
||||
struct dfs_state *dfs_getchanstate(struct ath_dfs *dfs, uint8_t *index,
|
||||
int ext_ch_flag);
|
||||
|
||||
/* Reset and init data structures */
|
||||
|
||||
int dfs_init_radar_filters(struct ieee80211com *ic,
|
||||
struct ath_dfs_radar_tab_info *radar_info);
|
||||
void dfs_reset_alldelaylines(struct ath_dfs *dfs);
|
||||
void dfs_reset_delayline(struct dfs_delayline *dl);
|
||||
void dfs_reset_filter_delaylines(struct dfs_filtertype *dft);
|
||||
void dfs_reset_radarq(struct ath_dfs *dfs);
|
||||
|
||||
/* Detection algorithm prototypes */
|
||||
void dfs_add_pulse(struct ath_dfs *dfs, struct dfs_filter *rf,
|
||||
struct dfs_event *re, uint32_t deltaT, uint64_t this_ts);
|
||||
|
||||
int dfs_bin_fixedpattern_check(struct ath_dfs *dfs, struct dfs_filter *rf,
|
||||
uint32_t dur, int ext_chan_flag);
|
||||
int dfs_bin_check(struct ath_dfs *dfs, struct dfs_filter *rf,
|
||||
uint32_t deltaT, uint32_t dur, int ext_chan_flag);
|
||||
|
||||
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);
|
||||
int dfs_staggered_check(struct ath_dfs *dfs, struct dfs_filter *rf,
|
||||
uint32_t deltaT, uint32_t width);
|
||||
/* False detection reduction */
|
||||
int dfs_get_pri_margin(struct ath_dfs *dfs, int is_extchan_detect,
|
||||
int is_fixed_pattern);
|
||||
int dfs_get_filter_threshold(struct ath_dfs *dfs, struct dfs_filter *rf,
|
||||
int is_extchan_detect);
|
||||
|
||||
/* AR related prototypes */
|
||||
|
||||
/* Commenting out since all the ar functions are obsolete and
|
||||
* the function definition has been removed as part of dfs_ar.c
|
||||
* void dfs_process_ar_event(struct ath_dfs *dfs,
|
||||
* struct ieee80211_channel *chan);
|
||||
*/
|
||||
/* Commenting out since all the ar functions are obsolete and
|
||||
* the function definition has been removed as part of dfs_ar.c
|
||||
* void ath_ar_disable(struct ath_dfs *dfs);
|
||||
*/
|
||||
/* Commenting out since all the ar functions are obsolete and
|
||||
* the function definition has been removed as part of dfs_ar.c
|
||||
* void ath_ar_enable(struct ath_dfs *dfs);
|
||||
*/
|
||||
void dfs_reset_ar(struct ath_dfs *dfs);
|
||||
/* Commenting out since all the ar functions are obsolete and
|
||||
* the function definition has been removed as part of dfs_ar.c
|
||||
* void dfs_reset_arq(struct ath_dfs *dfs);
|
||||
*/
|
||||
|
||||
struct ieee80211_channel *ieee80211_get_extchan(struct ieee80211com *ic);
|
||||
|
||||
#endif /* _DFS_H_ */
|
171
core/sap/dfs/inc/dfs_interface.h
Normal file
171
core/sap/dfs/inc/dfs_interface.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2015 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_interface.h
|
||||
|
||||
OVERVIEW:
|
||||
|
||||
Source code borrowed from QCA_MAIN DFS module
|
||||
|
||||
DEPENDENCIES:
|
||||
|
||||
Are listed for each API below.
|
||||
|
||||
===========================================================================*/
|
||||
|
||||
#ifndef _DFS__INTERFACE_H_
|
||||
#define _DFS__INTERFACE_H_
|
||||
|
||||
/*
|
||||
* These are the only functions exported to the upper (device) layer.
|
||||
*/
|
||||
|
||||
/*
|
||||
* EXPORT_SYMBOL(dfs_attach);
|
||||
* EXPORT_SYMBOL(dfs_detach);
|
||||
* EXPORT_SYMBOL(dfs_radar_enable);
|
||||
* EXPORT_SYMBOL(dfs_process_phyerr);
|
||||
* EXPORT_SYMBOL(dfs_control);
|
||||
* EXPORT_SYMBOL(dfs_clear_stats);
|
||||
* EXPORT_SYMBOL(dfs_usenol);
|
||||
* EXPORT_SYMBOL(dfs_isdfsregdomain);
|
||||
*/
|
||||
|
||||
/*
|
||||
* These are exported but not currently defined here; these should be
|
||||
* evaluated.
|
||||
*
|
||||
* EXPORT_SYMBOL(dfs_process_ar_event); -- legacy adaptive radio processing
|
||||
* EXPORT_SYMBOL(ath_ar_disable);
|
||||
* EXPORT_SYMBOL(ath_ar_enable);
|
||||
* EXPORT_SYMBOL(dfs_get_thresholds);
|
||||
* EXPORT_SYMBOL(dfs_init_radar_filters);
|
||||
* EXPORT_SYMBOL(dfs_getchanstate);
|
||||
*/
|
||||
|
||||
uint16_t dfs_isdfsregdomain(struct ieee80211com *ic);
|
||||
int dfs_attach(struct ieee80211com *ic);
|
||||
void dfs_detach(struct ieee80211com *ic);
|
||||
int dfs_radar_enable(struct ieee80211com *ic,
|
||||
struct ath_dfs_radar_tab_info *ri);
|
||||
int dfs_radar_disable(struct ieee80211com *ic);
|
||||
extern void dfs_process_phyerr(struct ieee80211com *ic, void *buf,
|
||||
uint16_t datalen, uint8_t rssi, uint8_t ext_rssi,
|
||||
uint32_t rs_tstamp, uint64_t fulltsf,
|
||||
bool enable_log);
|
||||
int dfs_control(struct ieee80211com *ic, u_int id, void *indata,
|
||||
uint32_t insize, void *outdata, uint32_t *outsize);
|
||||
void dfs_clear_stats(struct ieee80211com *ic);
|
||||
#if 0
|
||||
/* The following are for FCC Bin 1-4 pulses */
|
||||
struct dfs_pulse dfs_fcc_radars[] = {
|
||||
/* FCC TYPE 1 */
|
||||
/* {18, 1, 325, 1930, 0, 6, 7, 0, 1, 18, 0, 3, 0}, // 518 to 3066 */
|
||||
{18, 1, 700, 700, 0, 6, 5, 0, 1, 18, 0, 3, 1, 0},
|
||||
{18, 1, 350, 350, 0, 6, 5, 0, 1, 18, 0, 3, 0, 0},
|
||||
|
||||
/* FCC TYPE 6 */
|
||||
/* {9, 1, 3003, 3003, 1, 7, 5, 0, 1, 18, 0, 0, 1}, // 333 +/- 7 us */
|
||||
{9, 1, 3003, 3003, 1, 7, 5, 0, 1, 18, 0, 0, 1, 1},
|
||||
|
||||
/* FCC TYPE 2 */
|
||||
{23, 5, 4347, 6666, 0, 18, 11, 0, 7, 22, 0, 3, 0, 2},
|
||||
|
||||
/* FCC TYPE 3 */
|
||||
{18, 10, 2000, 5000, 0, 23, 8, 6, 13, 22, 0, 3, 0, 5},
|
||||
|
||||
/* FCC TYPE 4 */
|
||||
{16, 15, 2000, 5000, 0, 25, 7, 11, 23, 22, 0, 3, 0, 11},
|
||||
};
|
||||
|
||||
struct dfs_pulse dfs_mkk4_radars[] = {
|
||||
/* following two filters are specific to Japan/MKK4 */
|
||||
/* {18, 1, 720, 720, 1, 6, 6, 0, 1, 18, 0, 3, 17}, // 1389 +/- 6 us */
|
||||
/* {18, 4, 250, 250, 1, 10, 5, 1, 6, 18, 0, 3, 18}, // 4000 +/- 6 us */
|
||||
/* {18, 5, 260, 260, 1, 10, 6, 1, 6, 18, 0, 3, 19}, // 3846 +/- 7 us */
|
||||
{18, 1, 720, 720, 0, 6, 6, 0, 1, 18, 0, 3, 0, 17}, /* 1389 +/- 6 us */
|
||||
{18, 4, 250, 250, 0, 10, 5, 1, 6, 18, 0, 3, 0, 18}, /* 4000 +/- 6 us */
|
||||
{18, 5, 260, 260, 0, 10, 6, 1, 6, 18, 0, 3, 1, 19}, /* 3846 +/- 7 us */
|
||||
|
||||
/* following filters are common to both FCC and JAPAN */
|
||||
|
||||
/* FCC TYPE 1 */
|
||||
/* {18, 1, 325, 1930, 0, 6, 7, 0, 1, 18, 0, 3, 0}, // 518 to 3066 */
|
||||
{18, 1, 700, 700, 0, 6, 5, 0, 1, 18, 0, 3, 1, 0},
|
||||
{18, 1, 350, 350, 0, 6, 5, 0, 1, 18, 0, 3, 0, 0},
|
||||
|
||||
/* FCC TYPE 6 */
|
||||
/* {9, 1, 3003, 3003, 1, 7, 5, 0, 1, 18, 0, 0, 1}, // 333 +/- 7 us */
|
||||
{9, 1, 3003, 3003, 1, 7, 5, 0, 1, 18, 0, 0, 1, 1},
|
||||
|
||||
/* FCC TYPE 2 */
|
||||
{23, 5, 4347, 6666, 0, 18, 11, 0, 7, 22, 0, 3, 0, 2},
|
||||
|
||||
/* FCC TYPE 3 */
|
||||
{18, 10, 2000, 5000, 0, 23, 8, 6, 13, 22, 0, 3, 0, 5},
|
||||
|
||||
/* FCC TYPE 4 */
|
||||
{16, 15, 2000, 5000, 0, 25, 7, 11, 23, 22, 0, 3, 0, 11},
|
||||
};
|
||||
|
||||
struct dfs_bin5pulse dfs_fcc_bin5pulses[] = {
|
||||
{4, 28, 105, 12, 22, 5},
|
||||
};
|
||||
|
||||
struct dfs_bin5pulse dfs_jpn_bin5pulses[] = {
|
||||
{5, 28, 105, 12, 22, 5},
|
||||
};
|
||||
|
||||
struct dfs_pulse dfs_etsi_radars[] = {
|
||||
|
||||
/* TYPE staggered pulse */
|
||||
/* 0.8-2us, 2-3 bursts,300-400 PRF, 10 pulses each */
|
||||
{30, 2, 300, 400, 2, 30, 3, 0, 5, 15, 0, 0, 1, 31}, /* Type 5 */
|
||||
/* 0.8-2us, 2-3 bursts, 400-1200 PRF, 15 pulses each */
|
||||
{30, 2, 400, 1200, 2, 30, 7, 0, 5, 15, 0, 0, 0, 32}, /* Type 6 */
|
||||
|
||||
/* constant PRF based */
|
||||
/* 0.8-5us, 200 300 PRF, 10 pulses */
|
||||
{10, 5, 200, 400, 0, 24, 5, 0, 8, 15, 0, 0, 2, 33}, /* Type 1 */
|
||||
{10, 5, 400, 600, 0, 24, 5, 0, 8, 15, 0, 0, 2, 37}, /* Type 1 */
|
||||
{10, 5, 600, 800, 0, 24, 5, 0, 8, 15, 0, 0, 2, 38}, /* Type 1 */
|
||||
{10, 5, 800, 1000, 0, 24, 5, 0, 8, 15, 0, 0, 2, 39}, /* Type 1 */
|
||||
/* {10, 5, 200, 1000, 0, 24, 5, 0, 8, 15, 0, 0, 2, 33}, */
|
||||
|
||||
/* 0.8-15us, 200-1600 PRF, 15 pulses */
|
||||
{15, 15, 200, 1600, 0, 24, 8, 0, 18, 24, 0, 0, 0, 34}, /* Type 2 */
|
||||
|
||||
/* 0.8-15us, 2300-4000 PRF, 25 pulses */
|
||||
{25, 15, 2300, 4000, 0, 24, 10, 0, 18, 24, 0, 0, 0, 35}, /* Type 3 */
|
||||
|
||||
/* 20-30us, 2000-4000 PRF, 20 pulses */
|
||||
{20, 30, 2000, 4000, 0, 24, 6, 19, 33, 24, 0, 0, 0, 36}, /* Type 4 */
|
||||
};
|
||||
#endif
|
||||
#endif /* _DFS__INTERFACE_H_ */
|
142
core/sap/dfs/inc/radar_filters.h
Normal file
142
core/sap/dfs/inc/radar_filters.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2015 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.
|
||||
*/
|
||||
|
||||
/*===========================================================================
|
||||
|
||||
radar_filters.h
|
||||
|
||||
OVERVIEW:
|
||||
|
||||
Source code borrowed from QCA_MAIN DFS module
|
||||
|
||||
DEPENDENCIES:
|
||||
|
||||
Are listed for each API below.
|
||||
|
||||
===========================================================================*/
|
||||
|
||||
struct dfs_pulse dfs_fcc_radars[] = {
|
||||
/* FCC NEW TYPE 0 */
|
||||
/* {18, 1, 325, 1930, 0, 6, 7, 0, 1, 18, 0, 3, 0}, // 518 to 3066 */
|
||||
{18, 1, 700, 700, 0, 6, 5, 0, 1, 18, 0, 3, 1, 0},
|
||||
{18, 1, 350, 350, 0, 6, 5, 0, 1, 18, 0, 3, 0, 0},
|
||||
|
||||
/* FCC TYPE 6 */
|
||||
/* {9, 1, 3003, 3003, 1, 7, 5, 0, 1, 18, 0, 0, 1}, // 333 +/- 7 us */
|
||||
{9, 1, 3003, 3003, 1, 7, 5, 0, 1, 18, 0, 0, 1, 1},
|
||||
|
||||
/* FCC TYPE 2 */
|
||||
{23, 5, 4347, 6666, 0, 18, 8, 0, 7, 22, 0, 3, 0, 2},
|
||||
|
||||
/* FCC TYPE 3 */
|
||||
{18, 10, 2000, 5000, 0, 23, 6, 6, 13, 22, 0, 3, 0, 5},
|
||||
|
||||
/* FCC TYPE 4 */
|
||||
{16, 15, 2000, 5000, 0, 25, 5, 11, 23, 22, 0, 3, 0, 11},
|
||||
|
||||
/*
|
||||
* FCC NEW TYPE 1
|
||||
* 518us to 938us pulses (min 56 pulses)
|
||||
*/
|
||||
{57, 1, 1066, 1930, 0, 6, 20, 0, 1, 22, 0, 3, 0, 21},
|
||||
|
||||
/*
|
||||
* FCC NEW TYPE 1
|
||||
* 938us to 2000 pulses (min 26 pulses)
|
||||
*/
|
||||
{27, 1, 500, 1066, 0, 6, 13, 0, 1, 22, 0, 3, 0, 22},
|
||||
|
||||
/*
|
||||
* FCC NEW TYPE 1
|
||||
* 2000 to 3067us pulses (min 17 pulses)
|
||||
*/
|
||||
{18, 1, 325, 500, 0, 6, 9, 0, 1, 22, 0, 3, 0, 23},
|
||||
};
|
||||
|
||||
struct dfs_pulse dfs_mkk4_radars[] = {
|
||||
/* following two filters are specific to Japan/MKK4 */
|
||||
/* {18, 1, 720, 720, 1, 6, 6, 0, 1, 18, 0, 3, 17}, // 1389 +/- 6 us */
|
||||
/* {18, 4, 250, 250, 1, 10, 5, 1, 6, 18, 0, 3, 18}, // 4000 +/- 6 us */
|
||||
/* {18, 5, 260, 260, 1, 10, 6, 1, 6, 18, 0, 3, 19}, // 3846 +/- 7 us */
|
||||
{18, 1, 720, 720, 0, 6, 6, 0, 1, 18, 0, 3, 0, 17}, /* 1389 +/- 6 us */
|
||||
{18, 4, 250, 250, 0, 10, 5, 1, 6, 18, 0, 3, 0, 18}, /* 4000 +/- 6 us */
|
||||
{18, 5, 260, 260, 0, 10, 6, 1, 6, 18, 0, 3, 1, 19}, /* 3846 +/- 7 us */
|
||||
|
||||
/* following filters are common to both FCC and JAPAN */
|
||||
|
||||
/* FCC TYPE 1 */
|
||||
/* {18, 1, 325, 1930, 0, 6, 7, 0, 1, 18, 0, 3, 0}, // 518 to 3066 */
|
||||
{18, 1, 700, 700, 0, 6, 5, 0, 1, 18, 0, 3, 1, 0},
|
||||
{18, 1, 350, 350, 0, 6, 5, 0, 1, 18, 0, 3, 0, 0},
|
||||
|
||||
/* FCC TYPE 6 */
|
||||
/* {9, 1, 3003, 3003, 1, 7, 5, 0, 1, 18, 0, 0, 1}, // 333 +/- 7 us */
|
||||
{9, 1, 3003, 3003, 1, 7, 5, 0, 1, 18, 0, 0, 1, 1},
|
||||
|
||||
/* FCC TYPE 2 */
|
||||
{23, 5, 4347, 6666, 0, 18, 8, 0, 7, 22, 0, 3, 0, 2},
|
||||
|
||||
/* FCC TYPE 3 */
|
||||
{18, 10, 2000, 5000, 0, 23, 6, 6, 13, 22, 0, 3, 0, 5},
|
||||
|
||||
/* FCC TYPE 4 */
|
||||
{16, 15, 2000, 5000, 0, 25, 5, 11, 23, 22, 0, 3, 0, 11},
|
||||
};
|
||||
|
||||
struct dfs_bin5pulse dfs_fcc_bin5pulses[] = {
|
||||
{4, 28, 105, 12, 17, 5},
|
||||
};
|
||||
|
||||
struct dfs_bin5pulse dfs_jpn_bin5pulses[] = {
|
||||
{5, 28, 105, 12, 22, 5},
|
||||
};
|
||||
|
||||
struct dfs_pulse dfs_etsi_radars[] = {
|
||||
|
||||
/* TYPE staggered pulse */
|
||||
/* 0.8-2us, 2-3 bursts,300-400 PRF, 10 pulses each */
|
||||
{30, 2, 300, 400, 2, 30, 3, 0, 5, 15, 0, 0, 1, 31}, /* Type 5 */
|
||||
/* 0.8-2us, 2-3 bursts, 400-1200 PRF, 15 pulses each */
|
||||
{30, 2, 400, 1200, 2, 30, 7, 0, 5, 15, 0, 0, 0, 32}, /* Type 6 */
|
||||
|
||||
/* constant PRF based */
|
||||
/* 0.8-5us, 200 300 PRF, 10 pulses */
|
||||
{10, 5, 200, 400, 0, 24, 5, 0, 8, 15, 0, 0, 2, 33}, /* Type 1 */
|
||||
{10, 5, 400, 600, 0, 24, 5, 0, 8, 15, 0, 0, 2, 37}, /* Type 1 */
|
||||
{10, 5, 600, 800, 0, 24, 5, 0, 8, 15, 0, 0, 2, 38}, /* Type 1 */
|
||||
{10, 5, 800, 1000, 0, 24, 5, 0, 8, 15, 0, 0, 2, 39}, /* Type 1 */
|
||||
/* {10, 5, 200, 1000, 0, 24, 5, 0, 8, 15, 0, 0, 2, 33}, */
|
||||
|
||||
/* 0.8-15us, 200-1600 PRF, 15 pulses */
|
||||
{15, 15, 200, 1600, 0, 24, 8, 0, 18, 22, 0, 0, 0, 34}, /* Type 2 */
|
||||
|
||||
/* 0.8-15us, 2300-4000 PRF, 25 pulses */
|
||||
{25, 15, 2300, 4000, 0, 24, 10, 0, 18, 22, 0, 0, 0, 35}, /* Type 3 */
|
||||
|
||||
/* 20-30us, 2000-4000 PRF, 20 pulses */
|
||||
{20, 30, 2000, 4000, 0, 24, 6, 19, 33, 24, 0, 0, 0, 36}, /* Type 4 */
|
||||
};
|
62
core/sap/dfs/sources
Normal file
62
core/sap/dfs/sources
Normal file
@@ -0,0 +1,62 @@
|
||||
#
|
||||
# sources file for DFS module
|
||||
#
|
||||
LMAC=..
|
||||
TOP=$(LMAC)\..
|
||||
INC=$(TOP)\include
|
||||
HAL=$(TOP)\hal
|
||||
ATH=$(LMAC)\ath_dev
|
||||
|
||||
!IFDEF BUILD_UMAC
|
||||
MP=$(TOP)\os\win_nwf
|
||||
INC_MP=$(MP)\include
|
||||
IF_ATH=$(TOP)\umac\if_lmac
|
||||
!ELSE
|
||||
MP=$(TOP)\winvista
|
||||
INC_MP=$(INC)\winvista
|
||||
IF_ATH=$(TOP)\if_ath_net80211
|
||||
!ENDIF
|
||||
|
||||
!include $(INC_MP)\sources.inc
|
||||
|
||||
TARGETNAME=ath_dfs
|
||||
TARGETPATH=$(TOP)\lib
|
||||
TARGETTYPE=LIBRARY
|
||||
|
||||
!IFDEF BUILD_HTC
|
||||
# Put htc include dirs at the head of the list.
|
||||
# This ensures that the htc/cdf header files will preempt any
|
||||
# header files of the same names from the regular cdf directories.
|
||||
INCLUDES= $(INCLUDES) \
|
||||
$(TOP)\htc\inc; \
|
||||
$(TOP)\htc\adf\include; \
|
||||
$(TOP)\htc\adf\winvista\nbuf; \
|
||||
$(TOP)\htc\adf\winvista\include;
|
||||
!ENDIF
|
||||
|
||||
INCLUDES= $(INCLUDES) \
|
||||
$(TOP); \
|
||||
$(ATH); \
|
||||
$(ATH_DFS); \
|
||||
$(TOP)\ath\winvista; \
|
||||
$(TOP)\ath\winvista; \
|
||||
$(HAL); \
|
||||
$(HAL)\winvista; \
|
||||
$(IF_ATH); \
|
||||
$(INC); \
|
||||
$(INC_MP); \
|
||||
$(SDXROOT)\net\inc; \
|
||||
$(DDK_INC_PATH)
|
||||
|
||||
SOURCES=$(SOURCES) \
|
||||
dfs_staggered.c \
|
||||
dfs_bindetects.c \
|
||||
dfs_misc.c \
|
||||
dfs_debug.c \
|
||||
dfs_process_radarevent.c \
|
||||
dfs_process_phyerr.c \
|
||||
dfs_nol.c \
|
||||
dfs_ar.c \
|
||||
dfs_fcc_bin5.c \
|
||||
dfs_init.c \
|
||||
dfs.c
|
993
core/sap/dfs/src/dfs.c
Normal file
993
core/sap/dfs/src/dfs.c
Normal file
@@ -0,0 +1,993 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2015 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.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 <osdep.h>
|
||||
|
||||
#ifndef ATH_SUPPORT_DFS
|
||||
#define ATH_SUPPORT_DFS 1
|
||||
|
||||
/* #include "if_athioctl.h" */
|
||||
/* #include "if_athvar.h" */
|
||||
#include "dfs_ioctl.h"
|
||||
#include "dfs.h"
|
||||
|
||||
int domainoverride = DFS_UNINIT_DOMAIN;
|
||||
|
||||
/*
|
||||
** channel switch announcement (CSA)
|
||||
** usenol=1 (default) make CSA and switch to a new channel on radar detect
|
||||
** usenol=0, make CSA with next channel same as current on radar detect
|
||||
** usenol=2, no CSA and stay on the same channel on radar detect
|
||||
**/
|
||||
|
||||
int usenol = 1;
|
||||
uint32_t dfs_debug_level = ATH_DEBUG_DFS;
|
||||
|
||||
#if 0 /* the code to call this is curently commented-out below */
|
||||
/*
|
||||
* Mark a channel as having interference detected upon it.
|
||||
*
|
||||
* This adds the interference marker to both the primary and
|
||||
* extension channel.
|
||||
*
|
||||
* XXX TODO: make the NOL and channel interference logic a bit smarter
|
||||
* so only the channel with the radar event is marked, rather than
|
||||
* both the primary and extension.
|
||||
*/
|
||||
static void
|
||||
dfs_channel_mark_radar(struct ath_dfs *dfs, struct ieee80211_channel *chan)
|
||||
{
|
||||
struct ieee80211_channel_list chan_info;
|
||||
int i;
|
||||
|
||||
/* chan->ic_flagext |= CHANNEL_INTERFERENCE; */
|
||||
|
||||
/*
|
||||
* If radar is detected in 40MHz mode, add both the primary and the
|
||||
* extension channels to the NOL. chan is the channel data we return
|
||||
* to the ath_dev layer which passes it on to the 80211 layer.
|
||||
* As we want the AP to change channels and send out a CSA,
|
||||
* we always pass back the primary channel data to the ath_dev layer.
|
||||
*/
|
||||
if ((dfs->dfs_rinfo.rn_use_nol == 1) &&
|
||||
(dfs->ic->ic_opmode == IEEE80211_M_HOSTAP ||
|
||||
dfs->ic->ic_opmode == IEEE80211_M_IBSS)) {
|
||||
chan_info.cl_nchans = 0;
|
||||
dfs->ic->ic_get_ext_chan_info(dfs->ic, &chan_info);
|
||||
|
||||
for (i = 0; i < chan_info.cl_nchans; i++) {
|
||||
if (chan_info.cl_channels[i] == NULL) {
|
||||
DFS_PRINTK("%s: NULL channel\n", __func__);
|
||||
} else {
|
||||
chan_info.cl_channels[i]->ic_flagext |=
|
||||
CHANNEL_INTERFERENCE;
|
||||
dfs_nol_addchan(dfs, chan_info.cl_channels[i],
|
||||
dfs->ath_dfs_nol_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the umac/driver channels with the new NOL information.
|
||||
*/
|
||||
dfs_nol_update(dfs);
|
||||
}
|
||||
}
|
||||
#endif /* #if 0 */
|
||||
|
||||
static os_timer_func(dfs_task)
|
||||
{
|
||||
struct ieee80211com *ic;
|
||||
struct ath_dfs *dfs = NULL;
|
||||
|
||||
OS_GET_TIMER_ARG(ic, struct ieee80211com *);
|
||||
dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
/*
|
||||
* XXX no locking?!
|
||||
*/
|
||||
if (dfs_process_radarevent(dfs, ic->ic_curchan)) {
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
|
||||
/*
|
||||
* This marks the channel (and the extension channel, if HT40) as
|
||||
* having seen a radar event. It marks CHAN_INTERFERENCE and
|
||||
* will add it to the local NOL implementation.
|
||||
*
|
||||
* This is only done for 'usenol=1', as the other two modes
|
||||
* don't do radar notification or CAC/CSA/NOL; it just notes
|
||||
* there was a radar.
|
||||
*/
|
||||
|
||||
if (dfs->dfs_rinfo.rn_use_nol == 1) {
|
||||
/* dfs_channel_mark_radar(dfs, ic->ic_curchan); */
|
||||
}
|
||||
#endif /* ATH_DFS_RADAR_DETECTION_ONLY */
|
||||
|
||||
/*
|
||||
* This calls into the umac DFS code, which sets the umac related
|
||||
* radar flags and begins the channel change machinery.
|
||||
*
|
||||
* XXX TODO: the umac NOL code isn't used, but IEEE80211_CHAN_RADAR
|
||||
* still gets set. Since the umac NOL code isn't used, that flag
|
||||
* is never cleared. This needs to be fixed. See EV 105776.
|
||||
*/
|
||||
if (dfs->dfs_rinfo.rn_use_nol == 1) {
|
||||
ic->ic_dfs_notify_radar(ic, ic->ic_curchan);
|
||||
} else if (dfs->dfs_rinfo.rn_use_nol == 0) {
|
||||
/*
|
||||
* For the test mode, don't do a CSA here; but setup the
|
||||
* test timer so we get a CSA _back_ to the original channel.
|
||||
*/
|
||||
OS_CANCEL_TIMER(&dfs->ath_dfstesttimer);
|
||||
dfs->ath_dfstest = 1;
|
||||
dfs->ath_dfstest_ieeechan = ic->ic_curchan->ic_ieee;
|
||||
dfs->ath_dfstesttime = 1; /* 1ms */
|
||||
OS_SET_TIMER(&dfs->ath_dfstesttimer,
|
||||
dfs->ath_dfstesttime);
|
||||
}
|
||||
}
|
||||
dfs->ath_radar_tasksched = 0;
|
||||
}
|
||||
|
||||
static os_timer_func(dfs_testtimer_task)
|
||||
{
|
||||
struct ieee80211com *ic;
|
||||
struct ath_dfs *dfs = NULL;
|
||||
|
||||
OS_GET_TIMER_ARG(ic, struct ieee80211com *);
|
||||
dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
|
||||
/* XXX no locking? */
|
||||
dfs->ath_dfstest = 0;
|
||||
|
||||
/*
|
||||
* Flip the channel back to the original channel.
|
||||
* Make sure this is done properly with a CSA.
|
||||
*/
|
||||
DFS_PRINTK("%s: go back to channel %d\n",
|
||||
__func__, dfs->ath_dfstest_ieeechan);
|
||||
|
||||
/*
|
||||
* XXX The mere existence of this method indirection
|
||||
* to a umac function means this code belongs in
|
||||
* the driver, _not_ here. Please fix this!
|
||||
*/
|
||||
ic->ic_start_csa(ic, dfs->ath_dfstest_ieeechan);
|
||||
}
|
||||
|
||||
static int dfs_get_debug_info(struct ieee80211com *ic, int type, void *data)
|
||||
{
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
if (data) {
|
||||
*(uint32_t *) data = dfs->dfs_proc_phyerr;
|
||||
}
|
||||
return (int)dfs->dfs_proc_phyerr;
|
||||
}
|
||||
|
||||
int dfs_attach(struct ieee80211com *ic)
|
||||
{
|
||||
int i, n;
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
struct ath_dfs_radar_tab_info radar_info;
|
||||
|
||||
if (dfs != NULL) {
|
||||
/*DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"%s: ic_dfs was not NULL\n",
|
||||
__func__);
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
dfs =
|
||||
(struct ath_dfs *)os_malloc(NULL, sizeof(struct ath_dfs),
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (dfs == NULL) {
|
||||
/*DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"%s: ath_dfs allocation failed\n", __func__); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
OS_MEMZERO(dfs, sizeof(struct ath_dfs));
|
||||
|
||||
ic->ic_dfs = (void *)dfs;
|
||||
|
||||
dfs->ic = ic;
|
||||
|
||||
ic->ic_dfs_debug = dfs_get_debug_info;
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
dfs->dfs_nol = NULL;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Zero out radar_info. It's possible that the attach function won't
|
||||
* fetch an initial regulatory configuration; you really do want to
|
||||
* ensure that the contents indicates there aren't any filters.
|
||||
*/
|
||||
OS_MEMZERO(&radar_info, sizeof(radar_info));
|
||||
ic->ic_dfs_attach(ic, &dfs->dfs_caps, &radar_info);
|
||||
dfs_clear_stats(ic);
|
||||
dfs->dfs_event_log_on = 0;
|
||||
OS_INIT_TIMER(NULL, &(dfs->ath_dfs_task_timer), dfs_task, (void *)(ic),
|
||||
CDF_TIMER_TYPE_SW);
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
OS_INIT_TIMER(NULL, &(dfs->ath_dfstesttimer), dfs_testtimer_task,
|
||||
(void *)ic, CDF_TIMER_TYPE_SW);
|
||||
dfs->ath_dfs_cac_time = ATH_DFS_WAIT_MS;
|
||||
dfs->ath_dfstesttime = ATH_DFS_TEST_RETURN_PERIOD_MS;
|
||||
#endif
|
||||
ATH_DFSQ_LOCK_INIT(dfs);
|
||||
STAILQ_INIT(&dfs->dfs_radarq);
|
||||
ATH_ARQ_LOCK_INIT(dfs);
|
||||
STAILQ_INIT(&dfs->dfs_arq);
|
||||
STAILQ_INIT(&(dfs->dfs_eventq));
|
||||
ATH_DFSEVENTQ_LOCK_INIT(dfs);
|
||||
dfs->events = (struct dfs_event *)os_malloc(NULL,
|
||||
sizeof(struct dfs_event) *
|
||||
DFS_MAX_EVENTS, GFP_ATOMIC);
|
||||
if (dfs->events == NULL) {
|
||||
OS_FREE(dfs);
|
||||
ic->ic_dfs = NULL;
|
||||
DFS_PRINTK("%s: events allocation failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < DFS_MAX_EVENTS; i++) {
|
||||
STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), &dfs->events[i],
|
||||
re_list);
|
||||
}
|
||||
|
||||
dfs->pulses =
|
||||
(struct dfs_pulseline *)os_malloc(NULL,
|
||||
sizeof(struct dfs_pulseline),
|
||||
GFP_ATOMIC);
|
||||
if (dfs->pulses == NULL) {
|
||||
OS_FREE(dfs->events);
|
||||
dfs->events = NULL;
|
||||
OS_FREE(dfs);
|
||||
ic->ic_dfs = NULL;
|
||||
DFS_PRINTK("%s: pulse buffer allocation failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dfs->pulses->pl_lastelem = DFS_MAX_PULSE_BUFFER_MASK;
|
||||
|
||||
/* Allocate memory for radar filters */
|
||||
for (n = 0; n < DFS_MAX_RADAR_TYPES; n++) {
|
||||
dfs->dfs_radarf[n] =
|
||||
(struct dfs_filtertype *)os_malloc(NULL,
|
||||
sizeof(struct
|
||||
dfs_filtertype),
|
||||
GFP_ATOMIC);
|
||||
if (dfs->dfs_radarf[n] == NULL) {
|
||||
DFS_PRINTK
|
||||
("%s: cannot allocate memory for radar filter types\n",
|
||||
__func__);
|
||||
goto bad1;
|
||||
}
|
||||
OS_MEMZERO(dfs->dfs_radarf[n], sizeof(struct dfs_filtertype));
|
||||
}
|
||||
/* Allocate memory for radar table */
|
||||
dfs->dfs_radartable =
|
||||
(int8_t * *) os_malloc(NULL, 256 * sizeof(int8_t *), GFP_ATOMIC);
|
||||
if (dfs->dfs_radartable == NULL) {
|
||||
DFS_PRINTK("%s: cannot allocate memory for radar table\n",
|
||||
__func__);
|
||||
goto bad1;
|
||||
}
|
||||
for (n = 0; n < 256; n++) {
|
||||
dfs->dfs_radartable[n] =
|
||||
os_malloc(NULL, DFS_MAX_RADAR_OVERLAP * sizeof(int8_t),
|
||||
GFP_ATOMIC);
|
||||
if (dfs->dfs_radartable[n] == NULL) {
|
||||
DFS_PRINTK
|
||||
("%s: cannot allocate memory for radar table entry\n",
|
||||
__func__);
|
||||
goto bad2;
|
||||
}
|
||||
}
|
||||
|
||||
if (usenol == 0)
|
||||
DFS_PRINTK("%s: NOL disabled\n", __func__);
|
||||
else if (usenol == 2)
|
||||
DFS_PRINTK("%s: NOL disabled; no CSA\n", __func__);
|
||||
|
||||
dfs->dfs_rinfo.rn_use_nol = usenol;
|
||||
|
||||
/* Init the cached extension channel busy for false alarm reduction */
|
||||
dfs->dfs_rinfo.ext_chan_busy_ts = ic->ic_get_TSF64(ic);
|
||||
dfs->dfs_rinfo.dfs_ext_chan_busy = 0;
|
||||
/* Init the Bin5 chirping related data */
|
||||
dfs->dfs_rinfo.dfs_bin5_chirp_ts = dfs->dfs_rinfo.ext_chan_busy_ts;
|
||||
dfs->dfs_rinfo.dfs_last_bin5_dur = MAX_BIN5_DUR;
|
||||
dfs->dfs_b5radars = NULL;
|
||||
|
||||
/*
|
||||
* If dfs_init_radar_filters() fails, we can abort here and
|
||||
* reconfigure when the first valid channel + radar config
|
||||
* is available.
|
||||
*/
|
||||
if (dfs_init_radar_filters(ic, &radar_info)) {
|
||||
DFS_PRINTK(" %s: Radar Filter Intialization Failed \n",
|
||||
__func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dfs->ath_dfs_false_rssi_thres = RSSI_POSSIBLY_FALSE;
|
||||
dfs->ath_dfs_peak_mag = SEARCH_FFT_REPORT_PEAK_MAG_THRSH;
|
||||
dfs->dfs_phyerr_freq_min = 0x7fffffff;
|
||||
dfs->dfs_phyerr_freq_max = 0;
|
||||
dfs->dfs_phyerr_queued_count = 0;
|
||||
dfs->dfs_phyerr_w53_counter = 0;
|
||||
dfs->dfs_pri_multiplier = 2;
|
||||
|
||||
dfs->ath_dfs_nol_timeout = DFS_NOL_TIMEOUT_S;
|
||||
return 0;
|
||||
|
||||
bad2:
|
||||
OS_FREE(dfs->dfs_radartable);
|
||||
dfs->dfs_radartable = NULL;
|
||||
bad1:
|
||||
for (n = 0; n < DFS_MAX_RADAR_TYPES; n++) {
|
||||
if (dfs->dfs_radarf[n] != NULL) {
|
||||
OS_FREE(dfs->dfs_radarf[n]);
|
||||
dfs->dfs_radarf[n] = NULL;
|
||||
}
|
||||
}
|
||||
if (dfs->pulses) {
|
||||
OS_FREE(dfs->pulses);
|
||||
dfs->pulses = NULL;
|
||||
}
|
||||
if (dfs->events) {
|
||||
OS_FREE(dfs->events);
|
||||
dfs->events = NULL;
|
||||
}
|
||||
|
||||
if (ic->ic_dfs) {
|
||||
OS_FREE(ic->ic_dfs);
|
||||
ic->ic_dfs = NULL;
|
||||
}
|
||||
return 1;
|
||||
#undef N
|
||||
}
|
||||
|
||||
void dfs_detach(struct ieee80211com *ic)
|
||||
{
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
int n, empty;
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, "%s: ic_dfs is NULL\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Bug 29099 make sure all outstanding timers are cancelled */
|
||||
|
||||
if (dfs->ath_radar_tasksched) {
|
||||
OS_CANCEL_TIMER(&dfs->ath_dfs_task_timer);
|
||||
dfs->ath_radar_tasksched = 0;
|
||||
}
|
||||
|
||||
if (dfs->ath_dfstest) {
|
||||
OS_CANCEL_TIMER(&dfs->ath_dfstesttimer);
|
||||
dfs->ath_dfstest = 0;
|
||||
}
|
||||
#if 0
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
if (dfs->ic_dfswait) {
|
||||
OS_CANCEL_TIMER(&dfs->ic_dfswaittimer);
|
||||
dfs->ath_dfswait = 0;
|
||||
}
|
||||
|
||||
OS_CANCEL_TIMER(&dfs->sc_dfs_war_timer);
|
||||
if (dfs->dfs_nol != NULL) {
|
||||
struct dfs_nolelem *nol, *next;
|
||||
nol = dfs->dfs_nol;
|
||||
/* Bug 29099 - each NOL element has its own timer, cancel it and
|
||||
free the element */
|
||||
while (nol != NULL) {
|
||||
OS_CANCEL_TIMER(&nol->nol_timer);
|
||||
next = nol->nol_next;
|
||||
OS_FREE(nol);
|
||||
nol = next;
|
||||
}
|
||||
dfs->dfs_nol = NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
/* Return radar events to free q */
|
||||
dfs_reset_radarq(dfs);
|
||||
dfs_reset_alldelaylines(dfs);
|
||||
|
||||
/* Free up pulse log */
|
||||
if (dfs->pulses != NULL) {
|
||||
OS_FREE(dfs->pulses);
|
||||
dfs->pulses = NULL;
|
||||
}
|
||||
|
||||
for (n = 0; n < DFS_MAX_RADAR_TYPES; n++) {
|
||||
if (dfs->dfs_radarf[n] != NULL) {
|
||||
OS_FREE(dfs->dfs_radarf[n]);
|
||||
dfs->dfs_radarf[n] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dfs->dfs_radartable != NULL) {
|
||||
for (n = 0; n < 256; n++) {
|
||||
if (dfs->dfs_radartable[n] != NULL) {
|
||||
OS_FREE(dfs->dfs_radartable[n]);
|
||||
dfs->dfs_radartable[n] = NULL;
|
||||
}
|
||||
}
|
||||
OS_FREE(dfs->dfs_radartable);
|
||||
dfs->dfs_radartable = NULL;
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
dfs->ath_dfs_isdfsregdomain = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (dfs->dfs_b5radars != NULL) {
|
||||
OS_FREE(dfs->dfs_b5radars);
|
||||
dfs->dfs_b5radars = NULL;
|
||||
}
|
||||
|
||||
/* Commenting out since all the ar functions are obsolete and
|
||||
* the function definition has been removed as part of dfs_ar.c
|
||||
* dfs_reset_ar(dfs);
|
||||
*/
|
||||
ATH_ARQ_LOCK(dfs);
|
||||
empty = STAILQ_EMPTY(&(dfs->dfs_arq));
|
||||
ATH_ARQ_UNLOCK(dfs);
|
||||
if (!empty) {
|
||||
/*
|
||||
* Commenting out since all the ar functions are obsolete and
|
||||
* the function definition has been removed as part of dfs_ar.c
|
||||
*
|
||||
* dfs_reset_arq(dfs);
|
||||
*/
|
||||
}
|
||||
if (dfs->events != NULL) {
|
||||
OS_FREE(dfs->events);
|
||||
dfs->events = NULL;
|
||||
}
|
||||
dfs_nol_timer_cleanup(dfs);
|
||||
OS_FREE(dfs);
|
||||
|
||||
/* XXX? */
|
||||
ic->ic_dfs = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called each time a channel change occurs, to (potentially) enable
|
||||
* the radar code.
|
||||
*/
|
||||
int dfs_radar_disable(struct ieee80211com *ic)
|
||||
{
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
#ifdef ATH_ENABLE_AR
|
||||
dfs->dfs_proc_phyerr &= ~DFS_AR_EN;
|
||||
#endif
|
||||
dfs->dfs_proc_phyerr &= ~DFS_RADAR_EN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called each time a channel change occurs, to (potentially) enable
|
||||
* the radar code.
|
||||
*/
|
||||
int dfs_radar_enable(struct ieee80211com *ic,
|
||||
struct ath_dfs_radar_tab_info *radar_info)
|
||||
{
|
||||
int is_ext_ch;
|
||||
int is_fastclk = 0;
|
||||
int radar_filters_init_status = 0;
|
||||
/* uint32_t rfilt; */
|
||||
struct ath_dfs *dfs;
|
||||
struct dfs_state *rs_pri, *rs_ext;
|
||||
struct ieee80211_channel *chan = ic->ic_curchan, *ext_ch = NULL;
|
||||
is_ext_ch = IEEE80211_IS_CHAN_11N_HT40(ic->ic_curchan);
|
||||
dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
rs_pri = NULL;
|
||||
rs_ext = NULL;
|
||||
#if 0
|
||||
int i;
|
||||
#endif
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "%s: ic_dfs is NULL\n",
|
||||
__func__);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
ic->ic_dfs_disable(ic);
|
||||
|
||||
/*
|
||||
* Setting country code might change the DFS domain
|
||||
* so initialize the DFS Radar filters
|
||||
*/
|
||||
radar_filters_init_status = dfs_init_radar_filters(ic, radar_info);
|
||||
|
||||
/*
|
||||
* dfs_init_radar_filters() returns 1 on failure and
|
||||
* 0 on success.
|
||||
*/
|
||||
if (DFS_STATUS_FAIL == radar_filters_init_status) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s[%d]: DFS Radar Filters Initialization Failed",
|
||||
__func__, __LINE__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if ((ic->ic_opmode == IEEE80211_M_HOSTAP
|
||||
|| ic->ic_opmode == IEEE80211_M_IBSS)) {
|
||||
|
||||
if (IEEE80211_IS_CHAN_DFS(chan)) {
|
||||
|
||||
uint8_t index_pri, index_ext;
|
||||
#ifdef ATH_ENABLE_AR
|
||||
dfs->dfs_proc_phyerr |= DFS_AR_EN;
|
||||
#endif
|
||||
dfs->dfs_proc_phyerr |= DFS_RADAR_EN;
|
||||
|
||||
if (is_ext_ch) {
|
||||
ext_ch = ieee80211_get_extchan(ic);
|
||||
}
|
||||
dfs_reset_alldelaylines(dfs);
|
||||
|
||||
rs_pri = dfs_getchanstate(dfs, &index_pri, 0);
|
||||
if (ext_ch) {
|
||||
rs_ext = dfs_getchanstate(dfs, &index_ext, 1);
|
||||
}
|
||||
if (rs_pri != NULL
|
||||
&& ((ext_ch == NULL) || (rs_ext != NULL))) {
|
||||
struct ath_dfs_phyerr_param pe;
|
||||
|
||||
OS_MEMSET(&pe, '\0', sizeof(pe));
|
||||
|
||||
if (index_pri != dfs->dfs_curchan_radindex)
|
||||
dfs_reset_alldelaylines(dfs);
|
||||
|
||||
dfs->dfs_curchan_radindex = (int16_t) index_pri;
|
||||
dfs->dfs_pri_multiplier_ini =
|
||||
radar_info->dfs_pri_multiplier;
|
||||
|
||||
if (rs_ext)
|
||||
dfs->dfs_extchan_radindex =
|
||||
(int16_t) index_ext;
|
||||
|
||||
ath_dfs_phyerr_param_copy(&pe,
|
||||
&rs_pri->rs_param);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS3,
|
||||
"%s: firpwr=%d, rssi=%d, height=%d, "
|
||||
"prssi=%d, inband=%d, relpwr=%d, "
|
||||
"relstep=%d, maxlen=%d\n",
|
||||
__func__,
|
||||
pe.pe_firpwr,
|
||||
pe.pe_rrssi,
|
||||
pe.pe_height,
|
||||
pe.pe_prssi,
|
||||
pe.pe_inband,
|
||||
pe.pe_relpwr,
|
||||
pe.pe_relstep, pe.pe_maxlen);
|
||||
|
||||
ic->ic_dfs_enable(ic, &is_fastclk, &pe);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"Enabled radar detection on channel %d\n",
|
||||
chan->ic_freq);
|
||||
dfs->dur_multiplier =
|
||||
is_fastclk ? DFS_FAST_CLOCK_MULTIPLIER :
|
||||
DFS_NO_FAST_CLOCK_MULTIPLIER;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS3,
|
||||
"%s: duration multiplier is %d\n",
|
||||
__func__, dfs->dur_multiplier);
|
||||
} else
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"%s: No more radar states left\n",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
|
||||
return DFS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
dfs_control(struct ieee80211com *ic, u_int id,
|
||||
void *indata, uint32_t insize, void *outdata, uint32_t *outsize)
|
||||
{
|
||||
int error = 0;
|
||||
struct ath_dfs_phyerr_param peout;
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
struct dfs_ioctl_params *dfsparams;
|
||||
uint32_t val = 0;
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
struct dfsreq_nolinfo *nol;
|
||||
uint32_t *data = NULL;
|
||||
#endif /* ATH_DFS_RADAR_DETECTION_ONLY */
|
||||
int i;
|
||||
|
||||
if (dfs == NULL) {
|
||||
error = -EINVAL;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, "%s DFS is null\n", __func__);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case DFS_SET_THRESH:
|
||||
if (insize < sizeof(struct dfs_ioctl_params) || !indata) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"%s: insize=%d, expected=%zu bytes, indata=%p\n",
|
||||
__func__, insize,
|
||||
sizeof(struct dfs_ioctl_params), indata);
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dfsparams = (struct dfs_ioctl_params *)indata;
|
||||
if (!dfs_set_thresholds
|
||||
(ic, DFS_PARAM_FIRPWR, dfsparams->dfs_firpwr))
|
||||
error = -EINVAL;
|
||||
if (!dfs_set_thresholds
|
||||
(ic, DFS_PARAM_RRSSI, dfsparams->dfs_rrssi))
|
||||
error = -EINVAL;
|
||||
if (!dfs_set_thresholds
|
||||
(ic, DFS_PARAM_HEIGHT, dfsparams->dfs_height))
|
||||
error = -EINVAL;
|
||||
if (!dfs_set_thresholds
|
||||
(ic, DFS_PARAM_PRSSI, dfsparams->dfs_prssi))
|
||||
error = -EINVAL;
|
||||
if (!dfs_set_thresholds
|
||||
(ic, DFS_PARAM_INBAND, dfsparams->dfs_inband))
|
||||
error = -EINVAL;
|
||||
/* 5413 speicfic */
|
||||
if (!dfs_set_thresholds
|
||||
(ic, DFS_PARAM_RELPWR, dfsparams->dfs_relpwr))
|
||||
error = -EINVAL;
|
||||
if (!dfs_set_thresholds
|
||||
(ic, DFS_PARAM_RELSTEP, dfsparams->dfs_relstep))
|
||||
error = -EINVAL;
|
||||
if (!dfs_set_thresholds
|
||||
(ic, DFS_PARAM_MAXLEN, dfsparams->dfs_maxlen))
|
||||
error = -EINVAL;
|
||||
break;
|
||||
case DFS_GET_THRESH:
|
||||
if (!outdata || !outsize
|
||||
|| *outsize < sizeof(struct dfs_ioctl_params)) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
*outsize = sizeof(struct dfs_ioctl_params);
|
||||
dfsparams = (struct dfs_ioctl_params *)outdata;
|
||||
|
||||
/*
|
||||
* Fetch the DFS thresholds using the internal representation.
|
||||
*/
|
||||
(void)dfs_get_thresholds(ic, &peout);
|
||||
|
||||
/*
|
||||
* Convert them to the dfs IOCTL representation.
|
||||
*/
|
||||
ath_dfs_dfsparam_to_ioctlparam(&peout, dfsparams);
|
||||
break;
|
||||
case DFS_RADARDETECTS:
|
||||
if (!outdata || !outsize || *outsize < sizeof(uint32_t)) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
*outsize = sizeof(uint32_t);
|
||||
*((uint32_t *) outdata) = dfs->ath_dfs_stats.num_radar_detects;
|
||||
break;
|
||||
case DFS_DISABLE_DETECT:
|
||||
dfs->dfs_proc_phyerr &= ~DFS_RADAR_EN;
|
||||
dfs->ic->ic_dfs_state.ignore_dfs = 1;
|
||||
DFS_PRINTK("%s enable detects, ignore_dfs %d\n",
|
||||
__func__, dfs->ic->ic_dfs_state.ignore_dfs);
|
||||
break;
|
||||
case DFS_ENABLE_DETECT:
|
||||
dfs->dfs_proc_phyerr |= DFS_RADAR_EN;
|
||||
dfs->ic->ic_dfs_state.ignore_dfs = 0;
|
||||
DFS_PRINTK("%s enable detects, ignore_dfs %d\n",
|
||||
__func__, dfs->ic->ic_dfs_state.ignore_dfs);
|
||||
break;
|
||||
case DFS_DISABLE_FFT:
|
||||
/* UMACDFS: TODO: val = ath_hal_dfs_config_fft(sc->sc_ah, false); */
|
||||
DFS_PRINTK("%s TODO disable FFT val=0x%x \n", __func__, val);
|
||||
break;
|
||||
case DFS_ENABLE_FFT:
|
||||
/* UMACDFS TODO: val = ath_hal_dfs_config_fft(sc->sc_ah, true); */
|
||||
DFS_PRINTK("%s TODO enable FFT val=0x%x \n", __func__, val);
|
||||
break;
|
||||
case DFS_SET_DEBUG_LEVEL:
|
||||
if (insize < sizeof(uint32_t) || !indata) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dfs->dfs_debug_mask = *(uint32_t *) indata;
|
||||
DFS_PRINTK("%s debug level now = 0x%x \n",
|
||||
__func__, dfs->dfs_debug_mask);
|
||||
if (dfs->dfs_debug_mask & ATH_DEBUG_DFS3) {
|
||||
/* Enable debug Radar Event */
|
||||
dfs->dfs_event_log_on = 1;
|
||||
} else {
|
||||
dfs->dfs_event_log_on = 0;
|
||||
}
|
||||
break;
|
||||
case DFS_SET_FALSE_RSSI_THRES:
|
||||
if (insize < sizeof(uint32_t) || !indata) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dfs->ath_dfs_false_rssi_thres = *(uint32_t *) indata;
|
||||
DFS_PRINTK("%s false RSSI threshold now = 0x%x \n",
|
||||
__func__, dfs->ath_dfs_false_rssi_thres);
|
||||
break;
|
||||
case DFS_SET_PEAK_MAG:
|
||||
if (insize < sizeof(uint32_t) || !indata) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dfs->ath_dfs_peak_mag = *(uint32_t *) indata;
|
||||
DFS_PRINTK("%s peak_mag now = 0x%x \n",
|
||||
__func__, dfs->ath_dfs_peak_mag);
|
||||
break;
|
||||
case DFS_IGNORE_CAC:
|
||||
if (insize < sizeof(uint32_t) || !indata) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (*(uint32_t *) indata) {
|
||||
dfs->ic->ic_dfs_state.ignore_cac = 1;
|
||||
} else {
|
||||
dfs->ic->ic_dfs_state.ignore_cac = 0;
|
||||
}
|
||||
DFS_PRINTK("%s ignore cac = 0x%x \n",
|
||||
__func__, dfs->ic->ic_dfs_state.ignore_cac);
|
||||
break;
|
||||
case DFS_SET_NOL_TIMEOUT:
|
||||
if (insize < sizeof(uint32_t) || !indata) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (*(int *)indata) {
|
||||
dfs->ath_dfs_nol_timeout = *(int *)indata;
|
||||
} else {
|
||||
dfs->ath_dfs_nol_timeout = DFS_NOL_TIMEOUT_S;
|
||||
}
|
||||
DFS_PRINTK("%s nol timeout = %d sec \n",
|
||||
__func__, dfs->ath_dfs_nol_timeout);
|
||||
break;
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
case DFS_MUTE_TIME:
|
||||
if (insize < sizeof(uint32_t) || !indata) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
data = (uint32_t *) indata;
|
||||
dfs->ath_dfstesttime = *data;
|
||||
dfs->ath_dfstesttime *= (1000); /* convert sec into ms */
|
||||
break;
|
||||
case DFS_GET_USENOL:
|
||||
if (!outdata || !outsize || *outsize < sizeof(uint32_t)) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
*outsize = sizeof(uint32_t);
|
||||
*((uint32_t *) outdata) = dfs->dfs_rinfo.rn_use_nol;
|
||||
|
||||
for (i = 0;
|
||||
(i < DFS_EVENT_LOG_SIZE) && (i < dfs->dfs_event_log_count);
|
||||
i++) {
|
||||
/* DFS_DPRINTK(sc, ATH_DEBUG_DFS,"ts=%llu diff_ts=%u rssi=%u dur=%u\n", dfs->radar_log[i].ts, dfs->radar_log[i].diff_ts, dfs->radar_log[i].rssi, dfs->radar_log[i].dur); */
|
||||
|
||||
}
|
||||
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;
|
||||
break;
|
||||
case DFS_SET_USENOL:
|
||||
if (insize < sizeof(uint32_t) || !indata) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dfs->dfs_rinfo.rn_use_nol = *(uint32_t *) indata;
|
||||
/* iwpriv markdfs in linux can do the same thing... */
|
||||
break;
|
||||
case DFS_GET_NOL:
|
||||
if (!outdata || !outsize
|
||||
|| *outsize < sizeof(struct dfsreq_nolinfo)) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
*outsize = sizeof(struct dfsreq_nolinfo);
|
||||
nol = (struct dfsreq_nolinfo *)outdata;
|
||||
dfs_get_nol(dfs, (struct dfsreq_nolelem *)nol->dfs_nol,
|
||||
&nol->ic_nchans);
|
||||
dfs_print_nol(dfs);
|
||||
break;
|
||||
case DFS_SET_NOL:
|
||||
if (insize < sizeof(struct dfsreq_nolinfo) || !indata) {
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
nol = (struct dfsreq_nolinfo *)indata;
|
||||
dfs_set_nol(dfs, (struct dfsreq_nolelem *)nol->dfs_nol,
|
||||
nol->ic_nchans);
|
||||
break;
|
||||
|
||||
case DFS_SHOW_NOL:
|
||||
dfs_print_nol(dfs);
|
||||
break;
|
||||
case DFS_BANGRADAR:
|
||||
#if 0 /* MERGE_TBD */
|
||||
if (sc->sc_nostabeacons) {
|
||||
printk("No radar detection Enabled \n");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
dfs->dfs_bangradar = 1;
|
||||
dfs->ath_radar_tasksched = 1;
|
||||
OS_SET_TIMER(&dfs->ath_dfs_task_timer, 0);
|
||||
break;
|
||||
#endif /* ATH_DFS_RADAR_DETECTION_ONLY */
|
||||
default:
|
||||
error = -EINVAL;
|
||||
}
|
||||
bad:
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
dfs_set_thresholds(struct ieee80211com *ic, const uint32_t threshtype,
|
||||
const uint32_t value)
|
||||
{
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
int16_t chanindex;
|
||||
struct dfs_state *rs;
|
||||
struct ath_dfs_phyerr_param pe;
|
||||
int is_fastclk = 0; /* XXX throw-away */
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1, "%s: ic_dfs is NULL\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
chanindex = dfs->dfs_curchan_radindex;
|
||||
if ((chanindex < 0) || (chanindex >= DFS_NUM_RADAR_STATES)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"%s: chanindex = %d, DFS_NUM_RADAR_STATES=%d\n",
|
||||
__func__, chanindex, DFS_NUM_RADAR_STATES);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"%s: threshtype=%d, value=%d\n", __func__, threshtype,
|
||||
value);
|
||||
|
||||
ath_dfs_phyerr_init_noval(&pe);
|
||||
|
||||
rs = &(dfs->dfs_radar[chanindex]);
|
||||
switch (threshtype) {
|
||||
case DFS_PARAM_FIRPWR:
|
||||
rs->rs_param.pe_firpwr = (int32_t) value;
|
||||
pe.pe_firpwr = value;
|
||||
break;
|
||||
case DFS_PARAM_RRSSI:
|
||||
rs->rs_param.pe_rrssi = value;
|
||||
pe.pe_rrssi = value;
|
||||
break;
|
||||
case DFS_PARAM_HEIGHT:
|
||||
rs->rs_param.pe_height = value;
|
||||
pe.pe_height = value;
|
||||
break;
|
||||
case DFS_PARAM_PRSSI:
|
||||
rs->rs_param.pe_prssi = value;
|
||||
pe.pe_prssi = value;
|
||||
break;
|
||||
case DFS_PARAM_INBAND:
|
||||
rs->rs_param.pe_inband = value;
|
||||
pe.pe_inband = value;
|
||||
break;
|
||||
/* 5413 specific */
|
||||
case DFS_PARAM_RELPWR:
|
||||
rs->rs_param.pe_relpwr = value;
|
||||
pe.pe_relpwr = value;
|
||||
break;
|
||||
case DFS_PARAM_RELSTEP:
|
||||
rs->rs_param.pe_relstep = value;
|
||||
pe.pe_relstep = value;
|
||||
break;
|
||||
case DFS_PARAM_MAXLEN:
|
||||
rs->rs_param.pe_maxlen = value;
|
||||
pe.pe_maxlen = value;
|
||||
break;
|
||||
default:
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"%s: unknown threshtype (%d)\n",
|
||||
__func__, threshtype);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The driver layer dfs_enable routine is tasked with translating
|
||||
* values from the global format to the per-device (HAL, offload)
|
||||
* format.
|
||||
*/
|
||||
ic->ic_dfs_enable(ic, &is_fastclk, &pe);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
dfs_get_thresholds(struct ieee80211com *ic, struct ath_dfs_phyerr_param *param)
|
||||
{
|
||||
/* UMACDFS : TODO:ath_hal_getdfsthresh(sc->sc_ah, param); */
|
||||
|
||||
OS_MEMZERO(param, sizeof(*param));
|
||||
|
||||
(void)ic->ic_dfs_get_thresholds(ic, param);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t dfs_usenol(struct ieee80211com *ic)
|
||||
{
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
return dfs ? (uint16_t) dfs->dfs_rinfo.rn_use_nol : 0;
|
||||
}
|
||||
|
||||
uint16_t dfs_isdfsregdomain(struct ieee80211com *ic)
|
||||
{
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
return dfs ? dfs->dfsdomain : 0;
|
||||
}
|
||||
|
||||
#endif /* ATH_UPPORT_DFS */
|
485
core/sap/dfs/src/dfs_bindetects.c
Normal file
485
core/sap/dfs/src/dfs_bindetects.c
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* 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 */
|
160
core/sap/dfs/src/dfs_debug.c
Normal file
160
core/sap/dfs/src/dfs_debug.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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_debug.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
|
||||
|
||||
void dfs_print_delayline(struct ath_dfs *dfs, struct dfs_delayline *dl)
|
||||
{
|
||||
int i = 0, index;
|
||||
struct dfs_delayelem *de;
|
||||
|
||||
index = dl->dl_lastelem;
|
||||
for (i = 0; i < dl->dl_numelems; i++) {
|
||||
de = &dl->dl_elems[index];
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"Elem %d: ts = %u (0x%x) dur=%u\n", i,
|
||||
de->de_time, de->de_time, de->de_dur);
|
||||
index = (index - 1) & DFS_MAX_DL_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
void dfs_print_filter(struct ath_dfs *dfs, struct dfs_filter *rf)
|
||||
{
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"filterID[%d] rf_numpulses=%u; rf->rf_minpri=%u; "
|
||||
"rf->rf_maxpri=%u; rf->rf_threshold=%u; rf->rf_filterlen=%u; "
|
||||
"rf->rf_mindur=%u; rf->rf_maxdur=%u\n",
|
||||
rf->rf_pulseid,
|
||||
rf->rf_numpulses,
|
||||
rf->rf_minpri,
|
||||
rf->rf_maxpri,
|
||||
rf->rf_threshold,
|
||||
rf->rf_filterlen, rf->rf_mindur, rf->rf_maxdur);
|
||||
}
|
||||
|
||||
void dfs_print_filters(struct ath_dfs *dfs)
|
||||
{
|
||||
struct dfs_filtertype *ft = NULL;
|
||||
struct dfs_filter *rf;
|
||||
int i, j;
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "%s: sc_dfs is NULL\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < DFS_MAX_RADAR_TYPES; i++) {
|
||||
if (dfs->dfs_radarf[i] != NULL) {
|
||||
ft = dfs->dfs_radarf[i];
|
||||
if ((ft->ft_numfilters > DFS_MAX_NUM_RADAR_FILTERS)
|
||||
|| (!ft->ft_numfilters))
|
||||
continue;
|
||||
DFS_PRINTK
|
||||
("===========ft->ft_numfilters=%u===========\n",
|
||||
ft->ft_numfilters);
|
||||
for (j = 0; j < ft->ft_numfilters; j++) {
|
||||
rf = &(ft->ft_filters[j]);
|
||||
DFS_PRINTK
|
||||
("filter[%d] filterID = %d rf_numpulses=%u; rf->rf_minpri=%u; rf->rf_maxpri=%u; rf->rf_threshold=%u; rf->rf_filterlen=%u; rf->rf_mindur=%u; rf->rf_maxdur=%u\n",
|
||||
j, rf->rf_pulseid, rf->rf_numpulses,
|
||||
rf->rf_minpri, rf->rf_maxpri,
|
||||
rf->rf_threshold, rf->rf_filterlen,
|
||||
rf->rf_mindur, rf->rf_maxdur);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dfs_print_activity(struct ath_dfs *dfs)
|
||||
{
|
||||
int chan_busy = 0, ext_chan_busy = 0;
|
||||
uint32_t rxclear = 0, rxframe = 0, txframe = 0, cycles = 0;
|
||||
|
||||
cycles =
|
||||
dfs->ic->ic_get_mib_cycle_counts_pct(dfs->ic, &rxclear, &rxframe,
|
||||
&txframe);
|
||||
chan_busy = cycles;
|
||||
|
||||
ext_chan_busy = dfs->ic->ic_get_ext_busy(dfs->ic);
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "cycles=%d rxclear=%d rxframe=%d"
|
||||
" txframe=%d extchanbusy=%d\n", cycles, rxclear,
|
||||
rxframe, txframe, ext_chan_busy);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX migrate this to use ath_dfs as the arg, not ieee80211com!
|
||||
*/
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
os_timer_func(dfs_debug_timeout)
|
||||
{
|
||||
struct ieee80211com *ic;
|
||||
struct ath_dfs *dfs;
|
||||
|
||||
OS_GET_TIMER_ARG(ic, struct ieee80211com *);
|
||||
|
||||
dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
|
||||
dfs_print_activity(dfs);
|
||||
|
||||
OS_SET_TIMER(&dfs->ath_dfs_debug_timer, DFS_DEBUG_TIMEOUT_MS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ATH_SUPPORT_DFS */
|
874
core/sap/dfs/src/dfs_fcc_bin5.c
Normal file
874
core/sap/dfs/src/dfs_fcc_bin5.c
Normal file
@@ -0,0 +1,874 @@
|
||||
/*
|
||||
* 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_fcc_bin5.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
|
||||
|
||||
/*
|
||||
* Reject the pulse if:
|
||||
* + It's outside the RSSI threshold;
|
||||
* + It's outside the pulse duration;
|
||||
* + It's been verified by HW/SW chirp checking
|
||||
* and neither of those found a chirp.
|
||||
*/
|
||||
int
|
||||
dfs_bin5_check_pulse(struct ath_dfs *dfs, struct dfs_event *re,
|
||||
struct dfs_bin5radars *br)
|
||||
{
|
||||
int b5_rssithresh = br->br_pulse.b5_rssithresh;
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_PULSE,
|
||||
"%s: re_dur=%d, rssi=%d, check_chirp=%d, "
|
||||
"hw_chirp=%d, sw_chirp=%d\n",
|
||||
__func__,
|
||||
(int)re->re_dur,
|
||||
(int)re->re_rssi,
|
||||
!!(re->re_flags & DFS_EVENT_CHECKCHIRP),
|
||||
!!(re->re_flags & DFS_EVENT_HW_CHIRP),
|
||||
!!(re->re_flags & DFS_EVENT_SW_CHIRP));
|
||||
|
||||
/*
|
||||
* If the sw/hw chirp detection says to fail the pulse,
|
||||
* do so.
|
||||
*/
|
||||
if (DFS_EVENT_NOTCHIRP(re)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
"%s: rejecting chirp: ts=%llu, dur=%d, rssi=%d "
|
||||
"checkchirp=%d, hwchirp=%d, swchirp=%d\n",
|
||||
__func__,
|
||||
(unsigned long long)re->re_full_ts,
|
||||
(int)re->re_dur,
|
||||
(int)re->re_rssi,
|
||||
!!(re->re_flags & DFS_EVENT_CHECKCHIRP),
|
||||
!!(re->re_flags & DFS_EVENT_HW_CHIRP),
|
||||
!!(re->re_flags & DFS_EVENT_SW_CHIRP));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Adjust the filter threshold for rssi in non TURBO mode */
|
||||
if (!(dfs->ic->ic_curchan->ic_flags & CHANNEL_TURBO))
|
||||
b5_rssithresh += br->br_pulse.b5_rssimargin;
|
||||
|
||||
/*
|
||||
* Check if the pulse is within duration and rssi
|
||||
* thresholds.
|
||||
*/
|
||||
if ((re->re_dur >= br->br_pulse.b5_mindur) &&
|
||||
(re->re_dur <= br->br_pulse.b5_maxdur) &&
|
||||
(re->re_rssi >= b5_rssithresh)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
"%s: dur=%d, rssi=%d - adding!\n",
|
||||
__func__, (int)re->re_dur, (int)re->re_rssi);
|
||||
return (1);
|
||||
}
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
"%s: too low to be Bin5 pulse tsf=%llu, dur=%d, rssi=%d\n",
|
||||
__func__,
|
||||
(unsigned long long)re->re_full_ts,
|
||||
(int)re->re_dur, (int)re->re_rssi);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int dfs_bin5_addpulse(struct ath_dfs *dfs, struct dfs_bin5radars *br,
|
||||
struct dfs_event *re, uint64_t thists)
|
||||
{
|
||||
uint32_t index, stop;
|
||||
uint64_t tsDelta;
|
||||
|
||||
/* Check if this pulse is a valid pulse in terms of repetition,
|
||||
* if not, return without adding it to the queue.
|
||||
* PRI : Pulse Repitetion Interval
|
||||
* BRI : Burst Repitetion Interval */
|
||||
if (br->br_numelems != 0) {
|
||||
index = br->br_lastelem;
|
||||
tsDelta = thists - br->br_elems[index].be_ts;
|
||||
if ((tsDelta < DFS_BIN5_PRI_LOWER_LIMIT) ||
|
||||
((tsDelta > DFS_BIN5_PRI_HIGHER_LIMIT) &&
|
||||
(tsDelta < DFS_BIN5_BRI_LOWER_LIMIT))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Circular buffer of size 2^n */
|
||||
index = (br->br_lastelem + 1) & DFS_MAX_B5_MASK;
|
||||
br->br_lastelem = index;
|
||||
if (br->br_numelems == DFS_MAX_B5_SIZE)
|
||||
br->br_firstelem = (br->br_firstelem + 1) & DFS_MAX_B5_MASK;
|
||||
else
|
||||
br->br_numelems++;
|
||||
br->br_elems[index].be_ts = thists;
|
||||
br->br_elems[index].be_rssi = re->re_rssi;
|
||||
br->br_elems[index].be_dur = re->re_dur; /* please note that this is in u-sec */
|
||||
stop = 0;
|
||||
index = br->br_firstelem;
|
||||
while ((!stop) && (br->br_numelems - 1) > 0) {
|
||||
if ((thists - br->br_elems[index].be_ts) >
|
||||
((uint64_t) br->br_pulse.b5_timewindow)) {
|
||||
br->br_numelems--;
|
||||
br->br_firstelem =
|
||||
(br->br_firstelem + 1) & DFS_MAX_B5_MASK;
|
||||
index = br->br_firstelem;
|
||||
} else
|
||||
stop = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the dfs structure is NULL (which should be illegal if everyting is working
|
||||
* properly, then signify that a bin5 radar was found
|
||||
*/
|
||||
|
||||
int dfs_bin5_check(struct ath_dfs *dfs)
|
||||
{
|
||||
struct dfs_bin5radars *br;
|
||||
int index[DFS_MAX_B5_SIZE];
|
||||
uint32_t n = 0, i = 0, i1 = 0, this = 0, prev = 0, rssi_diff =
|
||||
0, width_diff = 0, bursts = 0;
|
||||
uint32_t total_diff = 0, average_diff = 0, total_width =
|
||||
0, average_width = 0, numevents = 0;
|
||||
uint64_t pri;
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_PRINTK("%s: ic_dfs is NULL\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
for (n = 0; n < dfs->dfs_rinfo.rn_numbin5radars; n++) {
|
||||
br = &(dfs->dfs_b5radars[n]);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
"Num elems = %d\n", br->br_numelems);
|
||||
|
||||
/* find a valid bin 5 pulse and use it as reference */
|
||||
for (i1 = 0; i1 < br->br_numelems; i1++) {
|
||||
this = ((br->br_firstelem + i1) & DFS_MAX_B5_MASK);
|
||||
if ((br->br_elems[this].be_dur >= MIN_BIN5_DUR_MICROSEC)
|
||||
&& (br->br_elems[this].be_dur <=
|
||||
MAX_BIN5_DUR_MICROSEC)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
prev = this;
|
||||
for (i = i1 + 1; i < br->br_numelems; i++) {
|
||||
this = ((br->br_firstelem + i) & DFS_MAX_B5_MASK);
|
||||
|
||||
/* first make sure it is a bin 5 pulse by checking the duration */
|
||||
if ((br->br_elems[this].be_dur < MIN_BIN5_DUR_MICROSEC)
|
||||
|| (br->br_elems[this].be_dur >
|
||||
MAX_BIN5_DUR_MICROSEC)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Rule 1: 1000 <= PRI <= 2000 + some margin */
|
||||
if (br->br_elems[this].be_ts >=
|
||||
br->br_elems[prev].be_ts) {
|
||||
pri =
|
||||
br->br_elems[this].be_ts -
|
||||
br->br_elems[prev].be_ts;
|
||||
} else { /* roll over case */
|
||||
/* pri = (0xffffffffffffffff - br->br_elems[prev].be_ts) + br->br_elems[this].be_ts; */
|
||||
pri = br->br_elems[this].be_ts;
|
||||
}
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
" pri=%llu this.ts=%llu this.dur=%d this.rssi=%d prev.ts=%llu\n",
|
||||
(unsigned long long)pri,
|
||||
(unsigned long long)br->br_elems[this].
|
||||
be_ts, (int)br->br_elems[this].be_dur,
|
||||
(int)br->br_elems[this].be_rssi,
|
||||
(unsigned long long)br->br_elems[prev].
|
||||
be_ts);
|
||||
if (((pri >= DFS_BIN5_PRI_LOWER_LIMIT) && (pri <= DFS_BIN5_PRI_HIGHER_LIMIT))) { /* pri: pulse repitition interval in us */
|
||||
/* Rule 2: pulse width of the pulses in the burst should be same (+/- margin) */
|
||||
if (br->br_elems[this].be_dur >=
|
||||
br->br_elems[prev].be_dur) {
|
||||
width_diff =
|
||||
br->br_elems[this].be_dur -
|
||||
br->br_elems[prev].be_dur;
|
||||
} else {
|
||||
width_diff =
|
||||
br->br_elems[prev].be_dur -
|
||||
br->br_elems[this].be_dur;
|
||||
}
|
||||
if (width_diff <= DFS_BIN5_WIDTH_MARGIN) {
|
||||
/* Rule 3: RSSI of the pulses in the burst should be same (+/- margin) */
|
||||
if (br->br_elems[this].be_rssi >=
|
||||
br->br_elems[prev].be_rssi) {
|
||||
rssi_diff =
|
||||
br->br_elems[this].be_rssi -
|
||||
br->br_elems[prev].be_rssi;
|
||||
} else {
|
||||
rssi_diff =
|
||||
br->br_elems[prev].be_rssi -
|
||||
br->br_elems[this].be_rssi;
|
||||
}
|
||||
if (rssi_diff <= DFS_BIN5_RSSI_MARGIN) {
|
||||
bursts++;
|
||||
/* Save the indexes of this pair for later width variance check */
|
||||
if (numevents >= 2) {
|
||||
/* make sure the event is not duplicated,
|
||||
* possible in a 3 pulse burst */
|
||||
if (index[numevents - 1]
|
||||
!= prev) {
|
||||
index
|
||||
[numevents++]
|
||||
= prev;
|
||||
}
|
||||
} else {
|
||||
index[numevents++] =
|
||||
prev;
|
||||
}
|
||||
index[numevents++] = this;
|
||||
} else {
|
||||
DFS_DPRINTK(dfs,
|
||||
ATH_DEBUG_DFS_BIN5,
|
||||
"%s %d Bin5 rssi_diff=%d\n",
|
||||
__func__, __LINE__,
|
||||
rssi_diff);
|
||||
}
|
||||
} else {
|
||||
DFS_DPRINTK(dfs,
|
||||
ATH_DEBUG_DFS_BIN5,
|
||||
"%s %d Bin5 width_diff=%d\n",
|
||||
__func__, __LINE__,
|
||||
width_diff);
|
||||
}
|
||||
} else if ((pri >= DFS_BIN5_BRI_LOWER_LIMIT) &&
|
||||
(pri <= DFS_BIN5_BRI_UPPER_LIMIT)) {
|
||||
/* check pulse width to make sure it is in range of bin 5 */
|
||||
/* if ((br->br_elems[this].be_dur >= MIN_BIN5_DUR_MICROSEC) && (br->br_elems[this].be_dur <= MAX_BIN5_DUR_MICROSEC)) { */
|
||||
bursts++;
|
||||
/* } */
|
||||
} else {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
"%s %d Bin5 PRI check fail pri=%llu\n",
|
||||
__func__, __LINE__,
|
||||
(unsigned long long)pri);
|
||||
}
|
||||
prev = this;
|
||||
}
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, "bursts=%u numevents=%u\n",
|
||||
bursts, numevents);
|
||||
if (bursts >= br->br_pulse.b5_threshold) {
|
||||
if ((br->br_elems[br->br_lastelem].be_ts -
|
||||
br->br_elems[br->br_firstelem].be_ts) < 3000000) {
|
||||
return 0;
|
||||
} else {
|
||||
/*
|
||||
* don't do this check since not all the cases have this kind of burst width variation.
|
||||
*
|
||||
for (i=0; i<bursts; i++){
|
||||
total_width += br->br_elems[index[i]].be_dur;
|
||||
}
|
||||
average_width = total_width/bursts;
|
||||
for (i=0; i<bursts; i++){
|
||||
total_diff += DFS_DIFF(br->br_elems[index[i]].be_dur, average_width);
|
||||
}
|
||||
average_diff = total_diff/bursts;
|
||||
if( average_diff > DFS_BIN5_WIDTH_MARGIN ) {
|
||||
return 1;
|
||||
} else {
|
||||
|
||||
DFS_DPRINTK(ic, ATH_DEBUG_DFS_BIN5, "bursts=%u numevents=%u total_width=%d average_width=%d total_diff=%d average_diff=%d\n", bursts, numevents, total_width, average_width, total_diff, average_diff);
|
||||
|
||||
}
|
||||
*/
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
"bursts=%u numevents=%u total_width=%d average_width=%d total_diff=%d average_diff=%d\n",
|
||||
bursts, numevents, total_width,
|
||||
average_width, total_diff,
|
||||
average_diff);
|
||||
DFS_PRINTK("bin 5 radar detected, bursts=%d\n",
|
||||
bursts);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return true if chirping pulse, false if not.
|
||||
Decision is made based on processing the FFT data included with the PHY error.
|
||||
Calculate the slope using the maximum bin index reported in the FFT data.
|
||||
Calculate slope between FFT packet 0 and packet n-1. Also calculate slope between
|
||||
packet 1 and packet n.
|
||||
If a pulse is chirping, a slope of 5 and greater is seen.
|
||||
Non-chirping pulses have slopes of 0, 1, 2 or 3.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Chirp detection for Sowl/Howl.
|
||||
*/
|
||||
static int
|
||||
dfs_check_chirping_sowl(struct ath_dfs *dfs, void *buf,
|
||||
uint16_t datalen, int is_ctl, int is_ext, int *slope,
|
||||
int *is_dc)
|
||||
{
|
||||
#define FFT_LEN 70
|
||||
#define FFT_LOWER_BIN_MAX_INDEX_BYTE 66
|
||||
#define FFT_UPPER_BIN_MAX_INDEX_BYTE 69
|
||||
#define MIN_CHIRPING_SLOPE 4
|
||||
int is_chirp = 0;
|
||||
int p, num_fft_packets = 0;
|
||||
int ctl_slope = 0, ext_slope = 0;
|
||||
int ctl_high0, ctl_low0, ctl_slope0 =
|
||||
0, ext_high0, ext_low0, ext_slope0 = 0;
|
||||
int ctl_high1, ctl_low1, ctl_slope1 =
|
||||
0, ext_high1, ext_low1, ext_slope1 = 0;
|
||||
uint8_t *fft_data_ptr;
|
||||
|
||||
*slope = 0;
|
||||
*is_dc = 0;
|
||||
|
||||
num_fft_packets = datalen / FFT_LEN;
|
||||
fft_data_ptr = ((uint8_t *) buf);
|
||||
|
||||
/* DEBUG - Print relevant portions of the FFT data */
|
||||
for (p = 0; p < num_fft_packets; p++) {
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, "fft_data_ptr=0x%p\t",
|
||||
fft_data_ptr);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, "[66]=%d [69]=%d\n",
|
||||
*(fft_data_ptr + FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2,
|
||||
*(fft_data_ptr +
|
||||
FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2);
|
||||
|
||||
fft_data_ptr += FFT_LEN;
|
||||
}
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"datalen=%d num_fft_packets=%d\n", datalen,
|
||||
num_fft_packets);
|
||||
|
||||
/* There is not enough FFT data to figure out whether the pulse is chirping or not */
|
||||
if (num_fft_packets < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fft_data_ptr = ((uint8_t *) buf);
|
||||
|
||||
if (is_ctl) {
|
||||
|
||||
fft_data_ptr = ((uint8_t *) buf);
|
||||
ctl_low0 = *(fft_data_ptr + FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2;
|
||||
fft_data_ptr += FFT_LEN;
|
||||
ctl_low1 = *(fft_data_ptr + FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2;
|
||||
|
||||
/* last packet with first packet */
|
||||
fft_data_ptr =
|
||||
((uint8_t *) buf) + (FFT_LEN * (num_fft_packets - 1));
|
||||
ctl_high1 = *(fft_data_ptr + FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2;
|
||||
|
||||
/* second last packet with 0th packet */
|
||||
fft_data_ptr =
|
||||
((uint8_t *) buf) + (FFT_LEN * (num_fft_packets - 2));
|
||||
ctl_high0 = *(fft_data_ptr + FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2;
|
||||
|
||||
ctl_slope0 = ctl_high0 - ctl_low0;
|
||||
if (ctl_slope0 < 0)
|
||||
ctl_slope0 *= (-1);
|
||||
|
||||
ctl_slope1 = ctl_high1 - ctl_low1;
|
||||
if (ctl_slope1 < 0)
|
||||
ctl_slope1 *= (-1);
|
||||
|
||||
ctl_slope =
|
||||
((ctl_slope0 > ctl_slope1) ? ctl_slope0 : ctl_slope1);
|
||||
*slope = ctl_slope;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"ctl_slope0=%d ctl_slope1=%d ctl_slope=%d\n",
|
||||
ctl_slope0, ctl_slope1, ctl_slope);
|
||||
|
||||
} else if (is_ext) {
|
||||
|
||||
fft_data_ptr = ((uint8_t *) buf);
|
||||
ext_low0 = *(fft_data_ptr + FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2;
|
||||
|
||||
fft_data_ptr += FFT_LEN;
|
||||
ext_low1 = *(fft_data_ptr + FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2;
|
||||
|
||||
fft_data_ptr =
|
||||
((uint8_t *) buf) + (FFT_LEN * (num_fft_packets - 1));
|
||||
ext_high1 = *(fft_data_ptr + FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2;
|
||||
fft_data_ptr =
|
||||
((uint8_t *) buf) + (FFT_LEN * (num_fft_packets - 2));
|
||||
ext_high0 = *(fft_data_ptr + FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2;
|
||||
|
||||
ext_slope0 = ext_high0 - ext_low0;
|
||||
if (ext_slope0 < 0)
|
||||
ext_slope0 *= (-1);
|
||||
|
||||
ext_slope1 = ext_high1 - ext_low1;
|
||||
if (ext_slope1 < 0)
|
||||
ext_slope1 *= (-1);
|
||||
|
||||
ext_slope =
|
||||
((ext_slope0 > ext_slope1) ? ext_slope0 : ext_slope1);
|
||||
*slope = ext_slope;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT | ATH_DEBUG_DFS_BIN5,
|
||||
"ext_slope0=%d ext_slope1=%d ext_slope=%d\n",
|
||||
ext_slope0, ext_slope1, ext_slope);
|
||||
} else
|
||||
return 0;
|
||||
|
||||
if ((ctl_slope >= MIN_CHIRPING_SLOPE)
|
||||
|| (ext_slope >= MIN_CHIRPING_SLOPE)) {
|
||||
is_chirp = 1;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5 | ATH_DEBUG_DFS_BIN5_FFT |
|
||||
ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"is_chirp=%d is_dc=%d\n", is_chirp, *is_dc);
|
||||
}
|
||||
return is_chirp;
|
||||
|
||||
#undef FFT_LEN
|
||||
#undef FFT_LOWER_BIN_MAX_INDEX_BYTE
|
||||
#undef FFT_UPPER_BIN_MAX_INDEX_BYTE
|
||||
#undef MIN_CHIRPING_SLOPE
|
||||
}
|
||||
|
||||
/*
|
||||
* Merlin (and Osprey, etc) chirp radar chirp detection.
|
||||
*/
|
||||
static int
|
||||
dfs_check_chirping_merlin(struct ath_dfs *dfs, void *buf, uint16_t datalen,
|
||||
int is_ctl, int is_ext, int *slope, int *is_dc)
|
||||
{
|
||||
#define ABS_DIFF(_x, _y) ((int)_x > (int)_y ? (int)_x - (int)_y : (int)_y - (int)_x)
|
||||
#define ABS(_x) ((int)_x > 0 ? (int)_x : -(int)_x)
|
||||
|
||||
#define DELTA_STEP 1 /* This should be between 1 and 3. Default is 1. */
|
||||
#define NUM_DIFFS 3 /* Number of Diffs to compute. valid range is 2-4 */
|
||||
#define MAX_DIFF 2 /* Threshold for difference of delta peaks */
|
||||
#define BIN_COUNT_MAX 6 /* Max. number of strong bins for narrow band */
|
||||
|
||||
/*
|
||||
* Dynamic 20/40 mode FFT packet format related definition
|
||||
*/
|
||||
|
||||
#define NUM_FFT_BYTES_HT40 70
|
||||
#define NUM_BIN_BYTES_HT40 64
|
||||
#define NUM_SUBCHAN_BINS_HT40 64
|
||||
#define LOWER_INDEX_BYTE_HT40 66
|
||||
#define UPPER_INDEX_BYTE_HT40 69
|
||||
#define LOWER_WEIGHT_BYTE_HT40 64
|
||||
#define UPPER_WEIGHT_BYTE_HT40 67
|
||||
#define LOWER_MAG_BYTE_HT40 65
|
||||
#define UPPER_MAG_BYTE_HT40 68
|
||||
|
||||
/*
|
||||
* Static 20 mode FFT packet format related definition
|
||||
*/
|
||||
|
||||
#define NUM_FFT_BYTES_HT20 31
|
||||
#define NUM_BIN_BYTES_HT20 28
|
||||
#define NUM_SUBCHAN_BINS_HT20 56
|
||||
#define LOWER_INDEX_BYTE_HT20 30
|
||||
#define UPPER_INDEX_BYTE_HT20 30
|
||||
#define LOWER_WEIGHT_BYTE_HT20 28
|
||||
#define UPPER_WEIGHT_BYTE_HT20 28
|
||||
#define LOWER_MAG_BYTE_HT20 29
|
||||
#define UPPER_MAG_BYTE_HT20 29
|
||||
|
||||
int num_fft_packets; /* number of FFT packets reported to software */
|
||||
int num_fft_bytes;
|
||||
int num_bin_bytes;
|
||||
int num_subchan_bins;
|
||||
int lower_index_byte;
|
||||
int upper_index_byte;
|
||||
int lower_weight_byte;
|
||||
int upper_weight_byte;
|
||||
int lower_mag_byte;
|
||||
int upper_mag_byte;
|
||||
|
||||
int max_index_lower[DELTA_STEP + NUM_DIFFS];
|
||||
int max_index_upper[DELTA_STEP + NUM_DIFFS];
|
||||
int max_mag_lower[DELTA_STEP + NUM_DIFFS];
|
||||
int max_mag_upper[DELTA_STEP + NUM_DIFFS];
|
||||
int bin_wt_lower[DELTA_STEP + NUM_DIFFS];
|
||||
int bin_wt_upper[DELTA_STEP + NUM_DIFFS];
|
||||
int max_mag_sel[DELTA_STEP + NUM_DIFFS];
|
||||
int max_mag[DELTA_STEP + NUM_DIFFS];
|
||||
int max_index[DELTA_STEP + NUM_DIFFS];
|
||||
|
||||
int max_d[] = { 10, 19, 28 };
|
||||
int min_d[] = { 1, 2, 3 };
|
||||
|
||||
uint8_t *ptr; /* pointer to FFT data */
|
||||
int i;
|
||||
int fft_start;
|
||||
int chirp_found;
|
||||
int delta_peak[NUM_DIFFS];
|
||||
int j;
|
||||
int bin_count;
|
||||
int bw_mask;
|
||||
int delta_diff;
|
||||
int same_sign;
|
||||
int temp;
|
||||
|
||||
if (IS_CHAN_HT40(dfs->ic->ic_curchan)) {
|
||||
num_fft_bytes = NUM_FFT_BYTES_HT40;
|
||||
num_bin_bytes = NUM_BIN_BYTES_HT40;
|
||||
num_subchan_bins = NUM_SUBCHAN_BINS_HT40;
|
||||
lower_index_byte = LOWER_INDEX_BYTE_HT40;
|
||||
upper_index_byte = UPPER_INDEX_BYTE_HT40;
|
||||
lower_weight_byte = LOWER_WEIGHT_BYTE_HT40;
|
||||
upper_weight_byte = UPPER_WEIGHT_BYTE_HT40;
|
||||
lower_mag_byte = LOWER_MAG_BYTE_HT40;
|
||||
upper_mag_byte = UPPER_MAG_BYTE_HT40;
|
||||
|
||||
/* if we are in HT40MINUS then swap primary and extension */
|
||||
if (IS_CHAN_HT40_MINUS(dfs->ic->ic_curchan)) {
|
||||
temp = is_ctl;
|
||||
is_ctl = is_ext;
|
||||
is_ext = temp;
|
||||
}
|
||||
|
||||
} else {
|
||||
num_fft_bytes = NUM_FFT_BYTES_HT20;
|
||||
num_bin_bytes = NUM_BIN_BYTES_HT20;
|
||||
num_subchan_bins = NUM_SUBCHAN_BINS_HT20;
|
||||
lower_index_byte = LOWER_INDEX_BYTE_HT20;
|
||||
upper_index_byte = UPPER_INDEX_BYTE_HT20;
|
||||
lower_weight_byte = LOWER_WEIGHT_BYTE_HT20;
|
||||
upper_weight_byte = UPPER_WEIGHT_BYTE_HT20;
|
||||
lower_mag_byte = LOWER_MAG_BYTE_HT20;
|
||||
upper_mag_byte = UPPER_MAG_BYTE_HT20;
|
||||
}
|
||||
|
||||
ptr = (uint8_t *) buf;
|
||||
/*
|
||||
* sanity check for FFT buffer
|
||||
*/
|
||||
|
||||
if ((ptr == NULL) || (datalen == 0)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"%s: FFT buffer pointer is null or size is 0\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
num_fft_packets = (datalen - 3) / num_fft_bytes;
|
||||
if (num_fft_packets < (NUM_DIFFS + DELTA_STEP)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"datalen = %d, num_fft_packets = %d, too few packets... (exiting)\n",
|
||||
datalen, num_fft_packets);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((((datalen - 3) % num_fft_bytes) == 2) && (datalen > num_fft_bytes)) {
|
||||
/* FIXME !!! */
|
||||
ptr += 2;
|
||||
datalen -= 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < (NUM_DIFFS + DELTA_STEP); i++) {
|
||||
fft_start = i * num_fft_bytes;
|
||||
bin_wt_lower[i] = ptr[fft_start + lower_weight_byte] & 0x3f;
|
||||
bin_wt_upper[i] = ptr[fft_start + upper_weight_byte] & 0x3f;
|
||||
|
||||
max_index_lower[i] = ptr[fft_start + lower_index_byte] >> 2;
|
||||
max_index_upper[i] =
|
||||
(ptr[fft_start + upper_index_byte] >> 2) + num_subchan_bins;
|
||||
|
||||
if (!IS_CHAN_HT40(dfs->ic->ic_curchan)) {
|
||||
/*
|
||||
* for HT20 mode indices are 6 bit signed number
|
||||
*/
|
||||
max_index_lower[i] ^= 0x20;
|
||||
max_index_upper[i] = 0;
|
||||
}
|
||||
/*
|
||||
* Reconstruct the maximum magnitude for each sub-channel. Also select
|
||||
* and flag the max overall magnitude between the two sub-channels.
|
||||
*/
|
||||
|
||||
max_mag_lower[i] =
|
||||
((ptr[fft_start + lower_index_byte] & 0x03) << 8) +
|
||||
ptr[fft_start + lower_mag_byte];
|
||||
max_mag_upper[i] =
|
||||
((ptr[fft_start + upper_index_byte] & 0x03) << 8) +
|
||||
ptr[fft_start + upper_mag_byte];
|
||||
bw_mask =
|
||||
((bin_wt_lower[i] ==
|
||||
0) ? 0 : is_ctl) + (((bin_wt_upper[i] ==
|
||||
0) ? 0 : is_ext) << 1);
|
||||
|
||||
/*
|
||||
* Limit the max bin based on channel bandwidth
|
||||
* If the upper sub-channel max index is stuck at '1', the signal is dominated
|
||||
* by residual DC (or carrier leak) and should be ignored.
|
||||
*/
|
||||
|
||||
if (bw_mask == 1) {
|
||||
max_mag_sel[i] = 0;
|
||||
max_mag[i] = max_mag_lower[i];
|
||||
max_index[i] = max_index_lower[i];
|
||||
} else if (bw_mask == 2) {
|
||||
max_mag_sel[i] = 1;
|
||||
max_mag[i] = max_mag_upper[i];
|
||||
max_index[i] = max_index_upper[i];
|
||||
} else if (max_index_upper[i] == num_subchan_bins) {
|
||||
max_mag_sel[i] = 0; /* Ignore DC bin. */
|
||||
max_mag[i] = max_mag_lower[i];
|
||||
max_index[i] = max_index_lower[i];
|
||||
} else {
|
||||
if (max_mag_upper[i] > max_mag_lower[i]) {
|
||||
max_mag_sel[i] = 1;
|
||||
max_mag[i] = max_mag_upper[i];
|
||||
max_index[i] = max_index_upper[i];
|
||||
} else {
|
||||
max_mag_sel[i] = 0;
|
||||
max_mag[i] = max_mag_lower[i];
|
||||
max_index[i] = max_index_lower[i];
|
||||
}
|
||||
}
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"i=%d, max_index[i]=%d, max_index_lower[i]=%d, "
|
||||
"max_index_upper[i]=%d\n",
|
||||
i, max_index[i], max_index_lower[i],
|
||||
max_index_upper[i]);
|
||||
}
|
||||
|
||||
chirp_found = 1;
|
||||
delta_diff = 0;
|
||||
same_sign = 1;
|
||||
|
||||
/*
|
||||
delta_diff computation -- look for movement in peak.
|
||||
make sure that the chirp direction (i.e. sign) is always the same,
|
||||
i.e. sign of the two peaks should be same.
|
||||
*/
|
||||
for (i = 0; i < NUM_DIFFS; i++) {
|
||||
delta_peak[i] = max_index[i + DELTA_STEP] - max_index[i];
|
||||
if (i > 0) {
|
||||
delta_diff = delta_peak[i] - delta_peak[i - 1];
|
||||
same_sign =
|
||||
!((delta_peak[i] & 0x80) ^
|
||||
(delta_peak[i - 1] & 0x80));
|
||||
}
|
||||
chirp_found &= (ABS(delta_peak[i]) >= min_d[DELTA_STEP - 1]) &&
|
||||
(ABS(delta_peak[i]) <= max_d[DELTA_STEP - 1]) &&
|
||||
same_sign && (ABS(delta_diff) <= MAX_DIFF);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"i=%d, delta_peak[i]=%d, delta_diff=%d\n",
|
||||
i, delta_peak[i], delta_diff);
|
||||
}
|
||||
|
||||
if (chirp_found) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"%s: CHIRPING_BEFORE_STRONGBIN_YES\n", __func__);
|
||||
} else {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"%s: CHIRPING_BEFORE_STRONGBIN_NO\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
Work around for potential hardware data corruption bug. Check for
|
||||
wide band signal by counting strong bins indicated by bitmap flags.
|
||||
This check is done if chirp_found is true. We do this as a final check
|
||||
to weed out corrupt FFTs bytes. This looks expensive but in most cases it
|
||||
will exit early.
|
||||
*/
|
||||
|
||||
for (i = 0; (i < (NUM_DIFFS + DELTA_STEP)) && (chirp_found == 1); i++) {
|
||||
bin_count = 0;
|
||||
/* point to the start of the 1st byte of the selected sub-channel. */
|
||||
fft_start =
|
||||
(i * num_fft_bytes) +
|
||||
(max_mag_sel[i] ? (num_subchan_bins >> 1) : 0);
|
||||
for (j = 0; j < (num_subchan_bins >> 1); j++) {
|
||||
/*
|
||||
* If either bin is flagged "strong", accumulate the bin_count.
|
||||
* It's not accurate, but good enough...
|
||||
*/
|
||||
bin_count += (ptr[fft_start + j] & 0x88) ? 1 : 0;
|
||||
}
|
||||
chirp_found &= (bin_count > BIN_COUNT_MAX) ? 0 : 1;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT,
|
||||
"i=%d, computed bin_count=%d\n", i, bin_count);
|
||||
}
|
||||
|
||||
if (chirp_found) {
|
||||
DFS_DPRINTK(dfs,
|
||||
ATH_DEBUG_DFS_BIN5_FFT | ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: CHIRPING_YES\n", __func__);
|
||||
} else {
|
||||
DFS_DPRINTK(dfs,
|
||||
ATH_DEBUG_DFS_BIN5_FFT | ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: CHIRPING_NO\n", __func__);
|
||||
}
|
||||
return chirp_found;
|
||||
|
||||
#undef ABS_DIFF
|
||||
#undef ABS
|
||||
#undef DELTA_STEP
|
||||
#undef NUM_DIFFS
|
||||
#undef MAX_DIFF
|
||||
#undef BIN_COUNT_MAX
|
||||
|
||||
#undef NUM_FFT_BYTES_HT40
|
||||
#undef NUM_BIN_BYTES_HT40
|
||||
#undef NUM_SUBCHAN_BINS_HT40
|
||||
#undef LOWER_INDEX_BYTE_HT40
|
||||
#undef UPPER_INDEX_BYTE_HT40
|
||||
#undef LOWER_WEIGHT_BYTE_HT40
|
||||
#undef UPPER_WEIGHT_BYTE_HT40
|
||||
#undef LOWER_MAG_BYTE_HT40
|
||||
#undef UPPER_MAG_BYTE_HT40
|
||||
|
||||
#undef NUM_FFT_BYTES_HT40
|
||||
#undef NUM_BIN_BYTES_HT40
|
||||
#undef NUM_SUBCHAN_BINS_HT40
|
||||
#undef LOWER_INDEX_BYTE_HT40
|
||||
#undef UPPER_INDEX_BYTE_HT40
|
||||
#undef LOWER_WEIGHT_BYTE_HT40
|
||||
#undef UPPER_WEIGHT_BYTE_HT40
|
||||
#undef LOWER_MAG_BYTE_HT40
|
||||
#undef UPPER_MAG_BYTE_HT40
|
||||
}
|
||||
|
||||
int
|
||||
dfs_check_chirping(struct ath_dfs *dfs, void *buf,
|
||||
uint16_t datalen, int is_ctl, int is_ext, int *slope,
|
||||
int *is_dc)
|
||||
{
|
||||
|
||||
if (dfs->dfs_caps.ath_dfs_use_enhancement)
|
||||
return dfs_check_chirping_merlin(dfs, buf, datalen, is_ctl,
|
||||
is_ext, slope, is_dc);
|
||||
else
|
||||
return dfs_check_chirping_sowl(dfs, buf, datalen, is_ctl,
|
||||
is_ext, slope, is_dc);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
dfs_retain_bin5_burst_pattern(struct ath_dfs *dfs, uint32_t diff_ts,
|
||||
uint8_t old_dur)
|
||||
{
|
||||
|
||||
/*
|
||||
* Pulses may get split into 2 during chirping, this print is only
|
||||
* to show that it happened, we do not handle this condition if we
|
||||
* cannot detect the chirping.
|
||||
*/
|
||||
/*
|
||||
* SPLIT pulses will have a time stamp difference of < 50
|
||||
*/
|
||||
if (diff_ts < 50) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
"%s SPLIT pulse diffTs=%u dur=%d (old_dur=%d)\n",
|
||||
__func__, diff_ts,
|
||||
dfs->dfs_rinfo.dfs_last_bin5_dur, old_dur);
|
||||
}
|
||||
/*
|
||||
* Check if this is the 2nd or 3rd pulse in the same burst,
|
||||
* PRI will be between 1000 and 2000 us
|
||||
*/
|
||||
if (((diff_ts >= DFS_BIN5_PRI_LOWER_LIMIT) &&
|
||||
(diff_ts <= DFS_BIN5_PRI_HIGHER_LIMIT))) {
|
||||
/*
|
||||
* This pulse belongs to the same burst as the pulse before,
|
||||
* so return the same random duration for it
|
||||
*/
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5,
|
||||
"%s this pulse belongs to the same burst as before, give "
|
||||
"it same dur=%d (old_dur=%d)\n",
|
||||
__func__, dfs->dfs_rinfo.dfs_last_bin5_dur,
|
||||
old_dur);
|
||||
|
||||
return (dfs->dfs_rinfo.dfs_last_bin5_dur);
|
||||
}
|
||||
/*
|
||||
* This pulse does not belong to this burst, return unchanged
|
||||
* duration.
|
||||
*/
|
||||
return old_dur;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chirping pulses may get cut off at DC and report lower durations.
|
||||
*
|
||||
* This function will compute a suitable random duration for each pulse.
|
||||
* Duration must be between 50 and 100 us, but remember that in
|
||||
* ath_process_phyerr() which calls this function, we are dealing with the
|
||||
* HW reported duration (unconverted). dfs_process_radarevent() will
|
||||
* actually convert the duration into the correct value.
|
||||
*
|
||||
* XXX This function doesn't take into account whether the hardware
|
||||
* is operating in 5GHz fast clock mode or not.
|
||||
*
|
||||
* XXX And this function doesn't take into account whether the hardware
|
||||
* is peregrine or not. Grr.
|
||||
*/
|
||||
int dfs_get_random_bin5_dur(struct ath_dfs *dfs, uint64_t tstamp)
|
||||
{
|
||||
uint8_t new_dur = MIN_BIN5_DUR;
|
||||
int range;
|
||||
|
||||
get_random_bytes(&new_dur, sizeof(uint8_t));
|
||||
|
||||
range = (MAX_BIN5_DUR - MIN_BIN5_DUR + 1);
|
||||
|
||||
new_dur %= range;
|
||||
|
||||
new_dur += MIN_BIN5_DUR;
|
||||
|
||||
return new_dur;
|
||||
}
|
||||
|
||||
#endif /* ATH_SUPPORT_DFS */
|
403
core/sap/dfs/src/dfs_init.c
Normal file
403
core/sap/dfs/src/dfs_init.c
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* 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_init.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
|
||||
|
||||
extern int domainoverride;
|
||||
|
||||
/*
|
||||
* Clear all delay lines for all filter types
|
||||
*
|
||||
* This may be called before any radar pulses are configured
|
||||
* (eg on a non-DFS channel, with radar PHY errors still showing up.)
|
||||
* In that case, just drop out early.
|
||||
*/
|
||||
void dfs_reset_alldelaylines(struct ath_dfs *dfs)
|
||||
{
|
||||
struct dfs_filtertype *ft = NULL;
|
||||
struct dfs_filter *rf;
|
||||
struct dfs_delayline *dl;
|
||||
struct dfs_pulseline *pl;
|
||||
int i, j;
|
||||
|
||||
if (dfs == NULL) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s[%d]: sc_dfs is NULL", __func__, __LINE__);
|
||||
return;
|
||||
}
|
||||
pl = dfs->pulses;
|
||||
|
||||
if (pl == NULL) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s[%d]: pl==NULL, dfs=%p", __func__, __LINE__, dfs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dfs->dfs_b5radars == NULL) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s[%d]: pl==NULL, b5radars=%p", __func__, __LINE__,
|
||||
dfs->dfs_b5radars);
|
||||
return;
|
||||
}
|
||||
|
||||
/* reset the pulse log */
|
||||
pl->pl_firstelem = pl->pl_numelems = 0;
|
||||
pl->pl_lastelem = DFS_MAX_PULSE_BUFFER_MASK;
|
||||
|
||||
for (i = 0; i < DFS_MAX_RADAR_TYPES; i++) {
|
||||
if (dfs->dfs_radarf[i] != NULL) {
|
||||
ft = dfs->dfs_radarf[i];
|
||||
if (NULL != ft) {
|
||||
for (j = 0; j < ft->ft_numfilters; j++) {
|
||||
rf = &(ft->ft_filters[j]);
|
||||
dl = &(rf->rf_dl);
|
||||
if (dl != NULL) {
|
||||
OS_MEMZERO(dl,
|
||||
sizeof(struct
|
||||
dfs_delayline));
|
||||
dl->dl_lastelem =
|
||||
(0xFFFFFFFF) &
|
||||
DFS_MAX_DL_MASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < dfs->dfs_rinfo.rn_numbin5radars; i++) {
|
||||
OS_MEMZERO(&(dfs->dfs_b5radars[i].br_elems[0]),
|
||||
sizeof(struct dfs_bin5elem) * DFS_MAX_B5_SIZE);
|
||||
dfs->dfs_b5radars[i].br_firstelem = 0;
|
||||
dfs->dfs_b5radars[i].br_numelems = 0;
|
||||
dfs->dfs_b5radars[i].br_lastelem =
|
||||
(0xFFFFFFFF) & DFS_MAX_B5_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear only a single delay line
|
||||
*/
|
||||
|
||||
void dfs_reset_delayline(struct dfs_delayline *dl)
|
||||
{
|
||||
OS_MEMZERO(&(dl->dl_elems[0]), sizeof(dl->dl_elems));
|
||||
dl->dl_lastelem = (0xFFFFFFFF) & DFS_MAX_DL_MASK;
|
||||
}
|
||||
|
||||
void dfs_reset_filter_delaylines(struct dfs_filtertype *dft)
|
||||
{
|
||||
int i;
|
||||
struct dfs_filter *df;
|
||||
for (i = 0; i < DFS_MAX_NUM_RADAR_FILTERS; i++) {
|
||||
df = &dft->ft_filters[i];
|
||||
dfs_reset_delayline(&(df->rf_dl));
|
||||
}
|
||||
}
|
||||
|
||||
void dfs_reset_radarq(struct ath_dfs *dfs)
|
||||
{
|
||||
struct dfs_event *event;
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "%s: sc_dfs is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
ATH_DFSQ_LOCK(dfs);
|
||||
ATH_DFSEVENTQ_LOCK(dfs);
|
||||
while (!STAILQ_EMPTY(&(dfs->dfs_radarq))) {
|
||||
event = STAILQ_FIRST(&(dfs->dfs_radarq));
|
||||
STAILQ_REMOVE_HEAD(&(dfs->dfs_radarq), re_list);
|
||||
OS_MEMZERO(event, sizeof(struct dfs_event));
|
||||
STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), event, re_list);
|
||||
}
|
||||
ATH_DFSEVENTQ_UNLOCK(dfs);
|
||||
ATH_DFSQ_UNLOCK(dfs);
|
||||
}
|
||||
|
||||
/* This function Initialize the radar filter tables
|
||||
* if the ath dfs domain is uninitalized or
|
||||
* ath dfs domain is different from hal dfs domain
|
||||
*/
|
||||
int dfs_init_radar_filters(struct ieee80211com *ic,
|
||||
struct ath_dfs_radar_tab_info *radar_info)
|
||||
{
|
||||
uint32_t T, Tmax;
|
||||
int numpulses, p, n, i;
|
||||
int numradars = 0, numb5radars = 0;
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
struct dfs_filtertype *ft = NULL;
|
||||
struct dfs_filter *rf = NULL;
|
||||
struct dfs_pulse *dfs_radars;
|
||||
struct dfs_bin5pulse *b5pulses = NULL;
|
||||
int32_t min_rssithresh = DFS_MAX_RSSI_VALUE;
|
||||
uint32_t max_pulsedur = 0;
|
||||
|
||||
if (dfs == NULL) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s[%d]: dfs is NULL", __func__, __LINE__);
|
||||
return DFS_STATUS_FAIL;
|
||||
}
|
||||
/* clear up the dfs domain flag first */
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
dfs->ath_dfs_isdfsregdomain = 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If radar_info is NULL or dfsdomain is NULL, treat
|
||||
* the rest of the radar configuration as suspect.
|
||||
*/
|
||||
if (radar_info == NULL || radar_info->dfsdomain == 0) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s[%d]: Unknown dfs domain %d ",
|
||||
__func__, __LINE__, dfs->dfsdomain);
|
||||
/* Disable radar detection since we don't have a radar domain */
|
||||
dfs->dfs_proc_phyerr &= ~DFS_RADAR_EN;
|
||||
/* Returning success though we are not completing init. A failure
|
||||
* will fail dfs_attach also.
|
||||
*/
|
||||
return DFS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s[%d]:dfsdomain=%d, numradars=%d, numb5radars=%d",
|
||||
__func__, __LINE__, radar_info->dfsdomain,
|
||||
radar_info->numradars, radar_info->numb5radars);
|
||||
dfs->dfsdomain = radar_info->dfsdomain;
|
||||
dfs_radars = radar_info->dfs_radars;
|
||||
numradars = radar_info->numradars;
|
||||
b5pulses = radar_info->b5pulses;
|
||||
numb5radars = radar_info->numb5radars;
|
||||
|
||||
/* XXX this should be an explicit copy of some sort! */
|
||||
dfs->dfs_defaultparams = radar_info->dfs_defaultparams;
|
||||
|
||||
#ifndef ATH_DFS_RADAR_DETECTION_ONLY
|
||||
dfs->ath_dfs_isdfsregdomain = 1;
|
||||
#endif
|
||||
|
||||
dfs->dfs_rinfo.rn_numradars = 0;
|
||||
/* Clear filter type table */
|
||||
for (n = 0; n < 256; n++) {
|
||||
for (i = 0; i < DFS_MAX_RADAR_OVERLAP; i++)
|
||||
(dfs->dfs_radartable[n])[i] = -1;
|
||||
}
|
||||
/* Now, initialize the radar filters */
|
||||
for (p = 0; p < numradars; p++) {
|
||||
ft = NULL;
|
||||
for (n = 0; n < dfs->dfs_rinfo.rn_numradars; n++) {
|
||||
if ((dfs_radars[p].rp_pulsedur ==
|
||||
dfs->dfs_radarf[n]->ft_filterdur)
|
||||
&& (dfs_radars[p].rp_numpulses ==
|
||||
dfs->dfs_radarf[n]->ft_numpulses)
|
||||
&& (dfs_radars[p].rp_mindur ==
|
||||
dfs->dfs_radarf[n]->ft_mindur)
|
||||
&& (dfs_radars[p].rp_maxdur ==
|
||||
dfs->dfs_radarf[n]->ft_maxdur)) {
|
||||
ft = dfs->dfs_radarf[n];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ft == NULL) {
|
||||
/* No filter of the appropriate dur was found */
|
||||
if ((dfs->dfs_rinfo.rn_numradars + 1) >
|
||||
DFS_MAX_RADAR_TYPES) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"%s: Too many filter types",
|
||||
__func__);
|
||||
goto bad4;
|
||||
}
|
||||
ft = dfs->dfs_radarf[dfs->dfs_rinfo.rn_numradars];
|
||||
ft->ft_numfilters = 0;
|
||||
ft->ft_numpulses = dfs_radars[p].rp_numpulses;
|
||||
ft->ft_patterntype = dfs_radars[p].rp_patterntype;
|
||||
ft->ft_mindur = dfs_radars[p].rp_mindur;
|
||||
ft->ft_maxdur = dfs_radars[p].rp_maxdur;
|
||||
ft->ft_filterdur = dfs_radars[p].rp_pulsedur;
|
||||
ft->ft_rssithresh = dfs_radars[p].rp_rssithresh;
|
||||
ft->ft_rssimargin = dfs_radars[p].rp_rssimargin;
|
||||
ft->ft_minpri = 1000000;
|
||||
|
||||
if (ft->ft_rssithresh < min_rssithresh)
|
||||
min_rssithresh = ft->ft_rssithresh;
|
||||
if (ft->ft_maxdur > max_pulsedur)
|
||||
max_pulsedur = ft->ft_maxdur;
|
||||
for (i = ft->ft_mindur; i <= ft->ft_maxdur; i++) {
|
||||
uint32_t stop = 0, tableindex = 0;
|
||||
while ((tableindex < DFS_MAX_RADAR_OVERLAP)
|
||||
&& (!stop)) {
|
||||
if ((dfs->
|
||||
dfs_radartable[i])[tableindex] ==
|
||||
-1)
|
||||
stop = 1;
|
||||
else
|
||||
tableindex++;
|
||||
}
|
||||
if (stop) {
|
||||
(dfs->dfs_radartable[i])[tableindex] =
|
||||
(int8_t) (dfs->dfs_rinfo.
|
||||
rn_numradars);
|
||||
} else {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"%s: Too many overlapping radar filters",
|
||||
__func__);
|
||||
goto bad4;
|
||||
}
|
||||
}
|
||||
dfs->dfs_rinfo.rn_numradars++;
|
||||
}
|
||||
rf = &(ft->ft_filters[ft->ft_numfilters++]);
|
||||
dfs_reset_delayline(&rf->rf_dl);
|
||||
numpulses = dfs_radars[p].rp_numpulses;
|
||||
|
||||
rf->rf_numpulses = numpulses;
|
||||
rf->rf_patterntype = dfs_radars[p].rp_patterntype;
|
||||
rf->rf_pulseid = dfs_radars[p].rp_pulseid;
|
||||
rf->rf_mindur = dfs_radars[p].rp_mindur;
|
||||
rf->rf_maxdur = dfs_radars[p].rp_maxdur;
|
||||
rf->rf_numpulses = dfs_radars[p].rp_numpulses;
|
||||
rf->rf_ignore_pri_window = dfs_radars[p].rp_ignore_pri_window;
|
||||
T = (100000000 / dfs_radars[p].rp_max_pulsefreq) -
|
||||
100 * (dfs_radars[p].rp_meanoffset);
|
||||
rf->rf_minpri =
|
||||
dfs_round((int32_t) T -
|
||||
(100 * (dfs_radars[p].rp_pulsevar)));
|
||||
Tmax =
|
||||
(100000000 / dfs_radars[p].rp_pulsefreq) -
|
||||
100 * (dfs_radars[p].rp_meanoffset);
|
||||
rf->rf_maxpri =
|
||||
dfs_round((int32_t) Tmax +
|
||||
(100 * (dfs_radars[p].rp_pulsevar)));
|
||||
|
||||
if (rf->rf_minpri < ft->ft_minpri)
|
||||
ft->ft_minpri = rf->rf_minpri;
|
||||
|
||||
rf->rf_fixed_pri_radar_pulse =
|
||||
(dfs_radars[p].rp_max_pulsefreq ==
|
||||
dfs_radars[p].rp_pulsefreq) ? 1 : 0;
|
||||
rf->rf_threshold = dfs_radars[p].rp_threshold;
|
||||
rf->rf_filterlen = rf->rf_maxpri * rf->rf_numpulses;
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s[%d]: minprf = %d maxprf = %d pulsevar = %d thresh=%d",
|
||||
__func__, __LINE__, dfs_radars[p].rp_pulsefreq,
|
||||
dfs_radars[p].rp_max_pulsefreq,
|
||||
dfs_radars[p].rp_pulsevar, rf->rf_threshold);
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s[%d]:minpri = %d maxpri = %d filterlen = %d filterID = %d",
|
||||
__func__, __LINE__, rf->rf_minpri, rf->rf_maxpri,
|
||||
rf->rf_filterlen, rf->rf_pulseid);
|
||||
|
||||
}
|
||||
|
||||
#ifdef DFS_DEBUG
|
||||
dfs_print_filters(ic);
|
||||
#endif
|
||||
dfs->dfs_rinfo.rn_numbin5radars = numb5radars;
|
||||
if (dfs->dfs_b5radars != NULL)
|
||||
OS_FREE(dfs->dfs_b5radars);
|
||||
|
||||
dfs->dfs_b5radars = (struct dfs_bin5radars *)os_malloc(NULL,
|
||||
numb5radars *
|
||||
sizeof(struct
|
||||
dfs_bin5radars),
|
||||
GFP_KERNEL);
|
||||
if (dfs->dfs_b5radars == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"%s: cannot allocate memory for bin5 radars",
|
||||
__func__);
|
||||
goto bad4;
|
||||
}
|
||||
for (n = 0; n < numb5radars; n++) {
|
||||
dfs->dfs_b5radars[n].br_pulse = b5pulses[n];
|
||||
dfs->dfs_b5radars[n].br_pulse.b5_timewindow *= 1000000;
|
||||
if (dfs->dfs_b5radars[n].br_pulse.b5_rssithresh <
|
||||
min_rssithresh)
|
||||
min_rssithresh =
|
||||
dfs->dfs_b5radars[n].br_pulse.b5_rssithresh;
|
||||
if (dfs->dfs_b5radars[n].br_pulse.b5_maxdur > max_pulsedur)
|
||||
max_pulsedur = dfs->dfs_b5radars[n].br_pulse.b5_maxdur;
|
||||
}
|
||||
dfs_reset_alldelaylines(dfs);
|
||||
dfs_reset_radarq(dfs);
|
||||
dfs->dfs_curchan_radindex = -1;
|
||||
dfs->dfs_extchan_radindex = -1;
|
||||
dfs->dfs_rinfo.rn_minrssithresh = min_rssithresh;
|
||||
/* Convert durations to TSF ticks */
|
||||
dfs->dfs_rinfo.rn_maxpulsedur =
|
||||
dfs_round((int32_t) ((max_pulsedur * 100 / 80) * 100));
|
||||
/* relax the max pulse duration a little bit due to inaccuracy caused by chirping. */
|
||||
dfs->dfs_rinfo.rn_maxpulsedur = dfs->dfs_rinfo.rn_maxpulsedur + 20;
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s[%d]: DFS min filter rssiThresh = %d",
|
||||
__func__, __LINE__, min_rssithresh);
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s[%d]:DFS max pulse dur = %d ticks",
|
||||
__func__, __LINE__, dfs->dfs_rinfo.rn_maxpulsedur);
|
||||
return DFS_STATUS_SUCCESS;
|
||||
|
||||
bad4:
|
||||
return DFS_STATUS_FAIL;
|
||||
}
|
||||
|
||||
void dfs_clear_stats(struct ieee80211com *ic)
|
||||
{
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
if (dfs == NULL)
|
||||
return;
|
||||
OS_MEMZERO(&dfs->ath_dfs_stats, sizeof(struct dfs_stats));
|
||||
dfs->ath_dfs_stats.last_reset_tstamp = ic->ic_get_TSF64(ic);
|
||||
}
|
||||
|
||||
#endif /* ATH_SUPPORT_DFS */
|
117
core/sap/dfs/src/dfs_ioctl.h
Normal file
117
core/sap/dfs/src/dfs_ioctl.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2015 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_ioctl.h
|
||||
|
||||
OVERVIEW:
|
||||
|
||||
Source code borrowed from QCA_MAIN DFS module
|
||||
|
||||
DEPENDENCIES:
|
||||
|
||||
Are listed for each API below.
|
||||
|
||||
===========================================================================*/
|
||||
|
||||
/*
|
||||
* ioctl defines
|
||||
*/
|
||||
|
||||
#ifndef _DFS_IOCTL_H_
|
||||
#define _DFS_IOCTL_H_
|
||||
|
||||
#define DFS_MUTE_TIME 1
|
||||
#define DFS_SET_THRESH 2
|
||||
#define DFS_GET_THRESH 3
|
||||
#define DFS_GET_USENOL 4
|
||||
#define DFS_SET_USENOL 5
|
||||
#define DFS_RADARDETECTS 6
|
||||
#define DFS_BANGRADAR 7
|
||||
#define DFS_SHOW_NOL 8
|
||||
#define DFS_DISABLE_DETECT 9
|
||||
#define DFS_ENABLE_DETECT 10
|
||||
#define DFS_DISABLE_FFT 11
|
||||
#define DFS_ENABLE_FFT 12
|
||||
#define DFS_SET_DEBUG_LEVEL 13
|
||||
#define DFS_GET_NOL 14
|
||||
#define DFS_SET_NOL 15
|
||||
|
||||
#define DFS_SET_FALSE_RSSI_THRES 16
|
||||
#define DFS_SET_PEAK_MAG 17
|
||||
#define DFS_IGNORE_CAC 18
|
||||
#define DFS_SET_NOL_TIMEOUT 19
|
||||
#define DFS_LAST_IOCTL 20
|
||||
#ifndef IEEE80211_CHAN_MAX
|
||||
#define IEEE80211_CHAN_MAX 255
|
||||
#endif
|
||||
|
||||
struct dfsreq_nolelem {
|
||||
uint16_t nol_freq; /* NOL channel frequency */
|
||||
uint16_t nol_chwidth;
|
||||
unsigned long nol_start_ticks; /* OS ticks when the NOL timer started */
|
||||
uint32_t nol_timeout_ms; /* Nol timeout value in msec */
|
||||
};
|
||||
|
||||
struct dfsreq_nolinfo {
|
||||
uint32_t ic_nchans;
|
||||
struct dfsreq_nolelem dfs_nol[IEEE80211_CHAN_MAX];
|
||||
};
|
||||
|
||||
/*
|
||||
* ioctl parameter types
|
||||
*/
|
||||
|
||||
#define DFS_PARAM_FIRPWR 1
|
||||
#define DFS_PARAM_RRSSI 2
|
||||
#define DFS_PARAM_HEIGHT 3
|
||||
#define DFS_PARAM_PRSSI 4
|
||||
#define DFS_PARAM_INBAND 5
|
||||
/* 5413 specific parameters */
|
||||
#define DFS_PARAM_RELPWR 7
|
||||
#define DFS_PARAM_RELSTEP 8
|
||||
#define DFS_PARAM_MAXLEN 9
|
||||
|
||||
struct dfs_ioctl_params {
|
||||
int32_t dfs_firpwr; /* FIR pwr out threshold */
|
||||
int32_t dfs_rrssi; /* Radar rssi thresh */
|
||||
int32_t dfs_height; /* Pulse height thresh */
|
||||
int32_t dfs_prssi; /* Pulse rssi thresh */
|
||||
int32_t dfs_inband; /* Inband thresh */
|
||||
int32_t dfs_relpwr; /* pulse relative pwr thresh */
|
||||
int32_t dfs_relstep; /* pulse relative step thresh */
|
||||
int32_t dfs_maxlen; /* pulse max duration */
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX keep these in sync with ath_dfs_phyerr_param!
|
||||
*/
|
||||
#define DFS_IOCTL_PARAM_NOVAL 65535
|
||||
#define DFS_IOCTL_PARAM_ENABLE 0x8000
|
||||
|
||||
#endif /* _DFS_IOCTL_H_ */
|
100
core/sap/dfs/src/dfs_ioctl_private.h
Normal file
100
core/sap/dfs/src/dfs_ioctl_private.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2015 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_ioctl_private.h
|
||||
|
||||
OVERVIEW:
|
||||
|
||||
Source code borrowed from QCA_MAIN DFS module
|
||||
|
||||
DEPENDENCIES:
|
||||
|
||||
Are listed for each API below.
|
||||
|
||||
===========================================================================*/
|
||||
|
||||
/*
|
||||
* ioctl defines
|
||||
*/
|
||||
|
||||
#ifndef _DFS_IOCTL_PRIVATE_H_
|
||||
#define _DFS_IOCTL_PRIVATE_H_
|
||||
|
||||
/*
|
||||
* Assert that the NOVAL values match.
|
||||
*/
|
||||
#if (ATH_DFS_PHYERR_PARAM_NOVAL != DFS_IOCTL_PARAM_NOVAL)
|
||||
#error "ATH_DFS_PHYERR_PARAM_NOVAL != DFS_IOCTL_PARAM_NOVAL"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assert that the ENABLE values match.
|
||||
*/
|
||||
#if (ATH_DFS_PHYERR_PARAM_ENABLE != DFS_IOCTL_PARAM_ENABLE)
|
||||
#error "ATH_DFS_PHYERR_PARAM_ENABLE != DFS_IOCTL_PARAM_ENABLE"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These two methods are used by the lmac glue to copy between
|
||||
* the DFS and HAL PHY configuration.
|
||||
*
|
||||
* I'm "cheating" here and assuming that the ENABLE and NOVAL
|
||||
* values match - see the above macros.
|
||||
*/
|
||||
static inline void
|
||||
ath_dfs_ioctlparam_to_dfsparam(const struct dfs_ioctl_params *src,
|
||||
struct ath_dfs_phyerr_param *dst)
|
||||
{
|
||||
|
||||
dst->pe_firpwr = src->dfs_firpwr;
|
||||
dst->pe_rrssi = src->dfs_rrssi;
|
||||
dst->pe_height = src->dfs_height;
|
||||
dst->pe_prssi = src->dfs_prssi;
|
||||
dst->pe_inband = src->dfs_inband;
|
||||
dst->pe_relpwr = src->dfs_relpwr;
|
||||
dst->pe_relstep = src->dfs_relstep;
|
||||
dst->pe_maxlen = src->dfs_maxlen;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ath_dfs_dfsparam_to_ioctlparam(struct ath_dfs_phyerr_param *src,
|
||||
struct dfs_ioctl_params *dst)
|
||||
{
|
||||
|
||||
dst->dfs_firpwr = src->pe_firpwr;
|
||||
dst->dfs_rrssi = src->pe_rrssi;
|
||||
dst->dfs_height = src->pe_height;
|
||||
dst->dfs_prssi = src->pe_prssi;
|
||||
dst->dfs_inband = src->pe_inband;
|
||||
dst->dfs_relpwr = src->pe_relpwr;
|
||||
dst->dfs_relstep = src->pe_relstep;
|
||||
dst->dfs_maxlen = src->pe_maxlen;
|
||||
}
|
||||
|
||||
#endif /* _DFS_IOCTL_PRIVATE_H_ */
|
276
core/sap/dfs/src/dfs_misc.c
Normal file
276
core/sap/dfs/src/dfs_misc.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* 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_misc.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>
|
||||
*/
|
||||
#ifndef UNINET
|
||||
/* TO DO DFS
|
||||
#include <ieee80211_channel.h>
|
||||
*/
|
||||
#endif
|
||||
#ifdef ATH_SUPPORT_DFS
|
||||
|
||||
static int adjust_pri_per_chan_busy(int ext_chan_busy, int pri_margin)
|
||||
{
|
||||
int adjust_pri = 0;
|
||||
|
||||
if (ext_chan_busy > DFS_EXT_CHAN_LOADING_THRESH) {
|
||||
|
||||
adjust_pri =
|
||||
(ext_chan_busy -
|
||||
DFS_EXT_CHAN_LOADING_THRESH) * (pri_margin);
|
||||
adjust_pri /= 100;
|
||||
|
||||
}
|
||||
return adjust_pri;
|
||||
}
|
||||
|
||||
static int adjust_thresh_per_chan_busy(int ext_chan_busy, int thresh)
|
||||
{
|
||||
int adjust_thresh = 0;
|
||||
|
||||
if (ext_chan_busy > DFS_EXT_CHAN_LOADING_THRESH) {
|
||||
|
||||
adjust_thresh =
|
||||
(ext_chan_busy - DFS_EXT_CHAN_LOADING_THRESH) * thresh;
|
||||
adjust_thresh /= 100;
|
||||
|
||||
}
|
||||
return adjust_thresh;
|
||||
}
|
||||
|
||||
/* For the extension channel, if legacy traffic is present, we see a lot of false alarms,
|
||||
so make the PRI margin narrower depending on the busy % for the extension channel.*/
|
||||
|
||||
int
|
||||
dfs_get_pri_margin(struct ath_dfs *dfs, int is_extchan_detect,
|
||||
int is_fixed_pattern)
|
||||
{
|
||||
|
||||
int adjust_pri = 0, ext_chan_busy = 0;
|
||||
int pri_margin;
|
||||
|
||||
if (is_fixed_pattern)
|
||||
pri_margin = DFS_DEFAULT_FIXEDPATTERN_PRI_MARGIN;
|
||||
else
|
||||
pri_margin = DFS_DEFAULT_PRI_MARGIN;
|
||||
|
||||
if (IS_CHAN_HT40(dfs->ic->ic_curchan)) {
|
||||
ext_chan_busy = dfs->ic->ic_get_ext_busy(dfs->ic);
|
||||
if (ext_chan_busy >= 0) {
|
||||
dfs->dfs_rinfo.ext_chan_busy_ts =
|
||||
dfs->ic->ic_get_TSF64(dfs->ic);
|
||||
dfs->dfs_rinfo.dfs_ext_chan_busy = ext_chan_busy;
|
||||
} else {
|
||||
/* Check to see if the cached value of ext_chan_busy can be used */
|
||||
ext_chan_busy = 0;
|
||||
if (dfs->dfs_rinfo.dfs_ext_chan_busy) {
|
||||
if (dfs->dfs_rinfo.rn_lastfull_ts <
|
||||
dfs->dfs_rinfo.ext_chan_busy_ts) {
|
||||
ext_chan_busy =
|
||||
dfs->dfs_rinfo.dfs_ext_chan_busy;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
" PRI Use cached copy of ext_chan_busy extchanbusy=%d \n",
|
||||
ext_chan_busy);
|
||||
}
|
||||
}
|
||||
}
|
||||
adjust_pri =
|
||||
adjust_pri_per_chan_busy(ext_chan_busy, pri_margin);
|
||||
|
||||
pri_margin -= adjust_pri;
|
||||
}
|
||||
return pri_margin;
|
||||
}
|
||||
|
||||
/* For the extension channel, if legacy traffic is present, we see a lot of false alarms,
|
||||
so make the thresholds higher depending on the busy % for the extension channel.*/
|
||||
|
||||
int dfs_get_filter_threshold(struct ath_dfs *dfs, struct dfs_filter *rf,
|
||||
int is_extchan_detect)
|
||||
{
|
||||
int ext_chan_busy = 0;
|
||||
int thresh, adjust_thresh = 0;
|
||||
|
||||
thresh = rf->rf_threshold;
|
||||
|
||||
if (IS_CHAN_HT40(dfs->ic->ic_curchan)) {
|
||||
ext_chan_busy = dfs->ic->ic_get_ext_busy(dfs->ic);
|
||||
if (ext_chan_busy >= 0) {
|
||||
dfs->dfs_rinfo.ext_chan_busy_ts =
|
||||
dfs->ic->ic_get_TSF64(dfs->ic);
|
||||
dfs->dfs_rinfo.dfs_ext_chan_busy = ext_chan_busy;
|
||||
} else {
|
||||
/* Check to see if the cached value of ext_chan_busy can be used */
|
||||
ext_chan_busy = 0;
|
||||
if (dfs->dfs_rinfo.dfs_ext_chan_busy) {
|
||||
if (dfs->dfs_rinfo.rn_lastfull_ts <
|
||||
dfs->dfs_rinfo.ext_chan_busy_ts) {
|
||||
ext_chan_busy =
|
||||
dfs->dfs_rinfo.dfs_ext_chan_busy;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
" THRESH Use cached copy of ext_chan_busy extchanbusy=%d rn_lastfull_ts=%llu ext_chan_busy_ts=%llu\n",
|
||||
ext_chan_busy,
|
||||
(unsigned long long)dfs->
|
||||
dfs_rinfo.rn_lastfull_ts,
|
||||
(unsigned long long)dfs->
|
||||
dfs_rinfo.ext_chan_busy_ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
adjust_thresh =
|
||||
adjust_thresh_per_chan_busy(ext_chan_busy, thresh);
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
" filterID=%d extchanbusy=%d adjust_thresh=%d\n",
|
||||
rf->rf_pulseid, ext_chan_busy, adjust_thresh);
|
||||
|
||||
thresh += adjust_thresh;
|
||||
}
|
||||
return thresh;
|
||||
}
|
||||
|
||||
uint32_t dfs_round(int32_t val)
|
||||
{
|
||||
uint32_t ival, rem;
|
||||
|
||||
if (val < 0)
|
||||
return 0;
|
||||
ival = val / 100;
|
||||
rem = val - (ival * 100);
|
||||
if (rem < 50)
|
||||
return ival;
|
||||
else
|
||||
return (ival + 1);
|
||||
}
|
||||
|
||||
struct ieee80211_channel *ieee80211_get_extchan(struct ieee80211com *ic)
|
||||
{
|
||||
int chan_offset = 0;
|
||||
if (IEEE80211_IS_CHAN_HT40PLUS_CAPABLE(ic->ic_curchan)) {
|
||||
chan_offset = 20;
|
||||
} else if (IEEE80211_IS_CHAN_HT40MINUS_CAPABLE(ic->ic_curchan)) {
|
||||
chan_offset = -20;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
return (ic->
|
||||
ic_find_channel(ic, ic->ic_curchan->ic_freq + chan_offset,
|
||||
IEEE80211_CHAN_11NA_HT20));
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the radar state entry that matches the current channel
|
||||
*/
|
||||
struct dfs_state *dfs_getchanstate(struct ath_dfs *dfs, uint8_t *index,
|
||||
int ext_chan_flag)
|
||||
{
|
||||
struct dfs_state *rs = NULL;
|
||||
int i;
|
||||
struct ieee80211_channel *cmp_ch;
|
||||
|
||||
if (dfs == NULL) {
|
||||
printk("%s[%d]: sc_dfs is NULL\n", __func__, __LINE__);
|
||||
/* DFS_DPRINTK(dfs, ATH_DEBUG_DFS,"%s: sc_dfs is NULL\n",__func__); */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ext_chan_flag) {
|
||||
cmp_ch = ieee80211_get_extchan(dfs->ic);
|
||||
if (cmp_ch) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"Extension channel freq = %u flags=0x%x\n",
|
||||
cmp_ch->ic_freq, cmp_ch->ic_flagext);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} else {
|
||||
cmp_ch = dfs->ic->ic_curchan;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"Primary channel freq = %u flags=0x%x\n",
|
||||
cmp_ch->ic_freq, cmp_ch->ic_flagext);
|
||||
}
|
||||
for (i = 0; i < DFS_NUM_RADAR_STATES; i++) {
|
||||
if ((dfs->dfs_radar[i].rs_chan.ic_freq == cmp_ch->ic_freq) &&
|
||||
(dfs->dfs_radar[i].rs_chan.ic_flags == cmp_ch->ic_flags)) {
|
||||
if (index != NULL)
|
||||
*index = (uint8_t) i;
|
||||
return &(dfs->dfs_radar[i]);
|
||||
}
|
||||
}
|
||||
/* No existing channel found, look for first free channel state entry */
|
||||
for (i = 0; i < DFS_NUM_RADAR_STATES; i++) {
|
||||
if (dfs->dfs_radar[i].rs_chan.ic_freq == 0) {
|
||||
rs = &(dfs->dfs_radar[i]);
|
||||
/* Found one, set channel info and default thresholds */
|
||||
rs->rs_chan = *cmp_ch;
|
||||
|
||||
/* Copy the parameters from the default set */
|
||||
ath_dfs_phyerr_param_copy(&rs->rs_param,
|
||||
&dfs->dfs_defaultparams);
|
||||
|
||||
if (index != NULL)
|
||||
*index = (uint8_t) i;
|
||||
return (rs);
|
||||
}
|
||||
}
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2, "%s: No more radar states left.\n",
|
||||
__func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
#endif /* ATH_SUPPORT_DFS */
|
405
core/sap/dfs/src/dfs_nol.c
Normal file
405
core/sap/dfs/src/dfs_nol.c
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2015 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_nol.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>
|
||||
*/
|
||||
#ifndef UNINET
|
||||
/*TO DO DFS
|
||||
#include <ieee80211_channel.h>
|
||||
*/
|
||||
#endif
|
||||
#if defined(ATH_SUPPORT_DFS) && !defined(ATH_DFS_RADAR_DETECTION_ONLY)
|
||||
#include "dfs_ioctl.h"
|
||||
#include "dfs_ioctl_private.h"
|
||||
#include "cdf_time.h" /* cdf_time_t, cdf_system_time_after */
|
||||
|
||||
static void
|
||||
dfs_nol_delete(struct ath_dfs *dfs, uint16_t delfreq, uint16_t delchwidth);
|
||||
|
||||
static os_timer_func(dfs_remove_from_nol)
|
||||
{
|
||||
struct dfs_nol_timer_arg *nol_arg;
|
||||
struct ath_dfs *dfs;
|
||||
struct ieee80211com *ic;
|
||||
uint16_t delfreq;
|
||||
uint16_t delchwidth;
|
||||
|
||||
OS_GET_TIMER_ARG(nol_arg, struct dfs_nol_timer_arg *);
|
||||
|
||||
dfs = nol_arg->dfs;
|
||||
ic = dfs->ic;
|
||||
delfreq = nol_arg->delfreq;
|
||||
delchwidth = nol_arg->delchwidth;
|
||||
|
||||
/* Only operate in HOSTAP/IBSS */
|
||||
if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
|
||||
ic->ic_opmode != IEEE80211_M_IBSS)
|
||||
goto done;
|
||||
|
||||
/* Delete the given NOL entry */
|
||||
dfs_nol_delete(dfs, delfreq, delchwidth);
|
||||
|
||||
/* Update the wireless stack with the new NOL */
|
||||
dfs_nol_update(dfs);
|
||||
|
||||
done:
|
||||
OS_FREE(nol_arg);
|
||||
return;
|
||||
}
|
||||
|
||||
void dfs_print_nol(struct ath_dfs *dfs)
|
||||
{
|
||||
struct dfs_nolelem *nol;
|
||||
uint32_t diff_ms, remaining_sec;
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL, "%s: sc_dfs is NULL",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
nol = dfs->dfs_nol;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL, "%s: NOL", __func__);
|
||||
while (nol != NULL) {
|
||||
diff_ms =
|
||||
cdf_system_ticks_to_msecs(cdf_system_ticks() -
|
||||
nol->nol_start_ticks);
|
||||
diff_ms = (nol->nol_timeout_ms - diff_ms);
|
||||
remaining_sec = diff_ms / 1000; /* convert to seconds */
|
||||
nol = nol->nol_next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dfs_get_nol(struct ath_dfs *dfs, struct dfsreq_nolelem *dfs_nol, int *nchan)
|
||||
{
|
||||
struct dfs_nolelem *nol;
|
||||
|
||||
*nchan = 0;
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL, "%s: sc_dfs is NULL",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
nol = dfs->dfs_nol;
|
||||
while (nol != NULL) {
|
||||
dfs_nol[*nchan].nol_freq = nol->nol_freq;
|
||||
dfs_nol[*nchan].nol_chwidth = nol->nol_chwidth;
|
||||
dfs_nol[*nchan].nol_start_ticks = nol->nol_start_ticks;
|
||||
dfs_nol[*nchan].nol_timeout_ms = nol->nol_timeout_ms;
|
||||
++(*nchan);
|
||||
nol = nol->nol_next;
|
||||
}
|
||||
}
|
||||
|
||||
void dfs_set_nol(struct ath_dfs *dfs, struct dfsreq_nolelem *dfs_nol, int nchan)
|
||||
{
|
||||
#define TIME_IN_MS 1000
|
||||
uint32_t nol_time_left_ms;
|
||||
struct ieee80211_channel chan;
|
||||
int i;
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL, "%s: sc_dfs is NULL",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < nchan; i++) {
|
||||
nol_time_left_ms =
|
||||
cdf_system_ticks_to_msecs(cdf_system_ticks() -
|
||||
dfs_nol[i].nol_start_ticks);
|
||||
if (nol_time_left_ms < dfs_nol[i].nol_timeout_ms) {
|
||||
chan.ic_freq = dfs_nol[i].nol_freq;
|
||||
chan.ic_flags = 0;
|
||||
chan.ic_flagext = 0;
|
||||
nol_time_left_ms =
|
||||
(dfs_nol[i].nol_timeout_ms - nol_time_left_ms);
|
||||
dfs_nol_addchan(dfs, &chan,
|
||||
(nol_time_left_ms / TIME_IN_MS));
|
||||
}
|
||||
}
|
||||
#undef TIME_IN_MS
|
||||
dfs_nol_update(dfs);
|
||||
}
|
||||
|
||||
void
|
||||
dfs_nol_addchan(struct ath_dfs *dfs, struct ieee80211_channel *chan,
|
||||
uint32_t dfs_nol_timeout)
|
||||
{
|
||||
#define TIME_IN_MS 1000
|
||||
#define TIME_IN_US (TIME_IN_MS * 1000)
|
||||
struct dfs_nolelem *nol, *elem, *prev;
|
||||
struct dfs_nol_timer_arg *dfs_nol_arg;
|
||||
/* XXX for now, assume all events are 20MHz wide */
|
||||
int ch_width = 20;
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL, "%s: sc_dfs is NULL",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
nol = dfs->dfs_nol;
|
||||
prev = dfs->dfs_nol;
|
||||
elem = NULL;
|
||||
while (nol != NULL) {
|
||||
if ((nol->nol_freq == chan->ic_freq) &&
|
||||
(nol->nol_chwidth == ch_width)) {
|
||||
nol->nol_start_ticks = cdf_system_ticks();
|
||||
nol->nol_timeout_ms = dfs_nol_timeout * TIME_IN_MS;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL,
|
||||
"%s: Update OS Ticks for NOL %d MHz / %d MHz",
|
||||
__func__, nol->nol_freq, nol->nol_chwidth);
|
||||
OS_CANCEL_TIMER(&nol->nol_timer);
|
||||
OS_SET_TIMER(&nol->nol_timer,
|
||||
dfs_nol_timeout * TIME_IN_MS);
|
||||
return;
|
||||
}
|
||||
prev = nol;
|
||||
nol = nol->nol_next;
|
||||
}
|
||||
/* Add a new element to the NOL */
|
||||
elem =
|
||||
(struct dfs_nolelem *)os_malloc(NULL, sizeof(struct dfs_nolelem),
|
||||
GFP_ATOMIC);
|
||||
if (elem == NULL) {
|
||||
goto bad;
|
||||
}
|
||||
dfs_nol_arg = (struct dfs_nol_timer_arg *)os_malloc(NULL,
|
||||
sizeof(struct
|
||||
dfs_nol_timer_arg),
|
||||
GFP_ATOMIC);
|
||||
if (dfs_nol_arg == NULL) {
|
||||
OS_FREE(elem);
|
||||
goto bad;
|
||||
}
|
||||
elem->nol_freq = chan->ic_freq;
|
||||
elem->nol_chwidth = ch_width;
|
||||
elem->nol_start_ticks = cdf_system_ticks();
|
||||
elem->nol_timeout_ms = dfs_nol_timeout * TIME_IN_MS;
|
||||
elem->nol_next = NULL;
|
||||
if (prev) {
|
||||
prev->nol_next = elem;
|
||||
} else {
|
||||
/* This is the first element in the NOL */
|
||||
dfs->dfs_nol = elem;
|
||||
}
|
||||
dfs_nol_arg->dfs = dfs;
|
||||
dfs_nol_arg->delfreq = elem->nol_freq;
|
||||
dfs_nol_arg->delchwidth = elem->nol_chwidth;
|
||||
|
||||
OS_INIT_TIMER(NULL, &elem->nol_timer, dfs_remove_from_nol,
|
||||
dfs_nol_arg, CDF_TIMER_TYPE_SW);
|
||||
OS_SET_TIMER(&elem->nol_timer, dfs_nol_timeout * TIME_IN_MS);
|
||||
|
||||
/* Update the NOL counter */
|
||||
dfs->dfs_nol_count++;
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL,
|
||||
"%s: new NOL channel %d MHz / %d MHz",
|
||||
__func__, elem->nol_freq, elem->nol_chwidth);
|
||||
return;
|
||||
|
||||
bad:
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL | ATH_DEBUG_DFS,
|
||||
"%s: failed to allocate memory for nol entry", __func__);
|
||||
|
||||
#undef TIME_IN_MS
|
||||
#undef TIME_IN_US
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the given frequency/chwidth from the NOL.
|
||||
*/
|
||||
static void
|
||||
dfs_nol_delete(struct ath_dfs *dfs, uint16_t delfreq, uint16_t delchwidth)
|
||||
{
|
||||
struct dfs_nolelem *nol, **prev_next;
|
||||
|
||||
if (dfs == NULL) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS, "%s: sc_dfs is NULL", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL,
|
||||
"%s: remove channel=%d/%d MHz from NOL",
|
||||
__func__, delfreq, delchwidth);
|
||||
prev_next = &(dfs->dfs_nol);
|
||||
nol = dfs->dfs_nol;
|
||||
while (nol != NULL) {
|
||||
if (nol->nol_freq == delfreq && nol->nol_chwidth == delchwidth) {
|
||||
*prev_next = nol->nol_next;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_NOL,
|
||||
"%s removing channel %d/%dMHz from NOL tstamp=%d",
|
||||
__func__, nol->nol_freq, nol->nol_chwidth,
|
||||
(cdf_system_ticks_to_msecs
|
||||
(cdf_system_ticks()) / 1000));
|
||||
OS_CANCEL_TIMER(&nol->nol_timer);
|
||||
OS_FREE(nol);
|
||||
nol = NULL;
|
||||
nol = *prev_next;
|
||||
|
||||
/* Update the NOL counter */
|
||||
dfs->dfs_nol_count--;
|
||||
|
||||
/* Be paranoid! */
|
||||
if (dfs->dfs_nol_count < 0) {
|
||||
DFS_PRINTK("%s: dfs_nol_count < 0; eek!",
|
||||
__func__);
|
||||
dfs->dfs_nol_count = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
prev_next = &(nol->nol_next);
|
||||
nol = nol->nol_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* notify the driver/umac that it should update the channel radar/NOL
|
||||
* flags based on the current NOL list.
|
||||
*/
|
||||
void dfs_nol_update(struct ath_dfs *dfs)
|
||||
{
|
||||
struct ieee80211com *ic = dfs->ic;
|
||||
struct dfs_nol_chan_entry *dfs_nol;
|
||||
struct dfs_nolelem *nol;
|
||||
int nlen;
|
||||
|
||||
/*
|
||||
* Allocate enough entries to store the NOL.
|
||||
*
|
||||
* At least on Linux (don't ask why), if you allocate a 0 entry
|
||||
* array, the returned pointer is 0x10. Make sure you're
|
||||
* aware of this when you start debugging.
|
||||
*/
|
||||
dfs_nol = (struct dfs_nol_chan_entry *)os_malloc(NULL,
|
||||
sizeof(struct
|
||||
dfs_nol_chan_entry)
|
||||
* dfs->dfs_nol_count,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (dfs_nol == NULL) {
|
||||
/*
|
||||
* XXX TODO: if this fails, just schedule a task to retry
|
||||
* updating the NOL at a later stage. That way the NOL
|
||||
* update _DOES_ happen - hopefully the failure was just
|
||||
* temporary.
|
||||
*/
|
||||
DFS_PRINTK("%s: failed to allocate NOL update memory!",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* populate the nol array */
|
||||
nlen = 0;
|
||||
|
||||
nol = dfs->dfs_nol;
|
||||
while (nol != NULL && nlen < dfs->dfs_nol_count) {
|
||||
dfs_nol[nlen].nol_chfreq = nol->nol_freq;
|
||||
dfs_nol[nlen].nol_chwidth = nol->nol_chwidth;
|
||||
dfs_nol[nlen].nol_start_ticks = nol->nol_start_ticks;
|
||||
dfs_nol[nlen].nol_timeout_ms = nol->nol_timeout_ms;
|
||||
nlen++;
|
||||
nol = nol->nol_next;
|
||||
}
|
||||
|
||||
/* Be suitably paranoid for now */
|
||||
if (nlen != dfs->dfs_nol_count)
|
||||
DFS_PRINTK("%s: nlen (%d) != dfs->dfs_nol_count (%d)!",
|
||||
__func__, nlen, dfs->dfs_nol_count);
|
||||
|
||||
/*
|
||||
* Call the driver layer to have it recalculate the NOL flags for
|
||||
* each driver/umac channel.
|
||||
*
|
||||
* If the list is empty, pass NULL instead of dfs_nol.
|
||||
*
|
||||
* The operating system may have some special representation for
|
||||
* "malloc a 0 byte memory region" - for example,
|
||||
* Linux 2.6.38-13 (ubuntu) returns 0x10 rather than a valid
|
||||
* allocation (and is likely not NULL so the pointer doesn't
|
||||
* match NULL checks in any later code.
|
||||
*/
|
||||
ic->ic_dfs_clist_update(ic, DFS_NOL_CLIST_CMD_UPDATE,
|
||||
(nlen > 0) ? dfs_nol : NULL, nlen);
|
||||
|
||||
/*
|
||||
* .. and we're done.
|
||||
*/
|
||||
OS_FREE(dfs_nol);
|
||||
}
|
||||
|
||||
void dfs_nol_timer_cleanup(struct ath_dfs *dfs)
|
||||
{
|
||||
struct dfs_nolelem *nol = dfs->dfs_nol, *prev;
|
||||
|
||||
/* First Cancel timer */
|
||||
while (nol) {
|
||||
OS_CANCEL_TIMER(&nol->nol_timer);
|
||||
nol = nol->nol_next;
|
||||
}
|
||||
/* Free NOL elem, don't mix this while loop with above loop */
|
||||
nol = dfs->dfs_nol;
|
||||
while (nol) {
|
||||
prev = nol;
|
||||
nol = nol->nol_next;
|
||||
OS_FREE(prev);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif /* defined(ATH_SUPPORT_DFS) && !defined(ATH_DFS_RADAR_DETECTION_ONLY) */
|
50
core/sap/dfs/src/dfs_phyerr.h
Normal file
50
core/sap/dfs/src/dfs_phyerr.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2015 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
|
||||
*
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for
|
||||
* any purpose with or without fee is hereby granted, provided that the
|
||||
* above copyright notice and this permission notice appear in all
|
||||
* copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file was originally distributed by Qualcomm Atheros, Inc.
|
||||
* under proprietary terms before Copyright ownership was assigned
|
||||
* to the Linux Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* dfs_phyerr.h
|
||||
*
|
||||
* OVERVIEW:
|
||||
*
|
||||
* Source code borrowed from QCA_MAIN DFS module
|
||||
*
|
||||
* DEPENDENCIES:
|
||||
*
|
||||
* Are listed for each API below.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __DFS_PHYERR_H__
|
||||
#define __DFS_PHYERR_H__
|
||||
|
||||
extern int dfs_process_phyerr_bb_tlv(struct ath_dfs *dfs, void *buf,
|
||||
uint16_t datalen, uint8_t rssi,
|
||||
uint8_t ext_rssi, uint32_t rs_tstamp,
|
||||
uint64_t fulltsf, struct dfs_phy_err *e,
|
||||
bool enable_log);
|
||||
|
||||
#endif /* __DFS_PHYERR_H__ */
|
726
core/sap/dfs/src/dfs_phyerr_tlv.c
Normal file
726
core/sap/dfs/src/dfs_phyerr_tlv.c
Normal file
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2015 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.
|
||||
*/
|
||||
|
||||
#include "dfs.h"
|
||||
/* TO DO DFS
|
||||
#include <ieee80211_var.h>
|
||||
*/
|
||||
/* TO DO DFS
|
||||
#include <ieee80211_channel.h>
|
||||
*/
|
||||
#include "dfs_phyerr.h"
|
||||
#include "dfs_phyerr_tlv.h"
|
||||
|
||||
/*
|
||||
* Parsed radar status
|
||||
*/
|
||||
struct rx_radar_status {
|
||||
uint32_t raw_tsf;
|
||||
uint32_t tsf_offset;
|
||||
int rssi;
|
||||
int pulse_duration;
|
||||
int is_chirp:1;
|
||||
int delta_peak;
|
||||
int delta_diff;
|
||||
int sidx;
|
||||
int freq_offset; /* in KHz */
|
||||
};
|
||||
|
||||
struct rx_search_fft_report {
|
||||
uint32_t total_gain_db;
|
||||
uint32_t base_pwr_db;
|
||||
int fft_chn_idx;
|
||||
int peak_sidx;
|
||||
int relpwr_db;
|
||||
int avgpwr_db;
|
||||
int peak_mag;
|
||||
int num_str_bins_ib;
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX until "fastclk" is stored in the DFS configuration..
|
||||
*/
|
||||
#define PERE_IS_OVERSAMPLING(_dfs) (1)
|
||||
|
||||
/*
|
||||
* XXX there _has_ to be a better way of doing this!
|
||||
* -adrian
|
||||
*/
|
||||
static int32_t sign_extend_32(uint32_t v, int nb)
|
||||
{
|
||||
uint32_t m = 1U << (nb - 1);
|
||||
|
||||
/* Chop off high bits, just in case */
|
||||
v &= v & ((1U << nb) - 1);
|
||||
|
||||
/* Extend */
|
||||
return (v ^ m) - m;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the frequency offset from the given signed bin index
|
||||
* from the radar summary report.
|
||||
*
|
||||
* This takes the oversampling mode into account.
|
||||
*
|
||||
* For oversampling, each bin has resolution 44MHz/128.
|
||||
* For non-oversampling, each bin has resolution 40MHz/128.
|
||||
*
|
||||
* It returns kHz - ie, 1000th's of MHz.
|
||||
*/
|
||||
static int calc_freq_offset(int sindex, int is_oversampling)
|
||||
{
|
||||
|
||||
if (is_oversampling)
|
||||
return sindex * (44000 / 128);
|
||||
else
|
||||
return sindex * (40000 / 128);
|
||||
}
|
||||
|
||||
static void
|
||||
radar_summary_print(struct ath_dfs *dfs,
|
||||
struct rx_radar_status *rsu,
|
||||
bool enable_log)
|
||||
{
|
||||
if (!enable_log)
|
||||
return;
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"\n ############ Radar Summary ############\n");
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - pulsedur = %d micro seconds\n", __func__,
|
||||
rsu->pulse_duration);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - rssi = %d dbm\n", __func__,
|
||||
rsu->rssi);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - ischirp = %d\n", __func__,
|
||||
rsu->is_chirp);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - sidx = %d\n", __func__,
|
||||
rsu->sidx);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - delta_peak = %d\n", __func__,
|
||||
rsu->delta_peak);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - delta_diff = %d\n", __func__,
|
||||
rsu->delta_diff);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - raw tsf = %d\n", __func__,
|
||||
rsu->raw_tsf);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - tsf_offset = %d micro seconds\n",
|
||||
__func__, rsu->tsf_offset);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Radar Summary - cooked tsf = %d\n", __func__,
|
||||
(rsu->raw_tsf - rsu->tsf_offset));
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: frequency offset = %d.%d MHz (oversampling = %d)\n",
|
||||
__func__, (int) (rsu->freq_offset / 1000),
|
||||
(int) abs(rsu->freq_offset % 1000),
|
||||
PERE_IS_OVERSAMPLING(dfs));
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"\n ###################################\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the radar summary frame.
|
||||
*
|
||||
* The frame contents _minus_ the TLV are passed in.
|
||||
*/
|
||||
static void
|
||||
radar_summary_parse(struct ath_dfs *dfs, const char *buf, size_t len,
|
||||
struct rx_radar_status *rsu)
|
||||
{
|
||||
uint32_t rs[2];
|
||||
int freq_centre, freq;
|
||||
|
||||
/* Drop out if we have < 2 DWORDs available */
|
||||
if (len < sizeof(rs)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR |
|
||||
ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: len (%zu) < expected (%zu)!",
|
||||
__func__, len, sizeof(rs));
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the TLVs may be unaligned for some reason
|
||||
* we take a private copy into aligned memory.
|
||||
* This enables us to use the HAL-like accessor macros
|
||||
* into the DWORDs to access sub-DWORD fields.
|
||||
*/
|
||||
OS_MEMCPY(rs, buf, sizeof(rs));
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s: two 32 bit values are: %08x %08x", __func__, rs[0],
|
||||
rs[1]);
|
||||
/* DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s (p=%p):", __func__, buf); */
|
||||
|
||||
/* Populate the fields from the summary report */
|
||||
rsu->tsf_offset =
|
||||
MS(rs[RADAR_REPORT_PULSE_REG_2], RADAR_REPORT_PULSE_TSF_OFFSET);
|
||||
rsu->pulse_duration =
|
||||
MS(rs[RADAR_REPORT_PULSE_REG_2], RADAR_REPORT_PULSE_DUR);
|
||||
rsu->is_chirp =
|
||||
MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_PULSE_IS_CHIRP);
|
||||
rsu->sidx =
|
||||
sign_extend_32(MS
|
||||
(rs[RADAR_REPORT_PULSE_REG_1],
|
||||
RADAR_REPORT_PULSE_SIDX), 10);
|
||||
rsu->freq_offset =
|
||||
calc_freq_offset(rsu->sidx, PERE_IS_OVERSAMPLING(dfs));
|
||||
|
||||
/* These are only relevant if the pulse is a chirp */
|
||||
rsu->delta_peak =
|
||||
sign_extend_32(MS
|
||||
(rs[RADAR_REPORT_PULSE_REG_1],
|
||||
RADAR_REPORT_PULSE_DELTA_PEAK), 6);
|
||||
rsu->delta_diff =
|
||||
MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_PULSE_DELTA_DIFF);
|
||||
|
||||
/* WAR for FCC Type 4 */
|
||||
/*
|
||||
* HW is giving longer pulse duration (in case of VHT80, with traffic)
|
||||
* which fails to detect FCC type4 radar pulses. Added a work around to
|
||||
* fix the pulse duration and duration delta.
|
||||
*
|
||||
* IF VHT80
|
||||
* && (primary_channel==30MHz || primary_channel== -30MHz)
|
||||
* && -4 <= pulse_index <= 4
|
||||
* && !chirp
|
||||
* && pulse duration > 20 us
|
||||
* THEN
|
||||
* Set pulse duration to 20 us
|
||||
*/
|
||||
|
||||
freq = ieee80211_chan2freq(dfs->ic, dfs->ic->ic_curchan);
|
||||
freq_centre = dfs->ic->ic_curchan->ic_vhtop_ch_freq_seg1;
|
||||
|
||||
if ((IEEE80211_IS_CHAN_11AC_VHT80(dfs->ic->ic_curchan) &&
|
||||
(abs(freq - freq_centre) == 30) &&
|
||||
!rsu->is_chirp &&
|
||||
abs(rsu->sidx) <= 4 && rsu->pulse_duration > 20)) {
|
||||
rsu->pulse_duration = 20;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
radar_fft_search_report_parse(struct ath_dfs *dfs, const char *buf, size_t len,
|
||||
struct rx_search_fft_report *rsfr)
|
||||
{
|
||||
uint32_t rs[2];
|
||||
|
||||
/* Drop out if we have < 2 DWORDs available */
|
||||
if (len < sizeof(rs)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR |
|
||||
ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: len (%zu) < expected (%zu)!",
|
||||
__func__, len, sizeof(rs));
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the TLVs may be unaligned for some reason
|
||||
* we take a private copy into aligned memory.
|
||||
* This enables us to use the HAL-like accessor macros
|
||||
* into the DWORDs to access sub-DWORD fields.
|
||||
*/
|
||||
OS_MEMCPY(rs, buf, sizeof(rs));
|
||||
rsfr->total_gain_db =
|
||||
MS(rs[SEARCH_FFT_REPORT_REG_1],
|
||||
SEARCH_FFT_REPORT_TOTAL_GAIN_DB);
|
||||
rsfr->base_pwr_db =
|
||||
MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_BASE_PWR_DB);
|
||||
rsfr->fft_chn_idx =
|
||||
MS(rs[SEARCH_FFT_REPORT_REG_1], SEARCH_FFT_REPORT_FFT_CHN_IDX);
|
||||
rsfr->peak_sidx =
|
||||
sign_extend_32(MS
|
||||
(rs[SEARCH_FFT_REPORT_REG_1],
|
||||
SEARCH_FFT_REPORT_PEAK_SIDX), 12);
|
||||
rsfr->relpwr_db =
|
||||
MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_RELPWR_DB);
|
||||
rsfr->avgpwr_db =
|
||||
MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_AVGPWR_DB);
|
||||
rsfr->peak_mag =
|
||||
MS(rs[SEARCH_FFT_REPORT_REG_2], SEARCH_FFT_REPORT_PEAK_MAG);
|
||||
rsfr->num_str_bins_ib =
|
||||
MS(rs[SEARCH_FFT_REPORT_REG_2],
|
||||
SEARCH_FFT_REPORT_NUM_STR_BINS_IB);
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s: two 32 bit values are: %08x %08x", __func__, rs[0],
|
||||
rs[1]);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: rsfr->total_gain_db = %d",
|
||||
__func__, rsfr->total_gain_db);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: rsfr->base_pwr_db = %d",
|
||||
__func__, rsfr->base_pwr_db);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: rsfr->fft_chn_idx = %d",
|
||||
__func__, rsfr->fft_chn_idx);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: rsfr->peak_sidx = %d",
|
||||
__func__, rsfr->peak_sidx);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: rsfr->relpwr_db = %d",
|
||||
__func__, rsfr->relpwr_db);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: rsfr->avgpwr_db = %d",
|
||||
__func__, rsfr->avgpwr_db);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: rsfr->peak_mag = %d",
|
||||
__func__, rsfr->peak_mag);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: rsfr->num_str_bins_ib = %d",
|
||||
__func__, rsfr->num_str_bins_ib);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a Peregrine BB TLV frame.
|
||||
*
|
||||
* This routine parses each TLV, prints out what's going on and
|
||||
* calls an appropriate sub-function.
|
||||
*
|
||||
* Since the TLV format doesn't _specify_ all TLV components are
|
||||
* DWORD aligned, we must treat them as not and access the fields
|
||||
* appropriately.
|
||||
*/
|
||||
static int
|
||||
tlv_parse_frame(struct ath_dfs *dfs, struct rx_radar_status *rs,
|
||||
struct rx_search_fft_report *rsfr, const char *buf, size_t len,
|
||||
uint8_t rssi)
|
||||
{
|
||||
int i = 0;
|
||||
uint32_t tlv_hdr[1];
|
||||
bool first_tlv = true;
|
||||
bool false_detect = false;
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s: total length = %zu bytes", __func__, len);
|
||||
while ((i < len) && (false_detect == false)) {
|
||||
/* Ensure we at least have four bytes */
|
||||
if ((len - i) < sizeof(tlv_hdr)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR |
|
||||
ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: ran out of bytes, len=%zu, i=%d",
|
||||
__func__, len, i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the offset into the header,
|
||||
* so the DWORD style access macros
|
||||
* can be used.
|
||||
*/
|
||||
OS_MEMCPY(&tlv_hdr, buf + i, sizeof(tlv_hdr));
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s: HDR: TLV SIG=0x%x, TAG=0x%x, LEN=%d bytes",
|
||||
__func__,
|
||||
MS(tlv_hdr[TLV_REG], TLV_SIG),
|
||||
MS(tlv_hdr[TLV_REG], TLV_TAG),
|
||||
MS(tlv_hdr[TLV_REG], TLV_LEN));
|
||||
|
||||
/*
|
||||
* Sanity check the length field is available in
|
||||
* the remaining frame. Drop out if this isn't
|
||||
* the case - we can't trust the rest of the TLV
|
||||
* entries.
|
||||
*/
|
||||
if (MS(tlv_hdr[TLV_REG], TLV_LEN) + i >= len) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s: TLV oversize: LEN=%d, avail=%zu, i=%d",
|
||||
__func__,
|
||||
MS(tlv_hdr[TLV_REG], TLV_LEN), len, i);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Skip the TLV header - one DWORD */
|
||||
i += sizeof(tlv_hdr);
|
||||
|
||||
/* Handle the payload */
|
||||
/* XXX TODO! */
|
||||
switch (MS(tlv_hdr[TLV_REG], TLV_SIG)) {
|
||||
case TAG_ID_RADAR_PULSE_SUMMARY: /* Radar pulse summary */
|
||||
/* XXX TODO verify return value */
|
||||
/* XXX TODO validate only seeing one of these */
|
||||
radar_summary_parse(dfs, buf + i,
|
||||
MS(tlv_hdr[TLV_REG], TLV_LEN), rs);
|
||||
break;
|
||||
case TAG_ID_SEARCH_FFT_REPORT:
|
||||
radar_fft_search_report_parse(dfs, buf + i,
|
||||
MS(tlv_hdr[TLV_REG],
|
||||
TLV_LEN), rsfr);
|
||||
/*
|
||||
* we examine search FFT report and make the following
|
||||
* assumption as per algorithms group's input:
|
||||
* (1) There may be multiple TLV
|
||||
* (2) We make false detection decison solely based on
|
||||
* the first TLV
|
||||
* (3) If the first TLV is a serch FFT report then we
|
||||
* check the peak_mag value. When RSSI is equal to
|
||||
* dfs->ath_dfs_false_rssI_thres (default 50) and
|
||||
* peak_mag is less than 2 * dfs->ath_dfs_peak_mag
|
||||
* (default 40) we treat it as false detect.
|
||||
* Please note that 50 is not a true RSSI estimate,
|
||||
* but value indicated by HW for RF saturation event.
|
||||
*/
|
||||
|
||||
if ((first_tlv == true) &&
|
||||
(rssi == dfs->ath_dfs_false_rssi_thres) &&
|
||||
(rsfr->peak_mag < (2 * dfs->ath_dfs_peak_mag))) {
|
||||
false_detect = true;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s: setting false_detect to true",
|
||||
__func__);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s: unknown entry, SIG=0x%02x",
|
||||
__func__, MS(tlv_hdr[TLV_REG], TLV_SIG));
|
||||
}
|
||||
|
||||
/* Skip the payload */
|
||||
i += MS(tlv_hdr[TLV_REG], TLV_LEN);
|
||||
first_tlv = false;
|
||||
}
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR, "%s: done", __func__);
|
||||
|
||||
return false_detect ? 0 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the channel centre in MHz.
|
||||
*/
|
||||
static int tlv_calc_freq_info(struct ath_dfs *dfs, struct rx_radar_status *rs)
|
||||
{
|
||||
uint32_t chan_centre;
|
||||
uint32_t chan_width;
|
||||
int chan_offset;
|
||||
|
||||
/*
|
||||
* For now, just handle up to VHT80 correctly.
|
||||
*/
|
||||
if (dfs->ic == NULL || dfs->ic->ic_curchan == NULL) {
|
||||
DFS_PRINTK("%s: dfs->ic=%p, that or curchan is null?",
|
||||
__func__, dfs->ic);
|
||||
return 0;
|
||||
/*
|
||||
* For now, the only 11ac channel with freq1/freq2 setup is
|
||||
* VHT80.
|
||||
*
|
||||
* XXX should have a flag macro to check this!
|
||||
*/
|
||||
} else if (IEEE80211_IS_CHAN_11AC_VHT80(dfs->ic->ic_curchan)) {
|
||||
/* 11AC, so cfreq1/cfreq2 are setup */
|
||||
|
||||
/*
|
||||
* XXX if it's 80+80 this won't work - need to use seg
|
||||
* appropriately!
|
||||
*/
|
||||
|
||||
chan_centre = dfs->ic->ic_curchan->ic_vhtop_ch_freq_seg1;
|
||||
} else {
|
||||
/* HT20/HT40 */
|
||||
|
||||
/*
|
||||
* XXX this is hard-coded - it should be 5 or 10 for
|
||||
* half/quarter appropriately.
|
||||
*/
|
||||
chan_width = 20;
|
||||
|
||||
/* Grab default channel centre */
|
||||
chan_centre = ieee80211_chan2freq(dfs->ic, dfs->ic->ic_curchan);
|
||||
|
||||
/* Calculate offset based on HT40U/HT40D and VHT40U/VHT40D */
|
||||
if (IEEE80211_IS_CHAN_11N_HT40PLUS(dfs->ic->ic_curchan) ||
|
||||
dfs->ic->ic_curchan->ic_flags & IEEE80211_CHAN_VHT40PLUS)
|
||||
chan_offset = chan_width;
|
||||
else if (IEEE80211_IS_CHAN_11N_HT40MINUS(dfs->ic->ic_curchan) ||
|
||||
dfs->ic->ic_curchan->
|
||||
ic_flags & IEEE80211_CHAN_VHT40MINUS)
|
||||
chan_offset = -chan_width;
|
||||
else
|
||||
chan_offset = 0;
|
||||
|
||||
/* Calculate new _real_ channel centre */
|
||||
chan_centre += (chan_offset / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX half/quarter rate support!
|
||||
*/
|
||||
|
||||
/* Return ev_chan_centre in MHz */
|
||||
return chan_centre;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the centre frequency and low/high range for a radar pulse event.
|
||||
*
|
||||
* XXX TODO: Handle half/quarter rates correctly!
|
||||
* XXX TODO: handle VHT160 correctly!
|
||||
* XXX TODO: handle VHT80+80 correctly!
|
||||
*/
|
||||
static int
|
||||
tlv_calc_event_freq_pulse(struct ath_dfs *dfs, struct rx_radar_status *rs,
|
||||
uint32_t *freq_centre, uint32_t *freq_lo,
|
||||
uint32_t *freq_hi)
|
||||
{
|
||||
int chan_width;
|
||||
int chan_centre;
|
||||
|
||||
/* Fetch the channel centre frequency in MHz */
|
||||
chan_centre = tlv_calc_freq_info(dfs, rs);
|
||||
|
||||
/* Convert to KHz */
|
||||
chan_centre *= 1000;
|
||||
|
||||
/*
|
||||
* XXX hard-code event width to be 2 * bin size for now;
|
||||
* XXX this needs to take into account the core clock speed
|
||||
* XXX for half/quarter rate mode.
|
||||
*/
|
||||
if (PERE_IS_OVERSAMPLING(dfs))
|
||||
chan_width = (44000 * 2 / 128);
|
||||
else
|
||||
chan_width = (40000 * 2 / 128);
|
||||
|
||||
/* XXX adjust chan_width for half/quarter rate! */
|
||||
|
||||
/*
|
||||
* Now we can do the math to figure out the correct channel range.
|
||||
*/
|
||||
(*freq_centre) = (uint32_t) (chan_centre + rs->freq_offset);
|
||||
(*freq_lo) = (uint32_t) ((chan_centre + rs->freq_offset)
|
||||
- chan_width);
|
||||
(*freq_hi) = (uint32_t) ((chan_centre + rs->freq_offset)
|
||||
+ chan_width);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The chirp bandwidth in KHz is defined as:
|
||||
*
|
||||
* totalBW(KHz) = delta_peak(mean)
|
||||
* * [ (bin resolution in KHz) / (radar_fft_long_period in uS) ]
|
||||
* * pulse_duration (us)
|
||||
*
|
||||
* The bin resolution depends upon oversampling.
|
||||
*
|
||||
* For now, we treat the radar_fft_long_period as a hard-coded 8uS.
|
||||
*/
|
||||
static int
|
||||
tlv_calc_event_freq_chirp(struct ath_dfs *dfs, struct rx_radar_status *rs,
|
||||
uint32_t *freq_centre, uint32_t *freq_lo,
|
||||
uint32_t *freq_hi)
|
||||
{
|
||||
int32_t bin_resolution; /* KHz * 100 */
|
||||
int32_t radar_fft_long_period = 8; /* microseconds */
|
||||
int32_t delta_peak;
|
||||
int32_t pulse_duration;
|
||||
int32_t total_bw;
|
||||
int32_t chan_centre;
|
||||
int32_t freq_1, freq_2;
|
||||
|
||||
/*
|
||||
* KHz isn't enough resolution here!
|
||||
* So treat it as deci-hertz (10Hz) and convert back to KHz
|
||||
* later.
|
||||
*/
|
||||
if (PERE_IS_OVERSAMPLING(dfs))
|
||||
bin_resolution = (44000 * 100) / 128;
|
||||
else
|
||||
bin_resolution = (40000 * 100) / 128;
|
||||
|
||||
delta_peak = rs->delta_peak;
|
||||
pulse_duration = rs->pulse_duration;
|
||||
|
||||
total_bw = delta_peak * (bin_resolution / radar_fft_long_period) *
|
||||
pulse_duration;
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR | ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: delta_peak=%d, pulse_duration=%d, bin_resolution=%d.%dKHz, "
|
||||
"radar_fft_long_period=%d, total_bw=%d.%ldKHz",
|
||||
__func__,
|
||||
delta_peak,
|
||||
pulse_duration,
|
||||
bin_resolution / 1000,
|
||||
bin_resolution % 1000,
|
||||
radar_fft_long_period, total_bw / 100, abs(total_bw % 100));
|
||||
|
||||
total_bw /= 100; /* back to KHz */
|
||||
|
||||
/* Grab the channel centre frequency in MHz */
|
||||
chan_centre = tlv_calc_freq_info(dfs, rs);
|
||||
|
||||
/* Early abort! */
|
||||
if (chan_centre == 0) {
|
||||
(*freq_centre) = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert to KHz */
|
||||
chan_centre *= 1000;
|
||||
|
||||
/*
|
||||
* sidx is the starting frequency; total_bw is a signed value and
|
||||
* for negative chirps (ie, moving down in frequency rather than up)
|
||||
* the end frequency may be less than the start frequency.
|
||||
*/
|
||||
if (total_bw > 0) {
|
||||
freq_1 = chan_centre + rs->freq_offset;
|
||||
freq_2 = chan_centre + rs->freq_offset + total_bw;
|
||||
} else {
|
||||
freq_1 = chan_centre + rs->freq_offset + total_bw;
|
||||
freq_2 = chan_centre + rs->freq_offset;
|
||||
}
|
||||
|
||||
(*freq_lo) = (uint32_t) (freq_1);
|
||||
(*freq_hi) = (uint32_t) (freq_2);
|
||||
(*freq_centre) = (uint32_t) (freq_1 + (abs(total_bw) / 2));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the centre and band edge frequencies of the given radar
|
||||
* event.
|
||||
*/
|
||||
static int
|
||||
tlv_calc_event_freq(struct ath_dfs *dfs, struct rx_radar_status *rs,
|
||||
uint32_t *freq_centre, uint32_t *freq_lo,
|
||||
uint32_t *freq_hi)
|
||||
{
|
||||
if (rs->is_chirp)
|
||||
return tlv_calc_event_freq_chirp(dfs, rs, freq_centre,
|
||||
freq_lo, freq_hi);
|
||||
else
|
||||
return tlv_calc_event_freq_pulse(dfs, rs, freq_centre,
|
||||
freq_lo, freq_hi);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the public facing function which parses the PHY error
|
||||
* and populates the dfs_phy_err struct.
|
||||
*/
|
||||
int
|
||||
dfs_process_phyerr_bb_tlv(struct ath_dfs *dfs, void *buf, uint16_t datalen,
|
||||
uint8_t rssi, uint8_t ext_rssi, uint32_t rs_tstamp,
|
||||
uint64_t fulltsf, struct dfs_phy_err *e,
|
||||
bool enable_log)
|
||||
{
|
||||
struct rx_radar_status rs;
|
||||
struct rx_search_fft_report rsfr;
|
||||
static int invalid_phyerr_count;
|
||||
|
||||
OS_MEMZERO(&rs, sizeof(rs));
|
||||
|
||||
/*
|
||||
* Add the ppdu_start/ppdu_end fields given to us by the upper
|
||||
* layers. The firmware gives us a summary set of parameters rather
|
||||
* than the whole PPDU_START/PPDU_END descriptor contenst.
|
||||
*/
|
||||
rs.rssi = rssi;
|
||||
rs.raw_tsf = rs_tstamp;
|
||||
/*
|
||||
* Try parsing the TLV set.
|
||||
*/
|
||||
if (!tlv_parse_frame(dfs, &rs, &rsfr, buf, datalen, rssi)) {
|
||||
invalid_phyerr_count++;
|
||||
/*
|
||||
* Print only at every 2 power times
|
||||
* to avoid flushing of the kernel
|
||||
* logs, since the frequency of
|
||||
* invalid phyerrors is very high
|
||||
* in noisy environments.
|
||||
*/
|
||||
if (!(invalid_phyerr_count & 0xFF)) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_DEBUG,
|
||||
"%s[%d]:parse failed invalid phyerror cnt = %d",
|
||||
__func__, __LINE__, invalid_phyerr_count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* For debugging, print what we have parsed */
|
||||
radar_summary_print(dfs, &rs, enable_log);
|
||||
|
||||
/* Populate dfs_phy_err from rs */
|
||||
OS_MEMSET(e, 0, sizeof(*e));
|
||||
e->rssi = rs.rssi;
|
||||
e->dur = rs.pulse_duration;
|
||||
e->sidx = rs.sidx;
|
||||
e->is_pri = 1; /* XXX always PRI for now */
|
||||
e->is_ext = 0;
|
||||
e->is_dc = 0;
|
||||
e->is_early = 0;
|
||||
/*
|
||||
* XXX TODO: add a "chirp detection enabled" capability or config
|
||||
* bit somewhere, in case for some reason the hardware chirp
|
||||
* detection AND FFTs are disabled.
|
||||
*/
|
||||
/* For now, assume this hardware always does chirp detection */
|
||||
e->do_check_chirp = 1;
|
||||
e->is_hw_chirp = !!(rs.is_chirp);
|
||||
e->is_sw_chirp = 0; /* We don't yet do software chirp checking */
|
||||
|
||||
e->fulltsf = fulltsf;
|
||||
e->rs_tstamp = rs.raw_tsf - rs.tsf_offset;
|
||||
|
||||
/* XXX error check */
|
||||
(void)tlv_calc_event_freq(dfs, &rs, &e->freq, &e->freq_lo, &e->freq_hi);
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: fbin=%d, freq=%d.%d MHz, raw tsf=%u, offset=%d, "
|
||||
"cooked tsf=%u, rssi=%d, dur=%d, is_chirp=%d, fulltsf=%llu, "
|
||||
"freq=%d.%d MHz, freq_lo=%d.%dMHz, freq_hi=%d.%d MHz",
|
||||
__func__,
|
||||
rs.sidx,
|
||||
(int)(rs.freq_offset / 1000),
|
||||
(int)abs(rs.freq_offset % 1000),
|
||||
rs.raw_tsf,
|
||||
rs.tsf_offset,
|
||||
e->rs_tstamp,
|
||||
rs.rssi,
|
||||
rs.pulse_duration,
|
||||
(int)rs.is_chirp,
|
||||
(unsigned long long)fulltsf,
|
||||
(int)e->freq / 1000,
|
||||
(int)abs(e->freq) % 1000,
|
||||
(int)e->freq_lo / 1000,
|
||||
(int)abs(e->freq_lo) % 1000,
|
||||
(int)e->freq_hi / 1000, (int)abs(e->freq_hi) % 1000);
|
||||
|
||||
return 1;
|
||||
}
|
206
core/sap/dfs/src/dfs_phyerr_tlv.h
Normal file
206
core/sap/dfs/src/dfs_phyerr_tlv.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2015 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
|
||||
*
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for
|
||||
* any purpose with or without fee is hereby granted, provided that the
|
||||
* above copyright notice and this permission notice appear in all
|
||||
* copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file was originally distributed by Qualcomm Atheros, Inc.
|
||||
* under proprietary terms before Copyright ownership was assigned
|
||||
* to the Linux Foundation.
|
||||
*/
|
||||
|
||||
/*===========================================================================
|
||||
|
||||
dfs_phyerr_tlv.h
|
||||
|
||||
OVERVIEW:
|
||||
|
||||
Source code borrowed from QCA_MAIN DFS module
|
||||
|
||||
DEPENDENCIES:
|
||||
|
||||
Are listed for each API below.
|
||||
|
||||
===========================================================================*/
|
||||
|
||||
#ifndef __DFS_PHYERR_PEREGRINE_H__
|
||||
#define __DFS_PHYERR_PEREGRINE_H__
|
||||
|
||||
/*
|
||||
* Register manipulation macros that expect bit field defines
|
||||
* to follow the convention that an _S suffix is appended for
|
||||
* a shift count, while the field mask has no suffix.
|
||||
*/
|
||||
#define SM(_v, _f) (((_v) << _f ## _S) & _f)
|
||||
#define MS(_v, _f) (((_v) & _f) >> _f ## _S)
|
||||
|
||||
/*
|
||||
* The TLV dword is at the beginning of each TLV section.
|
||||
*/
|
||||
#define TLV_REG 0x00
|
||||
|
||||
#define TLV_LEN 0x0000FFFF
|
||||
#define TLV_LEN_S 0
|
||||
|
||||
#define TLV_SIG 0x00FF0000
|
||||
#define TLV_SIG_S 16
|
||||
|
||||
#define TLV_TAG 0xFF000000
|
||||
#define TLV_TAG_S 24
|
||||
|
||||
#define TAG_ID_SEARCH_FFT_REPORT 0xFB
|
||||
#define TAG_ID_RADAR_PULSE_SUMMARY 0xF8
|
||||
/*
|
||||
* Radar pulse summary
|
||||
*
|
||||
* + TYPE=0xF8 (Radar pulse summary reprot)
|
||||
* + SIG=0xBB (baseband PHY generated TLV components)
|
||||
*/
|
||||
|
||||
#define RADAR_REPORT_PULSE_REG_1 0x00
|
||||
|
||||
#define RADAR_REPORT_PULSE_IS_CHIRP 0x80000000
|
||||
#define RADAR_REPORT_PULSE_IS_CHIRP_S 31
|
||||
|
||||
#define RADAR_REPORT_PULSE_IS_MAX_WIDTH 0x40000000
|
||||
#define RADAR_REPORT_PULSE_IS_MAX_WIDTH_S 30
|
||||
|
||||
#define RADAR_REPORT_AGC_TOTAL_GAIN 0x3FF00000
|
||||
#define RADAR_REPORT_AGC_TOTAL_GAIN_S 20
|
||||
|
||||
#define RADAR_REPORT_PULSE_DELTA_DIFF 0x000F0000
|
||||
#define RADAR_REPORT_PULSE_DELTA_DIFF_S 16
|
||||
|
||||
#define RADAR_REPORT_PULSE_DELTA_PEAK 0x0000FC00
|
||||
#define RADAR_REPORT_PULSE_DELTA_PEAK_S 10
|
||||
|
||||
#define RADAR_REPORT_PULSE_SIDX 0x000003FF
|
||||
#define RADAR_REPORT_PULSE_SIDX_S 0x0
|
||||
|
||||
#define RADAR_REPORT_PULSE_REG_2 0x01
|
||||
|
||||
#define RADAR_REPORT_PULSE_SRCH_FFT_A_VALID 0x80000000
|
||||
#define RADAR_REPORT_PULSE_SRCH_FFT_A_VALID_S 31
|
||||
|
||||
#define RADAR_REPORT_PULSE_AGC_MB_GAIN 0x7F000000
|
||||
#define RADAR_REPORT_PULSE_AGC_MB_GAIN_S 24
|
||||
|
||||
#define RADAR_REPORT_PULSE_SUBCHAN_MASK 0x00FF0000
|
||||
#define RADAR_REPORT_PULSE_SUBCHAN_MASK_S 16
|
||||
|
||||
#define RADAR_REPORT_PULSE_TSF_OFFSET 0x0000FF00
|
||||
#define RADAR_REPORT_PULSE_TSF_OFFSET_S 8
|
||||
|
||||
#define RADAR_REPORT_PULSE_DUR 0x000000FF
|
||||
#define RADAR_REPORT_PULSE_DUR_S 0
|
||||
|
||||
#define SEARCH_FFT_REPORT_REG_1 0x00
|
||||
|
||||
#define SEARCH_FFT_REPORT_TOTAL_GAIN_DB 0xFF800000
|
||||
#define SEARCH_FFT_REPORT_TOTAL_GAIN_DB_S 23
|
||||
|
||||
#define SEARCH_FFT_REPORT_BASE_PWR_DB 0x007FC000
|
||||
#define SEARCH_FFT_REPORT_BASE_PWR_DB_S 14
|
||||
|
||||
#define SEARCH_FFT_REPORT_FFT_CHN_IDX 0x00003000
|
||||
#define SEARCH_FFT_REPORT_FFT_CHN_IDX_S 12
|
||||
|
||||
#define SEARCH_FFT_REPORT_PEAK_SIDX 0x00000FFF
|
||||
#define SEARCH_FFT_REPORT_PEAK_SIDX_S 0
|
||||
|
||||
#define SEARCH_FFT_REPORT_REG_2 0x01
|
||||
|
||||
#define SEARCH_FFT_REPORT_RELPWR_DB 0xFC000000
|
||||
#define SEARCH_FFT_REPORT_RELPWR_DB_S 26
|
||||
|
||||
#define SEARCH_FFT_REPORT_AVGPWR_DB 0x03FC0000
|
||||
#define SEARCH_FFT_REPORT_AVGPWR_DB_S 18
|
||||
|
||||
#define SEARCH_FFT_REPORT_PEAK_MAG 0x0003FF00
|
||||
#define SEARCH_FFT_REPORT_PEAK_MAG_S 8
|
||||
|
||||
#define SEARCH_FFT_REPORT_NUM_STR_BINS_IB 0x000000FF
|
||||
#define SEARCH_FFT_REPORT_NUM_STR_BINS_IB_S 0
|
||||
|
||||
/*
|
||||
* Although this code is now not parsing the whole frame (descriptor
|
||||
* and all), the relevant fields are still useful information
|
||||
* for anyone who is working on the PHY error part of DFS pattern
|
||||
* matching.
|
||||
*
|
||||
* However, to understand _where_ these descriptors start, you
|
||||
* should do some digging into the peregrine descriptor format.
|
||||
* The 30 second version: each RX ring has a bitmap listing which
|
||||
* descriptors are to be included, and then a set of offsets
|
||||
* into the RX buffer for where each descriptor will be written.
|
||||
* It's not like the 802.11n generation hardware which has
|
||||
* a fixed descriptor format.
|
||||
*/
|
||||
|
||||
/*
|
||||
* RX_PPDU_START
|
||||
*/
|
||||
#define RX_PPDU_START_LEN (10*4)
|
||||
|
||||
#define RX_PPDU_START_REG_4 0x0004
|
||||
#define RX_PPDU_START_RSSI_COMB 0x000000FF
|
||||
#define RX_PPDU_START_RSSI_COMB_S 0
|
||||
|
||||
/*
|
||||
* RX_PPDU_END
|
||||
*/
|
||||
#define RX_PPDU_END_LEN (21*4)
|
||||
|
||||
#define RX_PPDU_END_REG_16 16
|
||||
#define RX_PPDU_END_TSF_TIMESTAMP 0xFFFFFFFF
|
||||
#define RX_PPDU_END_TSF_TIMESTAMP_S 0
|
||||
|
||||
#define RX_PPDU_END_REG_18 18
|
||||
#define RX_PPDU_END_PHY_ERR_CODE 0x0000FF00
|
||||
#define RX_PPDU_END_PHY_ERR_CODE_S 8
|
||||
#define RX_PPDU_END_PHY_ERR 0x00010000
|
||||
#define RX_PPDU_END_PHY_ERR_S 16
|
||||
|
||||
/*
|
||||
* The RSSI values can have "special meanings".
|
||||
*
|
||||
* If rssi=50, it means that the peak detector triggered.
|
||||
*/
|
||||
#define RSSI_PEAK_DETECTOR_SAT 50
|
||||
|
||||
/*
|
||||
*
|
||||
* If rssi=25, it means that the ADC was saturated, but that only is
|
||||
* valid when there is one ADC gain change. For short pulses this
|
||||
* is true - you won't have time to do a gain change before the pulse
|
||||
* goes away. But for longer pulses, ADC gain changes can occur, so
|
||||
* you'll get a more accurate RSSI figure.
|
||||
*
|
||||
* For short pulses (and the definition of "short" still isn't clear
|
||||
* at the time of writing) there isn't any real time to do a gain change
|
||||
* (or two, or three..) in order to get an accurate estimation of signal
|
||||
* sizing. Thus, RSSI will not be very accurate for short duration pulses.
|
||||
* All you can really say for certain is that yes, there's a pulse that
|
||||
* met the requirements of the pulse detector.
|
||||
*
|
||||
* For more information, see the 802.11ac Microarchitecture guide.
|
||||
* (TODO: add a twiki reference.)
|
||||
*/
|
||||
|
||||
#endif /* __DFS_PHYERR_PEREGRINE_H__ */
|
855
core/sap/dfs/src/dfs_process_phyerr.c
Normal file
855
core/sap/dfs/src/dfs_process_phyerr.c
Normal file
@@ -0,0 +1,855 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2015 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.
|
||||
*/
|
||||
|
||||
#include "dfs.h"
|
||||
#include "dfs_phyerr.h" /* For chip-specific phyerr func declarations */
|
||||
/* TO DO DFS
|
||||
#include <ieee80211_var.h>
|
||||
*/
|
||||
#ifndef UNINET
|
||||
/* TO DO DFS
|
||||
#include <ieee80211_channel.h>
|
||||
*/
|
||||
#endif
|
||||
#ifdef ATH_SUPPORT_DFS
|
||||
|
||||
/*
|
||||
* Return the frequency width for the current operating channel.
|
||||
*
|
||||
* This isn't the channel width - it's how wide the reported event
|
||||
* may be. For HT20 this is 20MHz. For HT40 on Howl and later it'll
|
||||
* still be 20MHz - the hardware returns either pri or ext channel.
|
||||
*/
|
||||
static inline int dfs_get_event_freqwidth(struct ath_dfs *dfs)
|
||||
{
|
||||
|
||||
/* Handle edge cases during startup/transition, shouldn't happen! */
|
||||
if (dfs == NULL)
|
||||
return 0;
|
||||
if (dfs->ic == NULL || dfs->ic->ic_curchan == NULL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* XXX For now, assume 20MHz wide - but this is incorrect when
|
||||
* operating in half/quarter mode!
|
||||
*/
|
||||
return 20;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the centre frequency for the current operating channel and
|
||||
* event.
|
||||
*
|
||||
* This is for post-Owl 11n chips which report pri/extension channel
|
||||
* events.
|
||||
*/
|
||||
static inline uint16_t
|
||||
dfs_get_event_freqcentre(struct ath_dfs *dfs, int is_pri, int is_ext, int is_dc)
|
||||
{
|
||||
struct ieee80211com *ic;
|
||||
int chan_offset = 0, chan_width;
|
||||
|
||||
/* Handle edge cases during startup/transition, shouldn't happen! */
|
||||
if (dfs == NULL)
|
||||
return 0;
|
||||
if (dfs->ic == NULL || dfs->ic->ic_curchan == NULL)
|
||||
return 0;
|
||||
|
||||
ic = dfs->ic;
|
||||
|
||||
/*
|
||||
*
|
||||
* For wide channels, DC and ext frequencies need a bit of hand-holding
|
||||
* based on whether it's an upper or lower channel.
|
||||
*/
|
||||
chan_width = dfs_get_event_freqwidth(dfs);
|
||||
|
||||
if (IEEE80211_IS_CHAN_11N_HT40PLUS(ic->ic_curchan))
|
||||
chan_offset = chan_width;
|
||||
else if (IEEE80211_IS_CHAN_11N_HT40MINUS(ic->ic_curchan))
|
||||
chan_offset = -chan_width;
|
||||
else
|
||||
chan_offset = 0;
|
||||
|
||||
/*
|
||||
* Check for DC events first - the sowl code may just set all
|
||||
* the bits together..
|
||||
*/
|
||||
if (is_dc) {
|
||||
/*
|
||||
* XXX TODO: Should DC events be considered 40MHz wide here?
|
||||
*/
|
||||
return ieee80211_chan2freq(ic, ic->ic_curchan) +
|
||||
(chan_offset / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* For non-wide channels, the centre frequency is just ic_freq.
|
||||
* The centre frequency for pri events is still ic_freq.
|
||||
*/
|
||||
if (is_pri)
|
||||
return ieee80211_chan2freq(ic, ic->ic_curchan);
|
||||
|
||||
if (is_ext)
|
||||
return ieee80211_chan2freq(ic, ic->ic_curchan) + chan_width;
|
||||
|
||||
/* XXX shouldn't get here */
|
||||
return ieee80211_chan2freq(ic, ic->ic_curchan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process an Owl-style phy error.
|
||||
*
|
||||
* Return 1 on success or 0 on failure.
|
||||
*/
|
||||
int
|
||||
dfs_process_phyerr_owl(struct ath_dfs *dfs, void *buf, uint16_t datalen,
|
||||
uint8_t rssi, uint8_t ext_rssi, uint32_t rs_tstamp,
|
||||
uint64_t fulltsf, struct dfs_phy_err *e)
|
||||
{
|
||||
const char *cbuf = (const char *)buf;
|
||||
uint8_t dur;
|
||||
int event_width;
|
||||
|
||||
/* XXX this shouldn't be kept count here */
|
||||
dfs->ath_dfs_stats.owl_phy_errors++;
|
||||
|
||||
/*
|
||||
* HW cannot detect extension channel radar so it only passes us
|
||||
* primary channel radar data
|
||||
*/
|
||||
if (datalen == 0)
|
||||
dur = 0;
|
||||
else
|
||||
dur = ((uint8_t *) cbuf)[0];
|
||||
|
||||
/*
|
||||
* This is a spurious event; toss.
|
||||
*/
|
||||
if (rssi == 0 && dur == 0)
|
||||
dfs->ath_dfs_stats.datalen_discards++;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Fill out dfs_phy_err with the information we have
|
||||
* at hand.
|
||||
*/
|
||||
OS_MEMSET(e, 0, sizeof(*e));
|
||||
e->rssi = rssi;
|
||||
e->dur = dur;
|
||||
e->is_pri = 1;
|
||||
e->is_ext = 0;
|
||||
e->is_dc = 0;
|
||||
e->is_early = 1;
|
||||
e->fulltsf = fulltsf;
|
||||
e->rs_tstamp = rs_tstamp;
|
||||
|
||||
/*
|
||||
* Owl only ever reports events on the primary channel;
|
||||
* it doesn't even see events on the secondary channel.
|
||||
*/
|
||||
event_width = dfs_get_event_freqwidth(dfs);
|
||||
e->freq = dfs_get_event_freqcentre(dfs, 1, 0, 0) * 1000;
|
||||
e->freq_lo = e->freq - (event_width / 2) * 1000;
|
||||
e->freq_hi = e->freq + (event_width / 2) * 1000;
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: rssi=%u dur=%u,freq=%dMHz, freq_lo=%dMHz, freq_hi=%dMHz\n",
|
||||
__func__, rssi, dur, e->freq / 1000, e->freq_lo / 1000,
|
||||
e->freq_hi / 1000);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a Sowl/Howl style phy error.
|
||||
*/
|
||||
int
|
||||
dfs_process_phyerr_sowl(struct ath_dfs *dfs, void *buf, uint16_t datalen,
|
||||
uint8_t rssi, uint8_t ext_rssi, uint32_t rs_tstamp,
|
||||
uint64_t fulltsf, struct dfs_phy_err *e)
|
||||
{
|
||||
#define EXT_CH_RADAR_FOUND 0x02
|
||||
#define PRI_CH_RADAR_FOUND 0x01
|
||||
#define EXT_CH_RADAR_EARLY_FOUND 0x04
|
||||
const char *cbuf = (const char *)buf;
|
||||
uint8_t dur = 0;
|
||||
uint8_t pulse_bw_info, pulse_length_ext, pulse_length_pri;
|
||||
int pri_found = 0, ext_found = 0;
|
||||
int early_ext = 0;
|
||||
int event_width;
|
||||
|
||||
/*
|
||||
* If radar can be detected on the extension channel, datalen zero
|
||||
* pulses are bogus, discard them.
|
||||
*/
|
||||
if (!datalen) {
|
||||
dfs->ath_dfs_stats.datalen_discards++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ensure that we have at least three bytes of payload */
|
||||
if (datalen < 3) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"%s: short error frame (%d bytes)\n",
|
||||
__func__, datalen);
|
||||
dfs->ath_dfs_stats.datalen_discards++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch the payload directly - the compiler will happily generate
|
||||
* byte-read instructions with a const char * cbuf pointer.
|
||||
*/
|
||||
pulse_length_pri = cbuf[datalen - 3];
|
||||
pulse_length_ext = cbuf[datalen - 2];
|
||||
pulse_bw_info = cbuf[datalen - 1];
|
||||
|
||||
/*
|
||||
* Only the last 3 bits of the BW info are relevant, they indicate
|
||||
* which channel the radar was detected in.
|
||||
*/
|
||||
pulse_bw_info &= 0x07;
|
||||
|
||||
/*
|
||||
* If pulse on DC, both primary and extension flags will be set
|
||||
*/
|
||||
if (((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
|
||||
(pulse_bw_info & PRI_CH_RADAR_FOUND))) {
|
||||
/*
|
||||
* Conducted testing, when pulse is on DC, both
|
||||
* pri and ext durations are reported to be same.
|
||||
*
|
||||
* Radiated testing, when pulse is on DC, different
|
||||
* pri and ext durations are reported, so take the
|
||||
* larger of the two
|
||||
*/
|
||||
if (pulse_length_ext >= pulse_length_pri) {
|
||||
dur = pulse_length_ext;
|
||||
ext_found = 1;
|
||||
} else {
|
||||
dur = pulse_length_pri;
|
||||
pri_found = 1;
|
||||
}
|
||||
dfs->ath_dfs_stats.dc_phy_errors++;
|
||||
} else {
|
||||
if (pulse_bw_info & EXT_CH_RADAR_FOUND) {
|
||||
dur = pulse_length_ext;
|
||||
pri_found = 0;
|
||||
ext_found = 1;
|
||||
dfs->ath_dfs_stats.ext_phy_errors++;
|
||||
}
|
||||
if (pulse_bw_info & PRI_CH_RADAR_FOUND) {
|
||||
dur = pulse_length_pri;
|
||||
pri_found = 1;
|
||||
ext_found = 0;
|
||||
dfs->ath_dfs_stats.pri_phy_errors++;
|
||||
}
|
||||
if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) {
|
||||
dur = pulse_length_ext;
|
||||
pri_found = 0;
|
||||
ext_found = 1;
|
||||
early_ext = 1;
|
||||
dfs->ath_dfs_stats.early_ext_phy_errors++;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"EARLY ext channel dur=%u rssi=%u datalen=%d\n",
|
||||
dur, rssi, datalen);
|
||||
}
|
||||
if (!pulse_bw_info) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"dur=%u rssi=%u bw_info=0x%x datalen = %d\n",
|
||||
dur, rssi, pulse_bw_info, (datalen & 0x3));
|
||||
/*
|
||||
* Bogus bandwidth info received in descriptor,
|
||||
* so ignore this PHY error
|
||||
*/
|
||||
dfs->ath_dfs_stats.bwinfo_errors++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Always use combined RSSI reported, unless RSSI reported on
|
||||
* extension is stronger
|
||||
*/
|
||||
if ((ext_rssi > rssi) && (ext_rssi < 128))
|
||||
rssi = ext_rssi;
|
||||
|
||||
/*
|
||||
* Fill out the rssi/duration fields from above.
|
||||
*/
|
||||
OS_MEMSET(e, 0, sizeof(*e));
|
||||
e->rssi = rssi;
|
||||
e->dur = dur;
|
||||
e->is_pri = pri_found;
|
||||
e->is_ext = ext_found;
|
||||
e->is_dc = !!(((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
|
||||
(pulse_bw_info & PRI_CH_RADAR_FOUND)));
|
||||
e->is_early = early_ext;
|
||||
e->fulltsf = fulltsf;
|
||||
e->rs_tstamp = rs_tstamp;
|
||||
|
||||
/*
|
||||
* Sowl and later can report pri/ext events.
|
||||
*/
|
||||
event_width = dfs_get_event_freqwidth(dfs);
|
||||
e->freq = dfs_get_event_freqcentre(dfs, e->is_pri, e->is_ext, e->is_dc)
|
||||
* 1000;
|
||||
e->freq_lo = e->freq - (event_width / 2) * 1000;
|
||||
e->freq_hi = e->freq + (event_width / 2) * 1000;
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR_SUM,
|
||||
"%s: pulse_bw_info=0x%x pulse_length_ext=%u pulse_length_pri=%u "
|
||||
"rssi=%u ext_rssi=%u, freq=%d MHz, freq_lo=%d MHz, "
|
||||
"freq_hi=%d MHz\n",
|
||||
__func__,
|
||||
pulse_bw_info,
|
||||
pulse_length_ext,
|
||||
pulse_length_pri,
|
||||
rssi,
|
||||
ext_rssi,
|
||||
e->freq / 1000, e->freq_lo / 1000, e->freq_hi / 1000);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a Merlin/Osprey style phy error.
|
||||
*/
|
||||
int
|
||||
dfs_process_phyerr_merlin(struct ath_dfs *dfs, void *buf,
|
||||
uint16_t datalen, uint8_t rssi, uint8_t ext_rssi,
|
||||
uint32_t rs_tstamp, uint64_t fulltsf,
|
||||
struct dfs_phy_err *e)
|
||||
{
|
||||
const char *cbuf = (const char *)buf;
|
||||
uint8_t pulse_bw_info = 0;
|
||||
|
||||
/*
|
||||
* Process using the sowl code
|
||||
*/
|
||||
if (!dfs_process_phyerr_sowl(dfs, buf, datalen, rssi, ext_rssi,
|
||||
rs_tstamp, fulltsf, e)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For osprey (and Merlin) bw_info has implication for selecting
|
||||
* RSSI value. So re-fetch the bw_info field so the RSSI values
|
||||
* can be appropriately overridden.
|
||||
*/
|
||||
pulse_bw_info = cbuf[datalen - 1];
|
||||
|
||||
switch (pulse_bw_info & 0x03) {
|
||||
case 0x00:
|
||||
/* No radar in ctrl or ext channel */
|
||||
rssi = 0;
|
||||
break;
|
||||
case 0x01:
|
||||
/* radar in ctrl channel */
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"RAW RSSI: rssi=%u ext_rssi=%u\n", rssi, ext_rssi);
|
||||
if (ext_rssi >= (rssi + 3)) {
|
||||
/*
|
||||
* cannot use ctrl channel RSSI if
|
||||
* extension channel is stronger
|
||||
*/
|
||||
rssi = 0;
|
||||
}
|
||||
break;
|
||||
case 0x02:
|
||||
/* radar in extension channel */
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"RAW RSSI: rssi=%u ext_rssi=%u\n", rssi, ext_rssi);
|
||||
if (rssi >= (ext_rssi + 12)) {
|
||||
/*
|
||||
* cannot use extension channel RSSI if control channel
|
||||
* is stronger
|
||||
*/
|
||||
rssi = 0;
|
||||
} else {
|
||||
rssi = ext_rssi;
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
/* when both are present use stronger one */
|
||||
if (rssi < ext_rssi)
|
||||
rssi = ext_rssi;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Override the rssi decision made by the sowl code.
|
||||
* The rest of the fields (duration, timestamp, etc)
|
||||
* are left untouched.
|
||||
*/
|
||||
e->rssi = rssi;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void dump_phyerr_contents(const char *d, int len)
|
||||
{
|
||||
#ifdef CONFIG_ENABLE_DUMP_PHYERR_CONTENTS
|
||||
int i, n, bufsize = 64;
|
||||
|
||||
/*
|
||||
* This is statically sized for a 4-digit address + 16 * 2 digit
|
||||
* data string.
|
||||
*
|
||||
* It's done so the printk() passed to the kernel is an entire
|
||||
* line, so the kernel logging code will atomically print it.
|
||||
* Otherwise we'll end up with interleaved lines with output
|
||||
* from other kernel threads.
|
||||
*/
|
||||
char buf[64];
|
||||
|
||||
/* Initial conditions */
|
||||
buf[0] = '\n';
|
||||
n = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i % 16 == 0)
|
||||
n += snprintf(buf + n, bufsize - n, "%04x: ", i);
|
||||
n += snprintf(buf + n, bufsize - n, "%02x ", d[i] & 0xff);
|
||||
if (i % 16 == 15) {
|
||||
n = 0;
|
||||
buf[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the final line if we didn't print it above.
|
||||
*/
|
||||
if (n != 0)
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO, "%s: %s\n",
|
||||
__func__, buf);
|
||||
#endif /* def CONFIG_ENABLE_DUMP_PHYERR_CONTENTS */
|
||||
}
|
||||
|
||||
void
|
||||
dfs_process_phyerr(struct ieee80211com *ic, void *buf, uint16_t datalen,
|
||||
uint8_t r_rssi, uint8_t r_ext_rssi, uint32_t r_rs_tstamp,
|
||||
uint64_t r_fulltsf, bool enable_log)
|
||||
{
|
||||
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
|
||||
struct ieee80211_channel *chan = ic->ic_curchan;
|
||||
struct dfs_event *event;
|
||||
struct dfs_phy_err e;
|
||||
int empty;
|
||||
|
||||
if (dfs == NULL) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s: sc_dfs is NULL\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
dfs->dfs_phyerr_count++;
|
||||
dump_phyerr_contents(buf, datalen);
|
||||
/*
|
||||
* XXX The combined_rssi_ok support has been removed.
|
||||
* This was only clear for Owl.
|
||||
*
|
||||
* XXX TODO: re-add this; it requires passing in the ctl/ext
|
||||
* RSSI set from the RX status descriptor.
|
||||
*
|
||||
* XXX TODO TODO: this may be done for us from the legacy
|
||||
* phy error path in ath_dev; please review that code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* At this time we have a radar pulse that we need to examine and
|
||||
* queue. But if dfs_process_radarevent already detected radar and set
|
||||
* CHANNEL_INTERFERENCE flag then do not queue any more radar data.
|
||||
* When we are in a new channel this flag will be clear and we will
|
||||
* start queueing data for new channel. (EV74162)
|
||||
*/
|
||||
if (dfs->dfs_debug_mask & ATH_DEBUG_DFS_PHYERR_PKT)
|
||||
dump_phyerr_contents(buf, datalen);
|
||||
|
||||
if (chan == NULL) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s: chan is NULL\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IEEE80211_IS_CHAN_RADAR(chan)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"%s: Radar already found in the channel, "
|
||||
" do not queue radar data\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
dfs->ath_dfs_stats.total_phy_errors++;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"%s[%d] phyerr %d len %d\n",
|
||||
__func__, __LINE__,
|
||||
dfs->ath_dfs_stats.total_phy_errors, datalen);
|
||||
|
||||
/*
|
||||
* hardware stores this as 8 bit signed value.
|
||||
* we will cap it at 0 if it is a negative number
|
||||
*/
|
||||
if (r_rssi & 0x80)
|
||||
r_rssi = 0;
|
||||
|
||||
if (r_ext_rssi & 0x80)
|
||||
r_ext_rssi = 0;
|
||||
|
||||
OS_MEMSET(&e, 0, sizeof(e));
|
||||
|
||||
/*
|
||||
* This is a bit evil - instead of just passing in
|
||||
* the chip version, the existing code uses a set
|
||||
* of HAL capability bits to determine what is
|
||||
* possible.
|
||||
*
|
||||
* The way I'm decoding it is thus:
|
||||
*
|
||||
* + DFS enhancement? Merlin or later
|
||||
* + DFS extension channel? Sowl or later. (Howl?)
|
||||
* + otherwise, Owl (and legacy.)
|
||||
*/
|
||||
if (dfs->dfs_caps.ath_chip_is_bb_tlv) {
|
||||
if (dfs_process_phyerr_bb_tlv(dfs, buf, datalen, r_rssi,
|
||||
r_ext_rssi, r_rs_tstamp,
|
||||
r_fulltsf, &e,
|
||||
enable_log) == 0) {
|
||||
dfs->dfs_phyerr_reject_count++;
|
||||
return;
|
||||
} else {
|
||||
if (dfs->dfs_phyerr_freq_min > e.freq)
|
||||
dfs->dfs_phyerr_freq_min = e.freq;
|
||||
|
||||
if (dfs->dfs_phyerr_freq_max < e.freq)
|
||||
dfs->dfs_phyerr_freq_max = e.freq;
|
||||
}
|
||||
} else if (dfs->dfs_caps.ath_dfs_use_enhancement) {
|
||||
if (dfs_process_phyerr_merlin(dfs, buf, datalen, r_rssi,
|
||||
r_ext_rssi, r_rs_tstamp,
|
||||
r_fulltsf, &e) == 0) {
|
||||
return;
|
||||
}
|
||||
} else if (dfs->dfs_caps.ath_dfs_ext_chan_ok) {
|
||||
if (dfs_process_phyerr_sowl(dfs, buf, datalen, r_rssi,
|
||||
r_ext_rssi, r_rs_tstamp, r_fulltsf,
|
||||
&e) == 0) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (dfs_process_phyerr_owl(dfs, buf, datalen, r_rssi,
|
||||
r_ext_rssi, r_rs_tstamp, r_fulltsf,
|
||||
&e) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"\n %s: Frequency at which the phyerror was injected = %d",
|
||||
__func__, e.freq);
|
||||
/*
|
||||
* If the hardware supports radar reporting on the extension channel
|
||||
* it will supply FFT data for longer radar pulses.
|
||||
*
|
||||
* TLV chips don't go through this software check - the hardware
|
||||
* check should be enough. If we want to do software checking
|
||||
* later on then someone will have to craft an FFT parser
|
||||
* suitable for the TLV FFT data format.
|
||||
*/
|
||||
if ((!dfs->dfs_caps.ath_chip_is_bb_tlv) &&
|
||||
dfs->dfs_caps.ath_dfs_ext_chan_ok) {
|
||||
/*
|
||||
* HW has a known issue with chirping pulses injected at or
|
||||
* around DC in 40MHz mode. Such pulses are reported with
|
||||
* much lower durations and SW then discards them because
|
||||
* they do not fit the minimum bin5 pulse duration.
|
||||
*
|
||||
* To work around this issue, if a pulse is within a 10us
|
||||
* range of the bin5 min duration, check if the pulse is
|
||||
* chirping. If the pulse is chirping, bump up the duration
|
||||
* to the minimum bin5 duration.
|
||||
*
|
||||
* This makes sure that a valid chirping pulse will not be
|
||||
* discarded because of incorrect low duration.
|
||||
*
|
||||
* TBD - Is it possible to calculate the 'real' duration of
|
||||
* the pulse using the slope of the FFT data?
|
||||
*
|
||||
* TBD - Use FFT data to differentiate between radar pulses
|
||||
* and false PHY errors.
|
||||
* This will let us reduce the number of false alarms seen.
|
||||
*
|
||||
* BIN 5 chirping pulses are only for FCC or Japan MMK4 domain
|
||||
*/
|
||||
if (((dfs->dfsdomain == DFS_FCC_DOMAIN) ||
|
||||
(dfs->dfsdomain == DFS_MKK4_DOMAIN)) &&
|
||||
(e.dur >= MAYBE_BIN5_DUR) && (e.dur < MAX_BIN5_DUR)) {
|
||||
int add_dur;
|
||||
int slope = 0, dc_found = 0;
|
||||
|
||||
/*
|
||||
* Set the event chirping flags; as we're doing
|
||||
* an actual chirp check.
|
||||
*/
|
||||
e.do_check_chirp = 1;
|
||||
e.is_hw_chirp = 0;
|
||||
e.is_sw_chirp = 0;
|
||||
|
||||
/*
|
||||
* dfs_check_chirping() expects is_pri and is_ext
|
||||
* to be '1' for true and '0' for false for now,
|
||||
* as the function itself uses these values in
|
||||
* constructing things rather than testing them
|
||||
* for 'true' or 'false'.
|
||||
*/
|
||||
add_dur = dfs_check_chirping(dfs, buf, datalen,
|
||||
(e.is_pri ? 1 : 0),
|
||||
(e.is_ext ? 1 : 0),
|
||||
&slope, &dc_found);
|
||||
if (add_dur) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"old dur %d slope =%d\n", e.dur,
|
||||
slope);
|
||||
e.is_sw_chirp = 1;
|
||||
/* bump up to a random bin5 pulse duration */
|
||||
if (e.dur < MIN_BIN5_DUR) {
|
||||
e.dur = dfs_get_random_bin5_dur(dfs,
|
||||
e.fulltsf);
|
||||
}
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"new dur %d\n", e.dur);
|
||||
} else {
|
||||
/* set the duration so that it is rejected */
|
||||
e.is_sw_chirp = 0;
|
||||
e.dur = MAX_BIN5_DUR + 100;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"is_chirping = %d dur=%d\n",
|
||||
add_dur, e.dur);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* We have a pulse that is either bigger than
|
||||
* MAX_BIN5_DUR or * less than MAYBE_BIN5_DUR
|
||||
*/
|
||||
if ((dfs->dfsdomain == DFS_FCC_DOMAIN) ||
|
||||
(dfs->dfsdomain == DFS_MKK4_DOMAIN)) {
|
||||
/*
|
||||
* XXX Would this result in very large pulses
|
||||
* wrapping around to become short pulses?
|
||||
*/
|
||||
if (e.dur >= MAX_BIN5_DUR) {
|
||||
/*
|
||||
* set the duration so that it is
|
||||
* rejected
|
||||
*/
|
||||
e.dur = MAX_BIN5_DUR + 50;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the parsed, checked and filtered entry to the radar pulse
|
||||
* event list. This is then checked by dfs_radar_processevent().
|
||||
*
|
||||
* XXX TODO: some filtering is still done below this point - fix
|
||||
* XXX this!
|
||||
*/
|
||||
ATH_DFSEVENTQ_LOCK(dfs);
|
||||
empty = STAILQ_EMPTY(&(dfs->dfs_eventq));
|
||||
ATH_DFSEVENTQ_UNLOCK(dfs);
|
||||
if (empty) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the channel is a turbo G channel, then the event is
|
||||
* for the adaptive radio (AR) pattern matching rather than
|
||||
* radar detection.
|
||||
*/
|
||||
if ((chan->ic_flags & CHANNEL_108G) == CHANNEL_108G) {
|
||||
if (!(dfs->dfs_proc_phyerr & DFS_AR_EN)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"%s: DFS_AR_EN not enabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
ATH_DFSEVENTQ_LOCK(dfs);
|
||||
event = STAILQ_FIRST(&(dfs->dfs_eventq));
|
||||
if (event == NULL) {
|
||||
ATH_DFSEVENTQ_UNLOCK(dfs);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"%s: no more events space left\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
STAILQ_REMOVE_HEAD(&(dfs->dfs_eventq), re_list);
|
||||
ATH_DFSEVENTQ_UNLOCK(dfs);
|
||||
event->re_rssi = e.rssi;
|
||||
event->re_dur = e.dur;
|
||||
event->re_full_ts = e.fulltsf;
|
||||
event->re_ts = (e.rs_tstamp) & DFS_TSMASK;
|
||||
event->re_chanindex = dfs->dfs_curchan_radindex;
|
||||
event->re_flags = 0;
|
||||
event->sidx = e.sidx;
|
||||
|
||||
/*
|
||||
* Handle chirp flags.
|
||||
*/
|
||||
if (e.do_check_chirp) {
|
||||
event->re_flags |= DFS_EVENT_CHECKCHIRP;
|
||||
if (e.is_hw_chirp)
|
||||
event->re_flags |= DFS_EVENT_HW_CHIRP;
|
||||
if (e.is_sw_chirp)
|
||||
event->re_flags |= DFS_EVENT_SW_CHIRP;
|
||||
}
|
||||
|
||||
ATH_ARQ_LOCK(dfs);
|
||||
STAILQ_INSERT_TAIL(&(dfs->dfs_arq), event, re_list);
|
||||
ATH_ARQ_UNLOCK(dfs);
|
||||
} else {
|
||||
if (IEEE80211_IS_CHAN_DFS(chan)) {
|
||||
if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS3,
|
||||
"%s: DFS_RADAR_EN not enabled\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* rssi is not accurate for short pulses, so do
|
||||
* not filter based on that for short duration pulses
|
||||
*
|
||||
* XXX do this filtering above?
|
||||
*/
|
||||
if (dfs->dfs_caps.ath_dfs_ext_chan_ok) {
|
||||
if ((e.rssi < dfs->dfs_rinfo.rn_minrssithresh &&
|
||||
(e.dur > 4)) ||
|
||||
e.dur > (dfs->dfs_rinfo.rn_maxpulsedur)) {
|
||||
dfs->ath_dfs_stats.rssi_discards++;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"Extension channel pulse is "
|
||||
"discarded: dur=%d, "
|
||||
"maxpulsedur=%d, rssi=%d, "
|
||||
"minrssi=%d\n",
|
||||
e.dur,
|
||||
dfs->dfs_rinfo.
|
||||
rn_maxpulsedur, e.rssi,
|
||||
dfs->dfs_rinfo.
|
||||
rn_minrssithresh);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (e.rssi < dfs->dfs_rinfo.rn_minrssithresh ||
|
||||
e.dur > dfs->dfs_rinfo.rn_maxpulsedur) {
|
||||
/* XXX TODO add a debug statement? */
|
||||
dfs->ath_dfs_stats.rssi_discards++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the event to the list, if there's space.
|
||||
*/
|
||||
ATH_DFSEVENTQ_LOCK(dfs);
|
||||
event = STAILQ_FIRST(&(dfs->dfs_eventq));
|
||||
if (event == NULL) {
|
||||
ATH_DFSEVENTQ_UNLOCK(dfs);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
|
||||
"%s: no more events space left\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
STAILQ_REMOVE_HEAD(&(dfs->dfs_eventq), re_list);
|
||||
ATH_DFSEVENTQ_UNLOCK(dfs);
|
||||
|
||||
dfs->dfs_phyerr_queued_count++;
|
||||
dfs->dfs_phyerr_w53_counter++;
|
||||
|
||||
event->re_dur = e.dur;
|
||||
event->re_full_ts = e.fulltsf;
|
||||
event->re_ts = (e.rs_tstamp) & DFS_TSMASK;
|
||||
event->re_rssi = e.rssi;
|
||||
event->sidx = e.sidx;
|
||||
|
||||
/*
|
||||
* Handle chirp flags.
|
||||
*/
|
||||
if (e.do_check_chirp) {
|
||||
event->re_flags |= DFS_EVENT_CHECKCHIRP;
|
||||
if (e.is_hw_chirp)
|
||||
event->re_flags |= DFS_EVENT_HW_CHIRP;
|
||||
if (e.is_sw_chirp)
|
||||
event->re_flags |= DFS_EVENT_SW_CHIRP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Correctly set which channel is being reported on
|
||||
*/
|
||||
if (e.is_pri) {
|
||||
event->re_chanindex = dfs->dfs_curchan_radindex;
|
||||
} else {
|
||||
if (dfs->dfs_extchan_radindex == -1) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s - phyerr on ext channel\n",
|
||||
__func__);
|
||||
}
|
||||
event->re_chanindex = dfs->dfs_extchan_radindex;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
|
||||
"%s New extension channel event is added "
|
||||
"to queue\n", __func__);
|
||||
}
|
||||
ATH_DFSQ_LOCK(dfs);
|
||||
STAILQ_INSERT_TAIL(&(dfs->dfs_radarq), event, re_list);
|
||||
ATH_DFSQ_UNLOCK(dfs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule the radar/AR task as appropriate.
|
||||
*
|
||||
* XXX isn't a lock needed for ath_radar_tasksched?
|
||||
*/
|
||||
|
||||
/*
|
||||
* Commenting out the dfs_process_ar_event() since the function is never
|
||||
* called at run time as dfs_arq will be empty and the function
|
||||
* dfs_process_ar_event is obsolete and function definition is removed
|
||||
* as part of dfs_ar.c file
|
||||
*
|
||||
* if (!STAILQ_EMPTY(&dfs->dfs_arq))
|
||||
* // XXX shouldn't this be a task/timer too?
|
||||
* dfs_process_ar_event(dfs, ic->ic_curchan);
|
||||
*/
|
||||
|
||||
if (!STAILQ_EMPTY(&dfs->dfs_radarq) && !dfs->ath_radar_tasksched) {
|
||||
dfs->ath_radar_tasksched = 1;
|
||||
OS_SET_TIMER(&dfs->ath_dfs_task_timer, 0);
|
||||
}
|
||||
#undef EXT_CH_RADAR_FOUND
|
||||
#undef PRI_CH_RADAR_FOUND
|
||||
#undef EXT_CH_RADAR_EARLY_FOUND
|
||||
}
|
||||
#endif /* ATH_SUPPORT_DFS */
|
799
core/sap/dfs/src/dfs_process_radarevent.c
Normal file
799
core/sap/dfs/src/dfs_process_radarevent.c
Normal file
@@ -0,0 +1,799 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2015 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
|
||||
|
||||
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 uint8_t dfs_process_pulse_dur(struct ath_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.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 (uint8_t) dfs_round((int32_t) ((dfs->dur_multiplier) * re_dur));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
/* commenting for now to validate radar indication msg to SAP */
|
||||
/* #if 0 */
|
||||
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;
|
||||
uint32_t tabledepth, index;
|
||||
uint64_t deltafull_ts = 0, this_ts, deltaT;
|
||||
struct ieee80211_channel *thischan;
|
||||
struct dfs_pulseline *pl;
|
||||
static uint32_t test_ts = 0;
|
||||
static uint32_t diff_ts = 0;
|
||||
int ext_chan_event_flag = 0;
|
||||
#if 0
|
||||
int pri_multiplier = 2;
|
||||
#endif
|
||||
int i;
|
||||
|
||||
if (dfs == NULL) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_ERROR,
|
||||
"%s[%d]: dfs is NULL", __func__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
pl = dfs->pulses;
|
||||
if (!(IEEE80211_IS_CHAN_DFS(dfs->ic->ic_curchan))) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"%s: radar event on non-DFS chan", __func__);
|
||||
dfs_reset_radarq(dfs);
|
||||
dfs_reset_alldelaylines(dfs);
|
||||
return 0;
|
||||
}
|
||||
#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)) {
|
||||
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;
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_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;
|
||||
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 +=
|
||||
(((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;
|
||||
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;
|
||||
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 =
|
||||
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 | ((uint64_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;
|
||||
diff_ts = (uint32_t) this_ts - test_ts;
|
||||
test_ts = (uint32_t) this_ts;
|
||||
DFS_DPRINTK(dfs, ATH_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,
|
||||
(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++;
|
||||
}
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s[%d]:xxxxx ts =%u re.re_dur=%u re.re_rssi =%u diff =%u pl->pl_lastelem.p_time=%llu xxxxx",
|
||||
__func__, __LINE__, (uint32_t) this_ts, re.re_dur,
|
||||
re.re_rssi, diff_ts,
|
||||
(unsigned long long)pl->pl_elems[index].p_time);
|
||||
|
||||
/* 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 < 100) {
|
||||
dfs_reset_alldelaylines(dfs);
|
||||
dfs_reset_radarq(dfs);
|
||||
}
|
||||
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 == 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)) {
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_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)) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s:chan->ic_flags=0x%x, MHz separation=%d\n",
|
||||
__func__, chan->ic_flags,
|
||||
chan->ic_pri_freq_center_freq_mhz_separation);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s: Peak Idx =%d,re.re_dur =%d,diff_ts =%d\n",
|
||||
__func__, re.sidx, re.re_dur, diff_ts);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"\n%s: Modify pulse dur to fit 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))) {
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"\n%s:chan->ic_flags=0x%x,MHz Separation=%d\n",
|
||||
__func__, chan->ic_flags,
|
||||
chan->ic_pri_freq_center_freq_mhz_separation);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s:Peak Index =%d,re.re_dur =%d,diff_ts =%d\n",
|
||||
__func__, re.sidx, re.re_dur, diff_ts);
|
||||
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO,
|
||||
"%s:Modify ETSI pulse dur to 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);
|
||||
|
||||
while ((tabledepth < DFS_MAX_RADAR_OVERLAP) &&
|
||||
((dfs->dfs_radartable[re.re_dur])[tabledepth] != -1) &&
|
||||
(!retval)) {
|
||||
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);
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP,
|
||||
CDF_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);
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP,
|
||||
CDF_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); 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);
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP,
|
||||
CDF_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);
|
||||
CDF_TRACE
|
||||
(CDF_MODULE_ID_SAP,
|
||||
CDF_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 >
|
||||
(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);
|
||||
CDF_TRACE
|
||||
(CDF_MODULE_ID_SAP,
|
||||
CDF_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,
|
||||
(uint32_t)
|
||||
deltaT,
|
||||
re.
|
||||
re_dur);
|
||||
} else {
|
||||
found =
|
||||
dfs_bin_check(dfs, rf,
|
||||
(uint32_t)
|
||||
deltaT,
|
||||
re.re_dur,
|
||||
ext_chan_event_flag);
|
||||
}
|
||||
if (dfs->
|
||||
dfs_debug_mask & ATH_DEBUG_DFS2) {
|
||||
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);
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_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) {
|
||||
/* Collect stats */
|
||||
dfs->ath_dfs_stats.num_radar_detects++;
|
||||
thischan = &rs->rs_chan;
|
||||
CDF_TRACE(CDF_MODULE_ID_SAP, CDF_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);
|
||||
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);
|
||||
}
|
||||
dfs->dfs_phyerr_freq_min = 0x7fffffff;
|
||||
dfs->dfs_phyerr_freq_max = 0;
|
||||
dfs->dfs_phyerr_w53_counter = 0;
|
||||
}
|
||||
/* CDF_TRACE(CDF_MODULE_ID_SAP, CDF_TRACE_LEVEL_INFO, "IN FUNC %s[%d]: retval = %d ",__func__,__LINE__,retval); */
|
||||
return retval;
|
||||
/* #endif */
|
||||
/* return 1; */
|
||||
}
|
||||
|
||||
#endif /* ATH_SUPPORT_DFS */
|
308
core/sap/dfs/src/dfs_staggered.c
Normal file
308
core/sap/dfs/src/dfs_staggered.c
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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_staggered.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
|
||||
|
||||
static int is_pri_multiple(uint32_t sample_pri, uint32_t refpri)
|
||||
{
|
||||
#define MAX_ALLOWED_MISSED 3
|
||||
int i;
|
||||
|
||||
if (sample_pri < refpri || (!refpri))
|
||||
return 0;
|
||||
|
||||
for (i = 1; i <= MAX_ALLOWED_MISSED; i++) {
|
||||
if ((sample_pri % (i * refpri) <= 5)) {
|
||||
/* printk("sample_pri=%d is a multiple of refpri=%d\n", sample_pri, refpri); */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#undef MAX_ALLOWED_MISSED
|
||||
}
|
||||
|
||||
static int is_unique_pri(uint32_t highestpri, uint32_t midpri,
|
||||
uint32_t lowestpri, uint32_t refpri)
|
||||
{
|
||||
#define DFS_STAGGERED_PRI_MARGIN_MIN 20
|
||||
#define DFS_STAGGERED_PRI_MARGIN_MAX 400
|
||||
if ((DFS_DIFF(lowestpri, refpri) >= DFS_STAGGERED_PRI_MARGIN_MIN) &&
|
||||
(DFS_DIFF(midpri, refpri) >= DFS_STAGGERED_PRI_MARGIN_MIN) &&
|
||||
(DFS_DIFF(highestpri, refpri) >= DFS_STAGGERED_PRI_MARGIN_MIN)) {
|
||||
return 1;
|
||||
} else {
|
||||
if ((is_pri_multiple(refpri, highestpri))
|
||||
|| (is_pri_multiple(refpri, lowestpri))
|
||||
|| (is_pri_multiple(refpri, midpri)))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
#undef DFS_STAGGERED_PRI_MARGIN_MIN
|
||||
#undef DFS_STAGGERED_PRI_MARGIN_MAX
|
||||
}
|
||||
|
||||
int dfs_staggered_check(struct ath_dfs *dfs, struct dfs_filter *rf,
|
||||
uint32_t deltaT, uint32_t width)
|
||||
{
|
||||
uint32_t refpri, refdur, searchpri = 0, deltapri; /* , averagerefpri; */
|
||||
uint32_t n, i, primargin, durmargin;
|
||||
int score[DFS_MAX_DL_SIZE], delayindex, dindex, found = 0;
|
||||
struct dfs_delayline *dl;
|
||||
uint32_t scoreindex, lowpriindex = 0, lowpri = 0xffff;
|
||||
#if 0
|
||||
int numpulses = 0;
|
||||
#endif
|
||||
int higherthan, lowerthan, numscores;
|
||||
int numpulseshigh = 0, numpulsesmid = 0, numpulsestemp = 0;
|
||||
uint32_t lowestscore = 0, lowestscoreindex = 0, lowestpri = 0;
|
||||
uint32_t midscore = 0, midscoreindex = 0, midpri = 0;
|
||||
uint32_t highestscore = 0, highestscoreindex = 0, highestpri = 0;
|
||||
|
||||
dl = &rf->rf_dl;
|
||||
if (dl->dl_numelems < (rf->rf_threshold - 1)) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"numelems %d < threshold for filter %d\n",
|
||||
dl->dl_numelems, rf->rf_pulseid);
|
||||
return 0;
|
||||
}
|
||||
if (deltaT > rf->rf_filterlen) {
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"numelems %d < threshold for filter %d\n",
|
||||
dl->dl_numelems, rf->rf_pulseid);
|
||||
return 0;
|
||||
}
|
||||
primargin = 6;
|
||||
if (rf->rf_maxdur < 10) {
|
||||
durmargin = 4;
|
||||
} else {
|
||||
durmargin = 6;
|
||||
}
|
||||
|
||||
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) || (refpri < rf->rf_minpri)) {
|
||||
score[n] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
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)
|
||||
score[n]++;
|
||||
}
|
||||
}
|
||||
for (n = 0; n < dl->dl_numelems; n++) {
|
||||
delayindex = (dl->dl_firstelem + n) & DFS_MAX_DL_MASK;
|
||||
refdur = dl->dl_elems[delayindex].de_time;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2, "score[%d]=%d pri=%d\n", n,
|
||||
score[n], refdur);
|
||||
}
|
||||
|
||||
/* find out the 2 or 3 highest scorers */
|
||||
scoreindex = 0;
|
||||
highestscore = 0;
|
||||
highestscoreindex = 0;
|
||||
highestpri = 0;
|
||||
numscores = 0;
|
||||
lowestscore = 0;
|
||||
|
||||
for (n = 0; n < dl->dl_numelems; n++) {
|
||||
higherthan = 0;
|
||||
lowerthan = 0;
|
||||
delayindex = (dl->dl_firstelem + n) & DFS_MAX_DL_MASK;
|
||||
refpri = dl->dl_elems[delayindex].de_time;
|
||||
|
||||
if ((score[n] >= highestscore) &&
|
||||
(is_unique_pri(highestpri, midpri, lowestpri, refpri))) {
|
||||
lowestscore = midscore;
|
||||
lowestpri = midpri;
|
||||
lowestscoreindex = midscoreindex;
|
||||
midscore = highestscore;
|
||||
midpri = highestpri;
|
||||
midscoreindex = highestscoreindex;
|
||||
highestscore = score[n];
|
||||
highestpri = refpri;
|
||||
highestscoreindex = n;
|
||||
} else {
|
||||
if ((score[n] >= midscore) &&
|
||||
(is_unique_pri
|
||||
(highestpri, midpri, lowestpri, refpri))) {
|
||||
lowestscore = midscore;
|
||||
lowestpri = midpri;
|
||||
lowestscoreindex = midscoreindex;
|
||||
midscore = score[n];
|
||||
midpri = refpri;
|
||||
midscoreindex = n;
|
||||
} else if ((score[n] >= lowestscore) &&
|
||||
(is_unique_pri
|
||||
(highestpri, midpri, lowestpri, refpri))) {
|
||||
lowestscore = score[n];
|
||||
lowestpri = refpri;
|
||||
lowestscoreindex = n;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (midscore == 0) {
|
||||
/* This means we have only 1 pulse type. It can not be staggered! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"FINAL highestscore=%d highestscoreindex=%d highestpri=%d\n",
|
||||
highestscore, highestscoreindex, highestpri);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"FINAL lowestscore=%d lowestscoreindex=%d lowpri=%d\n",
|
||||
lowestscore, lowestscoreindex, lowestpri);
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"FINAL midscore=%d midscoreindex=%d midpri=%d\n",
|
||||
midscore, midscoreindex, midpri);
|
||||
|
||||
delayindex = (dl->dl_firstelem + highestscoreindex) & DFS_MAX_DL_MASK;
|
||||
refdur = dl->dl_elems[delayindex].de_dur;
|
||||
refpri = dl->dl_elems[delayindex].de_time;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"highscoreindex=%d refdur=%d refpri=%d\n",
|
||||
highestscoreindex, refdur, refpri);
|
||||
|
||||
numpulsestemp =
|
||||
dfs_bin_pri_check(dfs, rf, dl, highestscore, refpri, refdur, 0,
|
||||
highestpri);
|
||||
numpulseshigh = numpulsestemp;
|
||||
numpulsestemp =
|
||||
dfs_bin_pri_check(dfs, rf, dl, highestscore, refpri, refdur, 0,
|
||||
highestpri + midpri);
|
||||
if (numpulsestemp > numpulseshigh) {
|
||||
numpulseshigh = numpulsestemp;
|
||||
}
|
||||
numpulsestemp =
|
||||
dfs_bin_pri_check(dfs, rf, dl, highestscore, refpri, refdur, 0,
|
||||
highestpri + midpri + lowestpri);
|
||||
if (numpulsestemp > numpulseshigh) {
|
||||
numpulseshigh = numpulsestemp;
|
||||
}
|
||||
|
||||
delayindex = (dl->dl_firstelem + midscoreindex) & DFS_MAX_DL_MASK;
|
||||
refdur = dl->dl_elems[delayindex].de_dur;
|
||||
refpri = dl->dl_elems[delayindex].de_time;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
|
||||
"midscoreindex=%d refdur=%d refpri=%d\n",
|
||||
midscoreindex, refdur, refpri);
|
||||
|
||||
/* numpulsesmid = dfs_bin_pri_check(dfs, rf, dl, midscore, refpri, refdur,0, 1); */
|
||||
numpulsestemp =
|
||||
dfs_bin_pri_check(dfs, rf, dl, midscore, refpri, refdur, 0, midpri);
|
||||
numpulsesmid = numpulsestemp;
|
||||
numpulsestemp =
|
||||
dfs_bin_pri_check(dfs, rf, dl, midscore, refpri, refdur, 0,
|
||||
highestpri + midpri);
|
||||
if (numpulsestemp > numpulsesmid) {
|
||||
numpulsesmid = numpulsestemp;
|
||||
}
|
||||
numpulsestemp =
|
||||
dfs_bin_pri_check(dfs, rf, dl, midscore, refpri, refdur, 0,
|
||||
highestpri + midpri + lowestpri);
|
||||
if (numpulsestemp > numpulsesmid) {
|
||||
numpulsesmid = numpulsestemp;
|
||||
}
|
||||
|
||||
/*delayindex = (dl->dl_firstelem + lowestscoreindex) & DFS_MAX_DL_MASK;
|
||||
refdur = dl->dl_elems[delayindex].de_dur;
|
||||
refpri = dl->dl_elems[delayindex].de_time;
|
||||
DFS_DPRINTK(ic, ATH_DEBUG_DFS1, "lowestscoreindex=%d refdur=%d refpri=%d\n", lowestscoreindex, refdur, refpri);
|
||||
|
||||
numpulseslow = dfs_bin_pri_check(dfs, rf, dl, lowestscore, refpri, refdur,0, 1);
|
||||
*/
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"numpulseshigh=%d, numpulsesmid=%d\n",
|
||||
numpulseshigh, numpulsesmid);
|
||||
/* printf("numpulseshigh=%d, numpulsesmid=%d, numpulseslow %d\n",numpulseshigh, numpulsesmid, numpulseslow); */
|
||||
|
||||
if ((numpulseshigh >= rf->rf_threshold)
|
||||
&& (numpulsesmid >= rf->rf_threshold)) {
|
||||
/*if (numpulses >= rf->rf_threshold) { */
|
||||
found = 1;
|
||||
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
|
||||
"MATCH filter=%u numpulseshigh=%u numpulsesmid= %u thresh=%u\n",
|
||||
rf->rf_pulseid, numpulseshigh, numpulsesmid,
|
||||
rf->rf_threshold);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
#endif /* ATH_SUPPORT_DFS */
|
Reference in New Issue
Block a user