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:
Prakash Dhavali
2015-11-02 17:55:19 -08:00
parent 8508e16801
commit 7090c5fd8d
547 changed files with 531140 additions and 0 deletions

View 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
View 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_ */

View 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_ */

View 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
View 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
View 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 */

View 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 */

View 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 */

View 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
View 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 */

View 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_ */

View 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
View 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
View 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) */

View 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__ */

View 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;
}

View 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__ */

View 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 */

View 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 */

View 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 */