Prechádzať zdrojové kódy

qcacmn: DFS componentization Initial version

The main task of the Dynamic Frequency Selection (DFS) component is to
analyze RADAR pulses and declare RADAR detected if a filter match is
found. The  DFS component also performs channel availability Check (CAC),
stores Non Occupancy List (NOL), and selects  a random channel from a
list of available channels. This componentization of DFS code brings all
the scattered code into a single place, which makes it is easy to maintain
and also easy to move the component to a desired software layer with
minimal effort. Any DFS data structure and code must be accessed through
the public APIs provided in the DFS dispatcher.

This commit contains only Filtering part. Next and subsequent commits will
contain: Filter Tables, CAC, Zero CAC, NOL, RADAR event notification, and
DFS Dispatcher.

Change-Id: I2c21f7752545eb910c48ade630ceba74053cc019
CRs-Fixed: 2001106
Abhijit Pradhan 8 rokov pred
rodič
commit
af7d7da0cc

+ 1893 - 0
umac/dfs/core/src/dfs.h

@@ -0,0 +1,1893 @@
+/*
+ * Copyright (c) 2013, 2016-2017 The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2005-2006 Atheros Communications, 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.
+ */
+
+/**
+ * DOC: This file has main dfs structures.
+ */
+
+#ifndef _DFS_H_
+#define _DFS_H_
+
+#include <qdf_types.h>       /* QDF_NBUF_EXEMPT_NO_EXEMPTION, etc. */
+#include <qdf_net_types.h>   /* QDF_NBUF_EXEMPT_NO_EXEMPTION, etc. */
+#include <qdf_nbuf.h>        /* qdf_nbuf_t, etc. */
+#include <qdf_util.h>        /* qdf_assert */
+#include <qdf_lock.h>        /* qdf_spinlock */
+#include <qdf_time.h>
+#include <qdf_timer.h>
+
+#include "dfs_structs.h"
+#include "dfs_channel.h"
+#include "dfs_ioctl.h"
+#include "dfs_ioctl_private.h"
+#include <i_qdf_types.h>     /* For qdf_packed*/
+#include <queue.h>           /* For STAILQ_ENTRY */
+#include <wlan_objmgr_psoc_obj.h>
+#include <wlan_objmgr_pdev_obj.h>
+#include <osdep.h>
+
+#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))) {    \
+		printk(_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
+
+/**
+ * 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!
+ */
+
+/**
+ * Cascade has issue with reported duration especially when there is a
+ * crossover of chirp from one segment to another. It may report a value
+ * of duration that is well below 50us for a valid FCC type 5 chirping
+ * pulse. For now changing minimum duration as a work around. This will
+ * affect all chips but since we detect chirp with Merlin+, we may be OK
+ * for now. We need a more robust solution for this.
+ */
+#define MIN_BIN5_DUR_CAS            25 /* 50 * 1.25*/
+#define MIN_BIN5_DUR_MICROSEC_CAS   20
+#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
+
+/* Conversion is already done using dfs->dur_multiplier */
+#define MAX_BIN5_DUR                145   /* use 145 for osprey */
+#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 WLAN_DFSQ_LOCK(_dfs)            spin_lock(&(_dfs)->dfs_radarqlock)
+#define WLAN_DFSQ_UNLOCK(_dfs)          spin_unlock(&(_dfs)->dfs_radarqlock)
+#define WLAN_DFSQ_LOCK_INIT(_dfs)       spin_lock_init(&(_dfs)->dfs_radarqlock)
+
+#define WLAN_ARQ_LOCK(_dfs)             spin_lock(&(_dfs)->dfs_arqlock)
+#define WLAN_ARQ_UNLOCK(_dfs)           spin_unlock(&(_dfs)->dfs_arqlock)
+#define WLAN_ARQ_LOCK_INIT(_dfs)        spin_lock_init(&(_dfs)->dfs_arqlock)
+
+#define WLAN_DFSEVENTQ_LOCK(_dfs)       spin_lock(&(_dfs)->dfs_eventqlock)
+#define WLAN_DFSEVENTQ_UNLOCK(_dfs)     spin_unlock(&(_dfs)->dfs_eventqlock)
+#define WLAN_DFSEVENTQ_LOCK_INIT(_dfs)  spin_lock_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 */
+
+/* Max number of overlapping filters */
+#define DFS_MAX_RADAR_OVERLAP 16
+
+/* 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 */
+/* Radar detect in second segment is capable */
+#define DFS_SECOND_SEGMENT_RADAR_EN 0x20000000
+#define DFS_MAX_RSSI_VALUE 0x7fffffff /* Max rssi value */
+
+#define DFS_BIN_MAX_PULSES 60 /* max num of pulses in a burst */
+#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)
+
+/**
+ * Software use: channel interference used for as AR as well as RADAR
+ * interference detection.
+ */
+#define CHANNEL_INTERFERENCE    0x01
+
+#define CHANNEL_2GHZ      0x00080 /* 2 GHz spectrum channel. */
+#define CHANNEL_OFDM      0x00040 /* OFDM channel */
+#define CHANNEL_TURBO     0x00010 /* Turbo Channel */
+#define CHANNEL_108G (CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+
+/* qdf_packed - denotes structure is packed. */
+#define qdf_packed __qdf_packed
+
+#define SEG_ID_PRIMARY         0
+#define SEG_ID_SECONDARY       1
+
+/* MIN and MAX width for different regions */
+#define REG0_MIN_WIDTH 33
+#define REG0_MAX_WIDTH 38
+#define REG1_MIN_WIDTH 39
+#define REG1_MAX_WIDTH 44
+#define REG2_MIN_WIDTH 53
+#define REG2_MAX_WIDTH 58
+#define REG3_MIN_WIDTH 126
+#define REG3_MAX_WIDTH 140
+#define REG4_MIN_WIDTH 141
+#define REG4_MAX_WIDTH 160
+#define REG5_MIN_WIDTH 189
+#define REG5_MAX_WIDTH 210
+#define REG6_MIN_WIDTH 360
+#define REG6_MAX_WIDTH 380
+#define REG7_MIN_WIDTH 257
+#define REG7_MAX_WIDTH 270
+#define REG8_MIN_WIDTH 295
+#define REG8_MAX_WIDTH 302
+
+#define OVER_SAMPLING_FREQ 44000
+#define SAMPLING_FREQ 40000
+#define HUNDRED 100
+#define NUM_BINS 128
+#define THOUSAND 1000
+
+/**
+ * struct dfs_pulseparams - DFS pulse param structure.
+ * @p_time:        Time for start of pulse in usecs.
+ * @p_dur:         Duration of pulse in usecs.
+ * @p_rssi:        Duration of pulse in usecs.
+ */
+struct dfs_pulseparams {
+	uint64_t p_time;
+	uint8_t p_dur;
+	uint8_t p_rssi;
+} qdf_packed;
+
+/**
+ * struct dfs_pulseline - Pulseline structure.
+ * @pl_elems[]:     array of pulses in delay line.
+ * @pl_firstelem:   Index of the first element.
+ * @pl_lastelem:    Index of the last element.
+ * @pl_numelems:    Number of elements in the delay line.
+ */
+struct dfs_pulseline {
+	struct dfs_pulseparams pl_elems[DFS_MAX_PULSE_BUFFER_SIZE];
+	uint32_t pl_firstelem;
+	uint32_t pl_lastelem;
+	uint32_t pl_numelems;
+} qdf_packed;
+
+#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 - DFS event structure.
+ * @re_full_ts:          64-bit full timestamp from interrupt time.
+ * @re_ts:               Original 15 bit recv timestamp.
+ * @re_rssi:             Rssi of radar event.
+ * @re_dur:              Duration of radar pulse.
+ * @re_chanindex:        Channel of event.
+ * @re_flags:            Event flags.
+ * @re_freq:             Centre frequency of event, KHz.
+ * @re_freq_lo:          Lower bounds of frequency, KHz.
+ * @re_freq_hi:          Upper bounds of frequency, KHz.
+ * @re_seg_id:           HT80_80/HT160 use.
+ * @re_sidx:             Seg index.
+ * @re_freq_offset_khz:  Freq offset in KHz
+ * @re_peak_mag:         Peak mag.
+ * @re_total_gain:       Total gain.
+ * @re_mb_gain:          Mb gain.
+ * @re_relpwr_db:        Relpower in db.
+ * @re_list:             List of radar events.
+ */
+struct dfs_event {
+	uint64_t  re_full_ts;
+	uint32_t  re_ts;
+	uint8_t   re_rssi;
+	uint8_t   re_dur;
+	uint8_t   re_chanindex;
+	uint8_t   re_flags;
+	uint32_t  re_freq;
+	uint32_t  re_freq_lo;
+	uint32_t  re_freq_hi;
+	u_int     re_seg_id;
+	int       re_sidx;
+	u_int     re_freq_offset_khz;
+	int       re_peak_mag;
+	int       re_total_gain;
+	int       re_mb_gain;
+	int       re_relpwr_db;
+
+	STAILQ_ENTRY(dfs_event) re_list;
+} qdf_packed;
+
+#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 - DFS AR state structure.
+ * @ar_prevwidth:         Previous width.
+ * @ar_phyerrcount[]:     Phy error count.
+ * @ar_acksum:            Acksum.
+ * @ar_packetthreshold:   Thresh to determine traffic load.
+ * @ar_parthreshold:      Thresh to determine peak.
+ * @ar_radarrssi:         Rssi threshold for AR event.
+ * @ar_prevtimestamp:     Prev time stamp.
+ * @ar_peaklist[]:        Peak list.
+ */
+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;
+	uint32_t ar_parthreshold;
+	uint32_t ar_radarrssi;
+	uint16_t ar_prevtimestamp;
+	uint16_t ar_peaklist[DFS_AR_MAX_NUM_PEAKS];
+};
+
+/**
+ * struct dfs_delayelem - Delay Element.
+ * @de_time:  Current "filter" time for start of pulse in usecs.
+ * @de_dur:   Duration of pulse in usecs.
+ * @de_rssi:  Rssi of pulse in dB.
+ * @de_ts:    Time stamp for this delay element.
+ */
+struct dfs_delayelem {
+	uint32_t de_time;
+	uint8_t  de_dur;
+	uint8_t  de_rssi;
+	uint64_t de_ts;
+} qdf_packed;
+
+/**
+ * struct dfs_delayline - DFS Delay Line.
+ * @dl_elems[]:    Array of pulses in delay line.
+ * @dl_last_ts:    Last timestamp the delay line was used (in usecs).
+ * @dl_firstelem:  Index of the first element.
+ * @dl_lastelem:   Index of the last element.
+ * @dl_numelems:   Number of elements in the delay line.
+ */
+struct dfs_delayline {
+	struct dfs_delayelem dl_elems[DFS_MAX_DL_SIZE];
+	uint64_t dl_last_ts;
+	uint32_t dl_firstelem;
+	uint32_t dl_lastelem;
+	uint32_t dl_numelems;
+} qdf_packed;
+
+/**
+ * struct dfs_filter - Dfs filter.
+ * @rf_dl:              Delay line of pulses for this filter.
+ * @rf_numpulses:       Number of pulses in the filter.
+ * @rf_minpri:          Min pri to be considered for this filter.
+ * @rf_maxpri:          Max pri to be considered for this filter.
+ * @rf_threshold:       Match filter output threshold for radar detect.
+ * @rf_filterlen:       Length (in usecs) of the filter.
+ * @rf_patterntype:     Fixed or variable pattern type.
+ * @rf_fixed_pri_radar_pulse: indicates if it is a fixed pri pulse.
+ * @rf_mindur:          Min duration for this radar filter.
+ * @rf_maxdur:          Max duration for this radar filter.
+ * @rf_ignore_pri_window: Ignore pri window.
+ * @rf_pulseid:         Unique ID corresponding to the original filter ID.
+ */
+struct dfs_filter {
+	struct dfs_delayline rf_dl;
+	uint32_t  rf_numpulses;
+	uint32_t  rf_minpri;
+	uint32_t  rf_maxpri;
+	uint32_t  rf_threshold;
+	uint32_t  rf_filterlen;
+	uint32_t  rf_patterntype;
+	uint32_t  rf_fixed_pri_radar_pulse;
+	uint32_t  rf_mindur;
+	uint32_t  rf_maxdur;
+	uint32_t  rf_ignore_pri_window;
+	uint32_t  rf_pulseid;
+} qdf_packed;
+
+/**
+ * struct dfs_filtertype - DFS Filter type.
+ * @ft_filterdur[]:    Filter array.
+ * @ft_filterdur:      Duration of pulse which specifies filter type.
+ * @ft_numfilters:     Num filters of this type.
+ * @ft_last_ts:        Last timestamp this filtertype was used (in usecs).
+ * @ft_mindur:         Min pulse duration to be considered for this filter type.
+ * @ft_maxdur:         Max pulse duration to be considered for this filter type.
+ * @ft_rssithresh:     Min rssi to be considered for this filter type.
+ * @ft_numpulses:      Num pulses in each filter of this type.
+ * @ft_patterntype:    Fixed or variable pattern type.
+ * @ft_minpri:         Min pri to be considered for this type.
+ * @ft_rssimargin:     Rssi threshold margin. In Turbo Mode HW reports rssi 3dB
+ *                     lower than in non TURBO mode. This will offset that diff.
+ */
+struct dfs_filtertype {
+	struct dfs_filter ft_filters[DFS_MAX_NUM_RADAR_FILTERS];
+	uint32_t  ft_filterdur;
+	uint32_t  ft_numfilters;
+	uint64_t  ft_last_ts;
+	uint32_t  ft_mindur;
+	uint32_t  ft_maxdur;
+	uint32_t  ft_rssithresh;
+	uint32_t  ft_numpulses;
+	uint32_t  ft_patterntype;
+	uint32_t  ft_minpri;
+	uint32_t  ft_rssimargin;
+};
+
+/**
+ * struct dfs_ieee80211_channel - Channel structure for dfs component.
+ * @ic_freq:                Frequency in Mhz.
+ * @ic_flags:               Channel flags.
+ * @ic_flagext:             Extended channel flags.
+ * @ic_ieee:                IEEE channel number.
+ * @ic_vhtop_ch_freq_seg1:  Channel Center frequency.
+ * @ic_vhtop_ch_freq_seg2:  Channel Center frequency applicable for 80+80MHz
+ *                          mode of operation.
+ */
+struct dfs_ieee80211_channel {
+	uint16_t       ic_freq;
+	uint32_t       ic_flags;
+	uint16_t       ic_flagext;
+	uint8_t        ic_ieee;
+	uint8_t        ic_vhtop_ch_freq_seg1;
+	uint8_t        ic_vhtop_ch_freq_seg2;
+};
+
+/**
+ * struct dfs_state - DFS state.
+ * @rs_chan:            Channel info.
+ * @rs_chanindex:       Channel index in radar structure.
+ * @rs_numradarevents:  Number of radar events.
+ * @rs_param:           Phy param.
+ */
+struct dfs_state {
+	struct dfs_ieee80211_channel rs_chan;
+	uint8_t  rs_chanindex;
+	uint32_t rs_numradarevents;
+	struct wlan_dfs_phyerr_param rs_param;
+};
+
+#define DFS_NOL_TIMEOUT_S  (30*60)    /* 30 minutes in seconds */
+#define DFS_NOL_TIMEOUT_MS (DFS_NOL_TIMEOUT_S * 1000)
+#define DFS_NOL_TIMEOUT_US (DFS_NOL_TIMEOUT_MS * 1000)
+
+/**
+ * struct dfs_nolelem - DFS NOL element.
+ * @nol_freq:         Centre frequency.
+ * @nol_chwidth:      Event width (MHz).
+ * @nol_start_ticks:  NOL start time in OS ticks.
+ * @nol_timeout_ms:   NOL timeout value in msec.
+ * @nol_timer:        Per element NOL timer.
+ * @nol_next:         Next element pointer.
+ */
+struct dfs_nolelem {
+	uint32_t       nol_freq;
+	uint32_t       nol_chwidth;
+	unsigned long  nol_start_ticks;
+	uint32_t       nol_timeout_ms;
+	os_timer_t     nol_timer;
+	struct dfs_nolelem *nol_next;
+} qdf_packed;
+
+/**
+ * struct dfs_nol_timer_arg - DFS NOL timer arguments.
+ * @dfs:        Object of wlan_dfs structure.
+ * @delfreq:    Freq to delete.
+ * @delchwidth: Channel width to delete.
+ */
+struct dfs_nol_timer_arg {
+	struct wlan_dfs *dfs;
+	uint16_t       delfreq;
+	uint16_t       delchwidth;
+};
+
+/**
+ * struct dfs_info - DFS Info.
+ * @rn_use_nol:            Use the NOL when radar found (default: TRUE).
+ * @rn_numradars:          Number of different types of radars.
+ * @rn_lastfull_ts:        Last 64 bit timstamp from recv interrupt.
+ * @rn_last_ts:            last 15 bit ts from recv descriptor.
+ * @rn_last_unique_ts:     last unique 32 bit ts from recv descriptor.
+ * @rn_ts_prefix:          Prefix to prepend to 15 bit recv ts.
+ * @rn_numbin5radars:      Number of bin5 radar pulses to search for.
+ * @rn_fastdivGCval:       Value of fast diversity gc limit from init file.
+ * @rn_minrssithresh:      Min rssi for all radar types.
+ * @rn_maxpulsedur:        Max pulse width in TSF ticks.
+ * @dfs_ext_chan_busy:     Ext chan busy.
+ * @ext_chan_busy_ts:      Ext chan busy time.
+ * @dfs_bin5_chirp_ts:     Ext bin5 chrip time.
+ * @dfs_last_bin5_dur:     Last bin5 during.
+ */
+struct dfs_info {
+	int       rn_use_nol;
+	uint32_t  rn_numradars;
+	uint64_t  rn_lastfull_ts;
+	uint16_t  rn_last_ts;
+	uint32_t  rn_last_unique_ts;
+	uint64_t  rn_ts_prefix;
+	uint32_t  rn_numbin5radars;
+	uint32_t  rn_fastdivGCval;
+	int32_t   rn_minrssithresh;
+	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;
+} qdf_packed;
+
+/**
+ * struct dfs_bin5elem - BIN5 elements.
+ * @be_ts:   Timestamp for the bin5 element.
+ * @be_rssi: Rssi for the bin5 element.
+ * @be_dur:  Duration of bin5 element.
+ */
+struct dfs_bin5elem {
+	uint64_t  be_ts;
+	uint32_t  be_rssi;
+	uint32_t  be_dur;
+};
+
+/**
+ * struct dfs_bin5radars - BIN5 radars.
+ * @br_elems[]:    List of bin5 elems that fall within the time window.
+ * @br_firstelem:  Index of the first element.
+ * @br_lastelem:   Index of the last element.
+ * @br_numelems:   Number of elements in the delay line.
+ * @br_pulse:      Original info about bin5 pulse.
+ */
+struct dfs_bin5radars {
+	struct dfs_bin5elem br_elems[DFS_MAX_B5_SIZE];
+	uint32_t  br_firstelem;
+	uint32_t  br_lastelem;
+	uint32_t  br_numelems;
+	struct dfs_bin5pulse br_pulse;
+};
+
+/**
+ * struct dfs_stats - DFS stats.
+ * @num_radar_detects:    Total num. of radar detects.
+ * @num_seg_two_radar_detects: Total num. of radar detected in secondary segment
+ * @total_phy_errors:     Total PHY errors.
+ * @owl_phy_errors:       OWL PHY errors.
+ * @pri_phy_errors:       Primary channel phy errors.
+ * @ext_phy_errors:       Extension channel phy errors.
+ * @dc_phy_errors:        DC PHY errors.
+ * @early_ext_phy_errors: Extension channel early radar found error.
+ * @bwinfo_errors:        Bogus bandwidth info received in descriptor.
+ * @datalen_discards:     data length at least three bytes of payload.
+ * @rssi_discards:        RSSI is not accurate.
+ * @last_reset_tstamp:    Last reset timestamp.
+ */
+struct dfs_stats {
+	uint32_t       num_radar_detects;
+	uint32_t  num_seg_two_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;
+};
+
+#define DFS_EVENT_LOG_SIZE      256
+
+/**
+ * struct dfs_event_log - DFS event log.
+ * @ts:               64-bit full timestamp from interrupt time.
+ * @diff_ts:          Diff timestamp.
+ * @rssi:             Rssi of radar event.
+ * @dur:              Duration of radar pulse.
+ * @is_chirp:         Chirp flag.
+ * @seg_id:           HT80_80/HT160 use.
+ * @sidx:             Seg index.
+ * @freq_offset_khz:  Freq offset in KHz
+ * @peak_mag:         Peak mag.
+ * @total_gain:       Total gain.
+ * @mb_gain:          Mb gain.
+ * @relpwr_db:        Relpower in db.
+ */
+
+struct dfs_event_log {
+	uint64_t  ts;
+	uint32_t  diff_ts;
+	uint8_t   rssi;
+	uint8_t   dur;
+	int       is_chirp;
+	u_int     seg_id;
+	int       sidx;
+	u_int     freq_offset_khz;
+	int       peak_mag;
+	int       total_gain;
+	int       mb_gain;
+	int       relpwr_db;
+};
+
+#define WLAN_DFS_RESET_TIME_S 7
+#define WLAN_DFS_WAIT (60 + WLAN_DFS_RESET_TIME_S) /* 60 seconds */
+#define WLAN_DFS_WAIT_MS ((WLAN_DFS_WAIT) * 1000)  /*in MS*/
+
+#define WLAN_DFS_WEATHER_CHANNEL_WAIT_MIN 10 /*10 minutes*/
+#define WLAN_DFS_WEATHER_CHANNEL_WAIT_S (WLAN_DFS_WEATHER_CHANNEL_WAIT_MIN * 60)
+#define WLAN_DFS_WEATHER_CHANNEL_WAIT_MS  \
+	((WLAN_DFS_WEATHER_CHANNEL_WAIT_S) * 1000) /*in MS*/
+
+#define WLAN_DFS_WAIT_POLL_PERIOD 2  /* 2 seconds */
+#define WLAN_DFS_WAIT_POLL_PERIOD_MS  \
+	((WLAN_DFS_WAIT_POLL_PERIOD) * 1000)  /*in MS*/
+
+#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
+
+#define PRECAC_LIST_LOCK(_dfs)    spin_lock_irqsave(  \
+		&(_dfs)->dfs_precac_lock, (_dfs)->dfs_precac_lock_flags)
+#define PRECAC_LIST_UNLOCK(_dfs)  spin_unlock_irqrestore(  \
+		&(_dfs)->dfs_precac_lock, (_dfs)->dfs_precac_lock_flags)
+
+/**
+ * struct wlan_dfs -        The main dfs structure.
+ * @dfs_debug_mask:        Current debug bitmask.
+ * @dfs_curchan_radindex:  Current channel radar index.
+ * @dfs_extchan_radindex:  Extension channel radar index.
+ * @dfsdomain:             cur. DFS domain.
+ * @dfs_proc_phyerr:       Flags for Phy Errs to process.
+ * @ic:                    pointer to ieee80211com structure.
+ * @dfs_eventq:            Q of free dfs event objects.
+ * @dfs_eventqlock:        Lock for free dfs event list.
+ * @dfs_radarq:            Q of radar events.
+ * @dfs_radarqlock:        Lock for dfs q.
+ * @dfs_arq:               Q of AR events.
+ * @dfs_arqlock:           Lock for AR q.
+ * @dfs_ar_state:          AR state.
+ * @dfs_radar[]:           dfs_radar - Per-Channel Radar detector state.
+ * @dfs_radarf[]:          dfs_radarf - One filter for each radar pulse type.
+ * @dfs_rinfo:             State vars for radar processing.
+ * @dfs_b5radars:          array of bin5 radar events.
+ * @dfs_radartable:        map of radar durs to filter types.
+ * @dfs_nol:               Non occupancy list for radar.
+ * @dfs_nol_count:         How many items?
+ * @dfs_defaultparams:     Default phy params per radar state.
+ * @wlan_dfs_stats:         DFS related stats.
+ * @pulses:                pulse history.
+ * @events:                Events structure.
+ * @wlan_radar_tasksched:   radar task is scheduled.
+ * @wlan_dfswait:           waiting on channel for radar detect.
+ * @wlan_dfstest:           Test timer in progress.
+ * @dfs_caps:              Object of wlan_dfs_caps structure.
+ * @wlan_dfstest_ieeechan:  IEEE chan num to return to after a dfs mute test.
+ * @wlan_dfs_cac_time:      CAC period.
+ * @wlan_dfstesttime:       Time to stay off chan during dfs test.
+ * @wlan_dfswaittimer:      dfs wait timer.
+ * @wlan_dfstesttimer:      dfs mute test timer.
+ * @wlan_dfs_debug_timer:   dfs debug timer.
+ * @dfs_second_segment_bangradar: Bangaradar on second segment of VHT80_80/160.
+ * @is_radar_found_on_secondary_seg: Radar on second segment.
+ * @is_radar_during_precac: Radar found during precac.
+ * @dfs_precac_lock:       lock to protect precac lists.
+ * @dfs_precac_lock_flags  Flags for dfs_precac_lock.
+ * @dfs_precac_enable:     Enable the precac.
+ * @dfs_precac_secondary_freq: Second segment freq for precac.
+ * @dfs_precac_primary_freq: Primary freq.
+ * @dfs_precac_timer_running: precac timer running.
+ * @dfs_defer_precac_channel_change: Defer precac channel change.
+ * @dfs_pre_cac_timeout_channel_change: Channel change due to precac timeout.
+ * @wlan_dfs_task_timer:    dfs wait timer.
+ * @dur_multiplier:        Duration multiplier.
+ * @wlan_dfs_isdfsregdomain: true when AP is in DFS domain
+ * @wlan_dfs_false_rssi_thres: False RSSI Threshold.
+ * @wlan_dfs_peak_mag:      peak mag.
+ * @radar_log[]:           radar log.
+ * @dfs_event_log_count:   Event log count.
+ * @dfs_event_log_on:      Event log on.
+ * @dfs_phyerr_count:      Same as number of PHY radar interrupts.
+ * @dfs_phyerr_reject_count: When TLV is supported, # of radar events ignored
+ *                           after TLV is parsed.
+ * @dfs_phyerr_queued_count: Number of radar events queued for matching the
+ *                           filters.
+ * @dfs_phyerr_freq_min:   Phyerr min freq.
+ * @dfs_phyerr_freq_max:   Phyerr max freq.
+ * @dfs_phyerr_w53_counter: phyerr w53 counter.
+ * @dfs_pri_multiplier:    Allow pulse if they are within multiple of PRI for
+ *                         the radar type.
+ * @wlan_dfs_nol_timeout:   NOL timeout.
+ * @update_nol:            Update NOL.
+ * @dfs_nol_event[]:       NOL event.
+ * @dfs_nol_timer:         NOL list processing.
+ * @dfs_cac_timer:         CAC timer.
+ * @dfs_cac_valid_timer:   Ignore CAC when this timer is running.
+ * @dfs_cac_timeout_override: overridden cac timeout.
+ * @dfs_enable:            DFS Enable.
+ * @dfs_cac_timer_running: DFS CAC timer running.
+ * @dfs_ignore_dfs:        Ignore DFS.
+ * @dfs_ignore_cac:        Ignore CAC.
+ * @dfs_cac_valid:         DFS CAC valid.
+ * @dfs_cac_valid_time:    Time for which CAC will be valid and will not be
+ *                         re-done.
+ * @dfs_precac_timer:      PRECAC timer.
+ * @dfs_precac_timeout_override: overridden precac timeout.
+ * @dfs_num_precac_freqs:  Number of PreCAC VHT80 frequencies.
+ * @dfs_precac_required_list: PreCAC required list.
+ * @dfs_precac_done_list:  PreCAC done list.
+ * @dfs_precac_nol_list:   PreCAC NOL List.
+ */
+struct wlan_dfs {
+	uint32_t  dfs_debug_mask;
+	int16_t   dfs_curchan_radindex;
+	int16_t   dfs_extchan_radindex;
+	uint32_t  dfsdomain;
+	uint32_t  dfs_proc_phyerr;
+	struct ieee80211com *ic;
+
+	STAILQ_HEAD(, dfs_event) dfs_eventq;
+	spinlock_t dfs_eventqlock;
+
+	STAILQ_HEAD(, dfs_event) dfs_radarq;
+	spinlock_t dfs_radarqlock;
+
+	STAILQ_HEAD(, dfs_event) dfs_arq;
+	spinlock_t dfs_arqlock;
+	struct dfs_ar_state dfs_ar_state;
+	struct dfs_state dfs_radar[DFS_NUM_RADAR_STATES];
+	struct dfs_filtertype *dfs_radarf[DFS_MAX_RADAR_TYPES];
+	struct dfs_info dfs_rinfo;
+	struct dfs_bin5radars *dfs_b5radars;
+	int8_t **dfs_radartable;
+	struct dfs_nolelem *dfs_nol;
+	int dfs_nol_count;
+	struct wlan_dfs_phyerr_param dfs_defaultparams;
+	struct dfs_stats wlan_dfs_stats;
+	struct dfs_pulseline *pulses;
+	struct dfs_event *events;
+	uint32_t wlan_radar_tasksched:1,
+			 wlan_dfswait:1,
+			 wlan_dfstest:1;
+	struct wlan_dfs_caps dfs_caps;
+	uint8_t wlan_dfstest_ieeechan;
+	uint32_t wlan_dfs_cac_time;
+	uint32_t wlan_dfstesttime;
+	os_timer_t wlan_dfswaittimer;
+	os_timer_t wlan_dfstesttimer;
+	os_timer_t wlan_dfs_debug_timer;
+	uint8_t    dfs_bangradar;
+	bool dfs_second_segment_bangradar;
+	bool is_radar_found_on_secondary_seg;
+	bool is_radar_during_precac;
+	spinlock_t dfs_precac_lock;
+	unsigned long dfs_precac_lock_flags;
+	bool dfs_precac_enable;
+	uint8_t dfs_precac_secondary_freq;
+	uint8_t dfs_precac_primary_freq;
+	uint8_t dfs_precac_timer_running;
+	uint8_t dfs_defer_precac_channel_change;
+	uint8_t dfs_pre_cac_timeout_channel_change:1;
+	os_timer_t wlan_dfs_task_timer;
+	int dur_multiplier;
+	uint16_t wlan_dfs_isdfsregdomain;
+	int wlan_dfs_false_rssi_thres;
+	int wlan_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;
+	int dfs_phyerr_reject_count;
+	int dfs_phyerr_queued_count;
+	int dfs_phyerr_freq_min;
+	int dfs_phyerr_freq_max;
+	int dfs_phyerr_w53_counter;
+	int dfs_pri_multiplier;
+	int wlan_dfs_nol_timeout;
+	bool update_nol;
+	int dfs_nol_event[IEEE80211_CHAN_MAX];
+	os_timer_t dfs_nol_timer;
+	os_timer_t dfs_cac_timer;
+	os_timer_t dfs_cac_valid_timer;
+	int dfs_cac_timeout_override;
+	int8_t dfs_enable:1,
+		   dfs_cac_timer_running:1,
+		   dfs_ignore_dfs:1,
+		   dfs_ignore_cac:1,
+		   dfs_cac_valid:1;
+	uint32_t dfs_cac_valid_time;
+	os_timer_t dfs_precac_timer;
+	int dfs_precac_timeout_override;
+	uint8_t dfs_num_precac_freqs;
+
+	TAILQ_HEAD(, dfs_precac_entry) dfs_precac_required_list;
+	TAILQ_HEAD(, dfs_precac_entry) dfs_precac_done_list;
+	TAILQ_HEAD(, dfs_precac_entry) dfs_precac_nol_list;
+	struct dfs_ieee80211_channel *dfs_curchan;
+	struct wlan_objmgr_pdev *dfs_pdev_obj;
+};
+
+/**
+ * enum DFS debug - This should match the table from if_ath.c.
+ * @WLAN_DEBUG_DFS:             Minimal DFS debug.
+ * @WLAN_DEBUG_DFS1:            Normal DFS debug.
+ * @WLAN_DEBUG_DFS2:            Maximal DFS debug.
+ * @WLAN_DEBUG_DFS3:            Matched filterID display.
+ * @WLAN_DEBUG_DFS_PHYERR:      Phy error parsing.
+ * @WLAN_DEBUG_DFS_NOL:         NOL related entries.
+ * @WLAN_DEBUG_DFS_PHYERR_SUM:  PHY error summary.
+ * @WLAN_DEBUG_DFS_PHYERR_PKT:  PHY error payload.
+ * @WLAN_DEBUG_DFS_BIN5:        BIN5 checks.
+ * @WLAN_DEBUG_DFS_BIN5_FFT:    BIN5 FFT check.
+ * @WLAN_DEBUG_DFS_BIN5_PULSE:  BIN5 pulse check.
+ * @WLAN_DEBUG_DFS_FALSE_DET:   False detection debug related prints.
+ */
+enum {
+	WLAN_DEBUG_DFS  = 0x00000100,
+	WLAN_DEBUG_DFS1 = 0x00000200,
+	WLAN_DEBUG_DFS2 = 0x00000400,
+	WLAN_DEBUG_DFS3 = 0x00000800,
+	WLAN_DEBUG_DFS_PHYERR = 0x00001000,
+	WLAN_DEBUG_DFS_NOL    = 0x00002000,
+	WLAN_DEBUG_DFS_PHYERR_SUM = 0x00004000,
+	WLAN_DEBUG_DFS_PHYERR_PKT = 0x00008000,
+	WLAN_DEBUG_DFS_BIN5       = 0x00010000,
+	WLAN_DEBUG_DFS_BIN5_FFT   = 0x00020000,
+	WLAN_DEBUG_DFS_BIN5_PULSE = 0x00040000,
+	WLAN_DEBUG_DFS_FALSE_DET  = 0x00080000,
+};
+
+/**
+ * struct dfs_phy_err - DFS phy error.
+ * @fulltsf:             64-bit TSF as read from MAC.
+ * @is_pri:              Detected on primary channel.
+ * @is_ext:              Detected on extension channel.
+ * @is_dc:               Detected at DC.
+ * @is_early:            Early detect.
+ * @do_check_chirp:      Whether to check hw_chirp/sw_chirp.
+ * @is_hw_chirp:         Hardware-detected chirp.
+ * @is_sw_chirp:         Software detected chirp.
+ * @rs_tstamp:           32 bit TSF from RX descriptor (event).
+ * @freq:                Centre frequency of event - KHz.
+ * @freq_lo:             Lower bounds of frequency - KHz.
+ * @freq_hi:             Upper bounds of frequency - KHz.
+ * @rssi:                Pulse RSSI.
+ * @dur:                 Pulse duration, raw (not uS).
+ * @seg_id:              HT80_80/HT160 use.
+ * @sidx:                Seg index.
+ * @freq_offset_khz:     Freq offset in KHz.
+ * @peak_mag:            Peak mag.
+ * @total_gain:          Total gain.
+ * @mb_gain:             Mb gain.
+ * @relpwr_db:           Relpower in DB.
+ *
+ * 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:
+ *
+ * 1)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.
+ *
+ * 2)For chips that do hardware chirp detection or FFT, the "do_check_chirp"
+ *   bit should be set.
+ *
+ * 3)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.
+ *
+ * 4)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:
+ *
+ * 1)add duration in uS and raw duration, so the PHY error parsing
+ *   code is responsible for doing the duration calculation;
+ * 2)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;
+	uint32_t is_pri:1,
+			 is_ext:1,
+			 is_dc:1,
+			 is_early:1,
+			 do_check_chirp:1,
+			 is_hw_chirp:1,
+			 is_sw_chirp:1;
+	uint32_t rs_tstamp;
+	uint32_t freq;
+	uint32_t freq_lo;
+	uint32_t freq_hi;
+	uint8_t  rssi;
+	uint8_t  dur;
+	u_int    seg_id;
+	int      sidx;
+	u_int    freq_offset_khz;
+	int      peak_mag;
+	int      total_gain;
+	int      mb_gain;
+	int      relpwr_db;
+};
+
+/**
+ * struct rx_radar_status - Parsed radar status
+ * @raw_tsf:           Raw tsf
+ * @tsf_offset:        TSF offset.
+ * @rssi:              RSSI.
+ * @pulse_duration:    Pulse duration.
+ * @is_chirp:          Is chirp.
+ * @delta_peak:        Delta peak.
+ * @delta_diff:        Delta diff.
+ * @sidx:              Starting frequency.
+ * @freq_offset:       Frequency offset.
+ * @agc_total_gain:    AGC total gain.
+ * @agc_mb_gain:       AGC MB gain.
+ */
+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 */
+	int      agc_total_gain;
+	int      agc_mb_gain;
+};
+
+/**
+ * struct rx_search_fft_report - FFT report.
+ * @total_gain_db:     Total gain in Db.
+ * @base_pwr_db:       Base power in Db.
+ * @fft_chn_idx:       FFT channel index.
+ * @peak_sidx:         Peak sidx.
+ * @relpwr_db:         Real power in Db.
+ * @avgpwr_db:         Average power in Db.
+ * @peak_mag:          Peak Mag.
+ * @num_str_bins_ib:   Num dtr BINs IB
+ * @seg_id:            Segment ID
+ */
+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;
+	int      seg_id;
+};
+
+/**
+ * dfs_radar_found_action() - It marks the channel as radar, adds it to NOL and
+ *                            indicates to mlme to choose a random channel.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_radar_found_action(struct wlan_dfs *dfs);
+
+/**
+ * dfs_process_radarevent() - process the radar event generated for a pulse.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @chan: Current channel.
+ *
+ * There is currently no way to specify that a radar event has occurred 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.
+ *
+ * Return: If a radar event is found, return 1.  Otherwise, return 0.
+ */
+int dfs_process_radarevent(struct wlan_dfs *dfs,
+		struct dfs_ieee80211_channel *chan);
+
+/**
+ * dfs_nol_addchan() - Add channel to NOL.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @freq: frequency to add to NOL.
+ * @dfs_nol_timeout: NOL timeout.
+ */
+void dfs_nol_addchan(struct wlan_dfs *dfs,
+		uint16_t freq,
+		uint32_t dfs_nol_timeout);
+
+/**
+ * dfs_get_nol() - Get NOL.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @dfs_nol: Pointer to dfsreq_nolelem structure to save the channels from NOL.
+ * @nchan: Number of channels.
+ */
+void dfs_get_nol(struct wlan_dfs *dfs,
+		struct dfsreq_nolelem *dfs_nol,
+		int *nchan);
+
+/**
+ * dfs_set_nol() - Set NOL.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @dfs_nol: Pointer to dfsreq_nolelem structure.
+ * @nchan: Number of channels.
+ */
+void dfs_set_nol(struct wlan_dfs *dfs,
+		struct dfsreq_nolelem *dfs_nol,
+		int nchan);
+
+/**
+ * dfs_nol_update() - NOL update
+ * @dfs: Pointer to wlan_dfs structure.
+ *
+ * Notify the driver/umac that it should update the channel radar/NOL flags
+ * based on the current NOL list.
+ */
+void dfs_nol_update(struct wlan_dfs *dfs);
+
+/**
+ * dfs_nol_timer_cleanup() - NOL timer cleanup.
+ * @dfs: Pointer to wlan_dfs structure.
+ *
+ * Cancels the NOL timer and frees the NOL elements.
+ */
+void dfs_nol_timer_cleanup(struct wlan_dfs *dfs);
+
+/**
+ * dfs_retain_bin5_burst_pattern() - Retain the BIN5 burst pattern.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @diff_ts: Timestamp diff.
+ * @old_dur: Old duration.
+ */
+uint8_t dfs_retain_bin5_burst_pattern(struct wlan_dfs *dfs,
+		uint32_t diff_ts,
+		uint8_t old_dur);
+
+/**
+ * dfs_bin5_check_pulse() - BIN5 check pulse.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @re: Pointer to dfs_event structure.
+ * @br: Pointer to dfs_bin5radars structure.
+ *
+ * Reject the pulse if:
+ * 1) It's outside the RSSI threshold;
+ * 2) It's outside the pulse duration;
+ * 3) It's been verified by HW/SW chirp checking
+ *    and neither of those found a chirp.
+ */
+int dfs_bin5_check_pulse(struct wlan_dfs *dfs,
+		struct dfs_event *re,
+		struct dfs_bin5radars *br);
+
+/**
+ * dfs_bin5_addpulse() - BIN5 add pulse.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @br: Pointer to dfs_bin5radars structure.
+ * @re: Pointer to dfs_event structure.
+ * @thists: Timestamp.
+ */
+int dfs_bin5_addpulse(struct wlan_dfs *dfs,
+		struct dfs_bin5radars *br,
+		struct dfs_event *re,
+		uint64_t thists);
+
+/**
+ * dfs_bin5_check() - BIN5 check.
+ * @dfs: Pointer to wlan_dfs structure.
+ *
+ * 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 wlan_dfs *dfs);
+
+/**
+ * dfs_check_chirping() - Check chirping.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer
+ * @datalen: Phyerr buf length
+ * @is_ctl: detected on primary channel.
+ * @is_ext: detected on extension channel.
+ * @slope: Slope
+ * @is_dc: DC found
+ *
+ * This examines the FFT data contained in the PHY error information to figure
+ * out whether the pulse is moving across frequencies.
+ */
+int dfs_check_chirping(struct wlan_dfs *dfs,
+		void *buf,
+		uint16_t datalen,
+		int is_ctl,
+		int is_ext,
+		int *slope,
+		int *is_dc);
+
+/**
+ * dfs_get_random_bin5_dur() - Get random BIN5 duration.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @tstamp: Timestamp.
+ *
+ * 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
+ * wlan_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.
+ * This function doesn't take into account whether the hardware
+ * is operating in 5GHz fast clock mode or not.
+ * And this function doesn't take into account whether the hardware
+ * is peregrine or not.
+ */
+int dfs_get_random_bin5_dur(struct wlan_dfs *dfs,
+		uint64_t tstamp);
+
+/**
+ * dfs_print_delayline() - Prints delayline.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @dl: Pointer to dfs_delayline structure.
+ */
+void dfs_print_delayline(struct wlan_dfs *dfs,
+		struct dfs_delayline *dl);
+
+/**
+ * dfs_print_nol() - Print NOL elements.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_print_nol(struct wlan_dfs *dfs);
+
+/**
+ * dfs_print_filter() - Prints the filter.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rf: Pointer to dfs_filter structure.
+ */
+void dfs_print_filter(struct wlan_dfs *dfs,
+		struct dfs_filter *rf);
+
+/**
+ * dfs_getchanstate() - Get chan state.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @index: To save the index of dfs_radar[]
+ * @ext_chan_flag: Extension channel flag;
+ */
+struct dfs_state *dfs_getchanstate(struct wlan_dfs *dfs,
+		uint8_t *index,
+		int ext_ch_flag);
+
+/**
+ * dfs_round() - DFS found.
+ * @val: Convert durations to TSF ticks.
+ *
+ * Return: TSF ticks.
+ */
+uint32_t dfs_round(int32_t val);
+
+/**
+ * dfs_reset_alldelaylines() - Reset alldelaylines.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_reset_alldelaylines(struct wlan_dfs *dfs);
+
+/**
+ * dfs_reset_delayline() - Clear only a single delay line.
+ * @dl: Pointer to dfs_delayline structure.
+ */
+void dfs_reset_delayline(struct dfs_delayline *dl);
+
+/**
+ * dfs_reset_filter_delaylines() - Reset filter delaylines.
+ * @dft: Pointer to dfs_filtertype structure.
+ */
+void dfs_reset_filter_delaylines(struct dfs_filtertype *dft);
+
+/**
+ * dfs_reset_radarq() - Reset radar queue.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_reset_radarq(struct wlan_dfs *dfs);
+
+/**
+ * dfs_add_pulse() - Adds pulse to the queue.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rf: Pointer to dfs_filter structure.
+ * @re: Pointer to dfs_event structure.
+ * @deltaT: deltaT value.
+ * @this_ts: Last time stamp.
+ */
+void dfs_add_pulse(struct wlan_dfs *dfs,
+		struct dfs_filter *rf,
+		struct dfs_event *re,
+		uint32_t deltaT,
+		uint64_t this_ts);
+
+/**
+ * dfs_bin_fixedpattern_check() - Checks the BIN pattern.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rf:  Pointer to dfs_filter structure.
+ * @dur: Duration.
+ * ext_chan_flag : Ext channel flag.
+ */
+int dfs_bin_fixedpattern_check(struct wlan_dfs *dfs,
+		struct dfs_filter *rf,
+		uint32_t dur,
+		int ext_chan_flag);
+
+/**
+ * dfs_bin_check() - BIN check
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rf: Pointer to dfs_filter structure.
+ * @deltaT: deltaT value.
+ * @width: Width
+ * @ext_chan_flag: Extension channel flag.
+ */
+int dfs_bin_check(struct wlan_dfs *dfs,
+		struct dfs_filter *rf,
+		uint32_t deltaT,
+		uint32_t dur,
+		int ext_chan_flag);
+
+/**
+ * dfs_bin_pri_check() - BIN PRI check
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rf: Pointer to dfs_filter structure.
+ * @dl: Pointer to dfs_delayline structure.
+ * @score: Primary score.
+ * @refpri: Current "filter" time for start of pulse in usecs.
+ * @refdur: Duration value.
+ * @ext_chan_flag: Extension channel flag.
+ * @fundamentalpri: Highest PRI.
+ */
+int dfs_bin_pri_check(struct wlan_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);
+
+/**
+ * dfs_staggered_check() - Detection implementation for staggered PRIs.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rf: Pointer to dfs_filter structure.
+ * @deltaT: Delta of the Timestamp.
+ * @width: Duration of radar pulse.
+ *
+ * Return: 1 on success and 0 on failure.
+ */
+int dfs_staggered_check(struct wlan_dfs *dfs,
+		struct dfs_filter *rf,
+		uint32_t deltaT,
+		uint32_t width);
+
+/**
+ * dfs_get_pri_margin() - Get Primary margin.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @is_extchan_detect: Extension channel detect.
+ * @is_fixed_pattern: Fixed pattern.
+ *
+ * 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.
+ *
+ * Return: Returns pri_margin.
+ */
+int dfs_get_pri_margin(struct wlan_dfs *dfs,
+		int is_extchan_detect,
+		int is_fixed_pattern);
+
+/**
+ * dfs_get_filter_threshold() - Get filter threshold.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rf: Pointer to dfs_filter structure.
+ * @is_extchan_detect: Extension channel detect.
+ *
+ * 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.
+ *
+ * Return: Returns threshold.
+ */
+int dfs_get_filter_threshold(struct wlan_dfs *dfs,
+		struct dfs_filter *rf,
+		int is_extchan_detect);
+
+/**
+ * dfs_process_ar_event() - Process the ar event.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @chan: Current channel structure.
+ */
+void dfs_process_ar_event(struct wlan_dfs *dfs,
+		struct dfs_ieee80211_channel *chan);
+
+/**
+ * dfs_reset_ar() - resets the ar state.
+ * @dfs: pointer to wlan_dfs structure.
+ */
+void dfs_reset_ar(struct wlan_dfs *dfs);
+
+/**
+ * dfs_reset_arq() - resets the ar queue.
+ * @dfs: pointer to wlan_dfs structure.
+ */
+void dfs_reset_arq(struct wlan_dfs *dfs);
+
+/**
+ * dfs_process_phyerr_bb_tlv() - Parses the PHY error and populates the
+ *                               dfs_phy_err struct.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer
+ * @datalen: Phyerr buf len
+ * @rssi: RSSI
+ * @ext_rssi: Extension RSSI.
+ * @rs_tstamp: Time stamp.
+ * @fulltsf: TSF64.
+ * @e: Pointer to dfs_phy_err structure.
+ *
+ * Return: Returns 1.
+ */
+int dfs_process_phyerr_bb_tlv(struct wlan_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);
+
+/**
+ * dfs_reset() - DFS reset
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_reset(struct wlan_dfs *dfs);
+
+/**
+ * dfs_radar_enable() - Enables the radar.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @no_cac: If no_cac is 0, it cancels the CAC.
+ */
+void dfs_radar_enable(struct wlan_dfs *dfs,
+		int no_cac, uint32_t opmode);
+
+/**
+ * dfs_process_phyerr() - Process phyerr.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer.
+ * @datalen: phyerr buffer length.
+ * @r_rssi: RSSI.
+ * @r_ext_rssi: Extension channel RSSI.
+ * @r_rs_tstamp: Timestamp.
+ * @r_fulltsf: TSF64.
+ */
+void dfs_process_phyerr(struct wlan_dfs *dfs,
+		void *buf,
+		uint16_t datalen,
+		uint8_t r_rssi,
+		uint8_t r_ext_rssi,
+		uint32_t r_rs_tstamp,
+		uint64_t r_fulltsf);
+
+/**
+ * dfs_is_precac_timer_running() - Check whether precac timer is running.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+bool dfs_is_precac_timer_running(struct wlan_dfs *dfs);
+
+/**
+ * dfs_get_radars() - Based on the chipset, calls init radar table functions.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_get_radars(struct wlan_dfs *dfs);
+
+/**
+ * dfs_attach() - Allocates memory for wlan_dfs members.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+int dfs_attach(struct wlan_dfs *dfs);
+
+/**
+ * dfs_create_object() - Creates DFS object.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+int dfs_create_object(struct wlan_dfs **dfs);
+
+/**
+ * dfs_destroy_object() - Destroys the DFS object.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_destroy_object(struct wlan_dfs *dfs);
+
+/**
+ * nif_dfs_reset() - DFS reset.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void nif_dfs_reset(struct wlan_dfs *dfs);
+
+/**
+ * dfs_random_channel() - Function to choose the random channel from the current
+ *                        channel list.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @is_select_nondfs: Select NON-DFS chan or both NON-DFS and DFS.
+ * @skip_curchan: Select the next channel post radar detecr and skip the
+ *                curchan.
+ */
+int dfs_random_channel(struct wlan_dfs *dfs,
+		uint8_t is_select_nondfs,
+		uint8_t skip_curchan);
+
+/**
+ * sif_dfs_detach() - DFS detach.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void sif_dfs_detach(struct wlan_dfs *dfs);
+
+/**
+ * nif_dfs_detach() - DFS detach
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void nif_dfs_detach(struct wlan_dfs *dfs);
+
+/**
+ * nif_dfs_attach() - DFS attach function.
+ * @dfs: Pointer to wlan_dfs structure.
+ **/
+void nif_dfs_attach(struct wlan_dfs *dfs);
+
+/**
+ * dfs_cac_valid_reset() - Cancels the dfs_cac_valid_timer timer.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @prevchan_ieee: Prevchan number.
+ * @prevchan_flags: Prevchan flags.
+ */
+void dfs_cac_valid_reset(struct wlan_dfs *dfs,
+		uint8_t prevchan_ieee,
+		uint32_t prevchan_flags);
+
+/**
+ * dfs_cac_stop() - Clear the AP CAC timer.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_cac_stop(struct wlan_dfs *dfs);
+
+/**
+ * dfs_cancel_cac_timer() - Cancels the CAC timer.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_cancel_cac_timer(struct wlan_dfs *dfs);
+
+/**
+ * dfs_start_cac_timer() - Starts the CAC timer.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_start_cac_timer(struct wlan_dfs *dfs);
+
+/**
+ * dfs_get_usenol() - Returns use_nol flag.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+uint16_t dfs_get_usenol(struct wlan_dfs *dfs);
+
+/**
+ * dfs_set_update_nol_flag() - Sets update_nol flag.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @val: update_nol flag.
+ */
+void dfs_set_update_nol_flag(struct wlan_dfs *dfs,
+		bool val);
+
+/**
+ * dfs_get_update_nol_flag() - Returns update_nol flag.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+bool dfs_get_update_nol_flag(struct wlan_dfs *dfs);
+
+/**
+ * dfs_get_rn_use_nol() - Get usenol.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+int dfs_get_rn_use_nol(struct wlan_dfs *dfs);
+
+/**
+ * dfs_get_nol_timeout() - Get NOL timeout.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+int dfs_get_nol_timeout(struct wlan_dfs *dfs);
+
+/**
+ * dfs_is_ap_cac_timer_running() - Returns the dfs cac timer.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+int dfs_is_ap_cac_timer_running(struct wlan_dfs *dfs);
+
+/**
+ * dfs_control()- Used to process ioctls related to DFS.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @id: Command type.
+ * @indata: Input buffer.
+ * @insize: size of the input buffer.
+ * @outdata: A buffer for the results.
+ * @outsize: Size of the output buffer.
+ */
+int dfs_control(struct wlan_dfs *dfs,
+		u_int id,
+		void *indata,
+		uint32_t insize,
+		void *outdata,
+		uint32_t *outsize);
+
+/**
+ * dfs_getnol() - Wrapper function for dfs_get_nol()
+ * @dfs: Pointer to wlan_dfs structure.
+ * @dfs_nolinfo: Pointer to dfsreq_nolinfo structure.
+ */
+void dfs_getnol(struct wlan_dfs *dfs,
+		void *dfs_nolinfo);
+
+/**
+ * dfs_get_override_cac_timeout() -  Get override CAC timeout value.
+ * @dfs: Pointer to DFS object.
+ * @cac_timeout: Pointer to save the CAC timeout value.
+ */
+int dfs_get_override_cac_timeout(struct wlan_dfs *dfs,
+		int *cac_timeout);
+
+/**
+ * dfs_override_cac_timeout() -  Override the default CAC timeout.
+ * @dfs: Pointer to DFS object.
+ * @cac_timeout: CAC timeout value.
+ */
+int dfs_override_cac_timeout(struct wlan_dfs *dfs,
+		int cac_timeout);
+
+/**
+ * dfs_clear_nolhistory() - unmarks IEEE80211_CHAN_CLR_HISTORY_RADAR flag for
+ *                          all the channels in ic_channels.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_clear_nolhistory(struct wlan_dfs *dfs);
+
+/**
+ * ol_if_dfs_configure() - Initialize the RADAR table for offload chipsets.
+ * @dfs: Pointer to wlan_dfs structure.
+ *
+ * This is called during a channel change or regulatory domain
+ * reset; in order to fetch the new configuration information and
+ * program the DFS pattern matching module.
+ *
+ * Eventually this should be split into "fetch config" (which can
+ * happen at regdomain selection time) and "configure DFS" (which
+ * can happen at channel config time) so as to minimise overheads
+ * when doing channel changes.  However, this'll do for now.
+ */
+void ol_if_dfs_configure(struct wlan_dfs *dfs);
+
+/**
+ * dfs_init_radar_filters() - Init Radar filters.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @radar_info: Pointer to wlan_dfs_radar_tab_info structure.
+ */
+int dfs_init_radar_filters(struct wlan_dfs *dfs,
+		struct wlan_dfs_radar_tab_info *radar_info);
+
+/**
+ * dfs_get_radars_for_ar5212() - Initialize radar table for AR5212 chipsets.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_get_radars_for_ar5212(struct wlan_dfs *dfs);
+
+/**
+ * dfs_get_radars_for_ar5416() - Initialize radar table for AR5416 chipsets.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_get_radars_for_ar5416(struct wlan_dfs *dfs);
+
+/**
+ * dfs_get_radars_for_ar9300() - Initialize radar table for AR9300 chipsets.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_get_radars_for_ar9300(struct wlan_dfs *dfs);
+
+/**
+ * dfs_print_filters() - Print the filters.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_print_filters(struct wlan_dfs *dfs);
+
+/**
+ * dfs_clear_stats() - Clear stats.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_clear_stats(struct wlan_dfs *dfs);
+
+/**
+ * dfs_radar_disable() - Disables the radar.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+int dfs_radar_disable(struct wlan_dfs *dfs);
+
+/**
+ * dfs_mark_precac_dfs() - Mark the precac channel as radar.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_mark_precac_dfs(struct wlan_dfs *dfs,
+		uint8_t is_radar_found_on_secondary_seg);
+
+/**
+ * dfs_get_debug_info() - Get debug info.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @data: void pointer to the data to save dfs_proc_phyerr.
+ */
+int dfs_get_debug_info(struct wlan_dfs *dfs,
+		void *data);
+
+/**
+ * dfs_print_nolhistory() - Print NOL history.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_print_nolhistory(struct wlan_dfs *dfs);
+
+/**
+ * dfs_stacac_stop() - Clear the STA CAC timer.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+void dfs_stacac_stop(struct wlan_dfs *dfs);
+
+/**
+ * dfs_find_precac_secondary_vht80_chan() - Get a VHT80 channel with the
+ *                                          precac primary center frequency.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @chan: Pointer to dfs channel structure.
+ */
+void dfs_find_precac_secondary_vht80_chan(struct wlan_dfs *dfs,
+		struct dfs_ieee80211_channel **chan);
+
+/**
+ * dfs_phyerr_param_copy() - Function to copy src buf to dest buf.
+ * @dst: dest buf.
+ * @src: src buf.
+ */
+void dfs_phyerr_param_copy(struct wlan_dfs_phyerr_param *dst,
+		struct wlan_dfs_phyerr_param *src);
+
+/**
+ * dfs_get_thresholds() - Get the threshold value.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @param: Pointer to wlan_dfs_phyerr_param structure.
+ */
+int dfs_get_thresholds(struct wlan_dfs *dfs,
+		struct wlan_dfs_phyerr_param *param);
+
+/**
+ * dfs_set_thresholds() - Sets the threshold value.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @threshtype: DFS ioctl param type.
+ * @value: Threshold value.
+ */
+int dfs_set_thresholds(struct wlan_dfs *dfs,
+		const uint32_t threshtype,
+		const uint32_t value);
+
+/**
+ * ol_if_dfs_clist_update() - Update the channel list with the given NOL list.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @cmd: DFS_NOL_CLIST_CMD_UPDATE command.
+ * @nollist: NOL channel entries.
+ * @nentries: Number of channels in the NOL entry.
+ * @opmode: Device operating mode.
+ */
+void ol_if_dfs_clist_update(struct wlan_dfs *dfs,
+		int cmd,
+		struct dfs_nol_chan_entry *nollist,
+		int nentries,
+		uint32_t opmode);
+
+/**
+ * dfs_set_current_channel() - Set DFS current channel.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @ic_freq: Frequency in Mhz.
+ * @ic_flags: Channel flags.
+ * @ic_flagext: Extended channel flags.
+ * @ic_ieee: IEEE channel number.
+ * @ic_vhtop_ch_freq_seg1: Channel Center frequency1.
+ * @ic_vhtop_ch_freq_seg2: Channel Center frequency2.
+ */
+void dfs_set_current_channel(struct wlan_dfs *dfs,
+		uint16_t ic_freq,
+		uint32_t ic_flags,
+		uint16_t ic_flagext,
+		uint8_t ic_ieee,
+		uint8_t ic_vhtop_ch_freq_seg1,
+		uint8_t ic_vhtop_ch_freq_seg2);
+
+/**
+ * dfs_second_segment_radar_disable() - Disables the second segment radar.
+ * @dfs: Pointer to wlan_dfs structure.
+ *
+ * This is called when AP detects the radar, to (potentially) disable
+ * the radar code.
+ *
+ * Return: returns 0.
+ */
+int dfs_second_segment_radar_disable(struct wlan_dfs *dfs);
+
+/**
+ * dfs_get_nol_chfreq_and_chwidth() - Get channel freq and width from NOL list.
+ * @nollist: Pointer to NOL channel entry.
+ * @nol_chfreq: Pointer to save channel frequency.
+ * @nol_chwidth: Pointer to save channel width.
+ */
+void dfs_get_nol_chfreq_and_chwidth(struct dfs_nol_chan_entry *nollist,
+		uint32_t *nol_chfreq,
+		uint32_t *nol_chwidth,
+		int index);
+
+/**
+ * dfs_process_phyerr_owl() - Process an Owl-style phy error.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer
+ * @datalen: Phyerr buf len
+ * @rssi: RSSI
+ * @ext_rssi: Extension RSSI.
+ * @rs_tstamp: Time stamp.
+ * @fulltsf: TSF64.
+ * @e: Pointer to dfs_phy_err structure.
+ *
+ * Return: Returns 1.
+ */
+int dfs_process_phyerr_owl(struct wlan_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);
+
+/**
+ * dfs_process_phyerr_sowl() -Process a Sowl/Howl style phy error.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer
+ * @datalen: Phyerr buf len
+ * @rssi: RSSI
+ * @ext_rssi: Extension RSSI.
+ * @rs_tstamp: Time stamp.
+ * @fulltsf: TSF64.
+ * @e: Pointer to dfs_phy_err structure.
+ *
+ * Return: Returns 1.
+ */
+int dfs_process_phyerr_sowl(struct wlan_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);
+
+/**
+ * dfs_process_phyerr_merlin() - Process a Merlin/Osprey style phy error.
+ *                               dfs_phy_err struct.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer
+ * @datalen: Phyerr buf len
+ * @rssi: RSSI
+ * @ext_rssi: Extension RSSI.
+ * @rs_tstamp: Time stamp.
+ * @fulltsf: TSF64.
+ * @e: Pointer to dfs_phy_err structure.
+ *
+ * Return: Returns 1.
+ */
+int dfs_process_phyerr_merlin(struct wlan_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);
+
+/*
+ * __dfs_process_radarevent() - Continuation of process a radar event function.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @ft: Pointer to dfs_filtertype structure.
+ * @rf: Pointer to dfs_filter structure.
+ * @re: Pointer to dfs_event structure.
+ * @this_ts: Timestamp.
+ *
+ * There is currently no way to specify that a radar event has occurred 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.
+ *
+ * Return: If a radar event is found, return 1.  Otherwise, return 0.
+ */
+void __dfs_process_radarevent(struct wlan_dfs *dfs,
+		struct dfs_filtertype *ft,
+		struct dfs_filter *rf,
+		struct dfs_event *re,
+		uint64_t this_ts,
+		int *found);
+
+/**
+ * bin5_rules_check_internal() - This is a extension of dfs_bin5_check().
+ * @dfs: Pointer to wlan_dfs structure.
+ * @br: Pointer to dfs_bin5radars structure.
+ * @bursts: Bursts.
+ * @numevents: Number of events.
+ * @prev: prev index.
+ * @i: Index.
+ * @this: index to br_elems[]
+ */
+void bin5_rules_check_internal(struct wlan_dfs *dfs,
+		struct dfs_bin5radars *br,
+		uint32_t *bursts,
+		uint32_t *numevents,
+		uint32_t prev,
+		uint32_t i,
+		uint32_t this);
+
+/**
+ * count_the_other_delay_elements() - Counts the ther delay elements.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rf: Pointer to dfs_filter structure.
+ * @dl: Pointer to dfs_delayline structure.
+ * @i: Index value.
+ * @refpri: Current "filter" time for start of pulse in usecs.
+ * @refdur: Duration value.
+ * @primargin: Primary margin.
+ * @durmargin: Duration margin.
+ * @numpulses: Number of pulses.
+ * @prev_good_timestamp: Previous good timestamp.
+ * @fundamentalpri: Highest PRI.
+ */
+void count_the_other_delay_elements(struct wlan_dfs *dfs,
+		struct dfs_filter *rf,
+		struct dfs_delayline *dl,
+		uint32_t i,
+		uint32_t refpri,
+		uint32_t refdur,
+		uint32_t primargin,
+		uint32_t durmargin,
+		int *numpulses,
+		uint32_t *prev_good_timestamp,
+		int fundamentalpri
+		);
+
+#endif  /* _DFS_H_ */

+ 557 - 0
umac/dfs/core/src/dfs_channel.h

@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008 Atheros Communications, 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.
+ */
+
+/**
+ * DOC: This file has channel related information.
+ */
+
+#ifndef _NET80211__IEEE80211_H_
+#define _NET80211__IEEE80211_H_
+
+/**
+ * enum ieee80211_phymode - Phymodes.
+ * @IEEE80211_MODE_AUTO:            autoselect.
+ * @IEEE80211_MODE_11A:             5GHz, OFDM
+ * @IEEE80211_MODE_11B:             2GHz, CCK
+ * @IEEE80211_MODE_11G:             2GHz, OFDM
+ * @IEEE80211_MODE_FH:              2GHz, GFSK
+ * @IEEE80211_MODE_TURBO_A:         5GHz, OFDM, 2x clock dynamic turbo
+ * @IEEE80211_MODE_TURBO_G:         2GHz, OFDM, 2x clock dynamic turbo
+ * @IEEE80211_MODE_11NA_HT20:       5Ghz, HT20
+ * @IEEE80211_MODE_11NG_HT20:       2Ghz, HT20
+ * @IEEE80211_MODE_11NA_HT40PLUS:   5Ghz, HT40 (ext ch +1)
+ * @IEEE80211_MODE_11NA_HT40MINUS:  5Ghz, HT40 (ext ch -1)
+ * @IEEE80211_MODE_11NG_HT40PLUS:   2Ghz, HT40 (ext ch +1)
+ * @IEEE80211_MODE_11NG_HT40MINUS:  2Ghz, HT40 (ext ch -1)
+ * @IEEE80211_MODE_11NG_HT40:       2Ghz, Auto HT40.
+ * @IEEE80211_MODE_11NA_HT40:       5Ghz, Auto HT40
+ * @IEEE80211_MODE_11AC_VHT20:      5Ghz, VHT20
+ * @IEEE80211_MODE_11AC_VHT40PLUS:  5Ghz, VHT40 (Ext ch +1)
+ * @IEEE80211_MODE_11AC_VHT40MINUS: 5Ghz  VHT40 (Ext ch -1)
+ * @IEEE80211_MODE_11AC_VHT40:      5Ghz, VHT40
+ * @IEEE80211_MODE_11AC_VHT80:      5Ghz, VHT80
+ * @IEEE80211_MODE_11AC_VHT160:     5Ghz, VHT160
+ * @IEEE80211_MODE_11AC_VHT80_80:   5Ghz, VHT80_80
+ */
+enum ieee80211_phymode {
+	IEEE80211_MODE_AUTO             = 0,
+	IEEE80211_MODE_11A              = 1,
+	IEEE80211_MODE_11B              = 2,
+	IEEE80211_MODE_11G              = 3,
+	IEEE80211_MODE_FH               = 4,
+	IEEE80211_MODE_TURBO_A          = 5,
+	IEEE80211_MODE_TURBO_G          = 6,
+	IEEE80211_MODE_11NA_HT20        = 7,
+	IEEE80211_MODE_11NG_HT20        = 8,
+	IEEE80211_MODE_11NA_HT40PLUS    = 9,
+	IEEE80211_MODE_11NA_HT40MINUS   = 10,
+	IEEE80211_MODE_11NG_HT40PLUS    = 11,
+	IEEE80211_MODE_11NG_HT40MINUS   = 12,
+	IEEE80211_MODE_11NG_HT40        = 13,
+	IEEE80211_MODE_11NA_HT40        = 14,
+	IEEE80211_MODE_11AC_VHT20       = 15,
+	IEEE80211_MODE_11AC_VHT40PLUS   = 16,
+	IEEE80211_MODE_11AC_VHT40MINUS  = 17,
+	IEEE80211_MODE_11AC_VHT40       = 18,
+	IEEE80211_MODE_11AC_VHT80       = 19,
+	IEEE80211_MODE_11AC_VHT160      = 20,
+	IEEE80211_MODE_11AC_VHT80_80    = 21,
+};
+#define IEEE80211_MODE_MAX      (IEEE80211_MODE_11AC_VHT80_80 + 1)
+
+/**
+ * enum dfs_ieee80211_opmode - Device opmode.
+ * @IEEE80211_M_STA:     Infrastructure station
+ * @IEEE80211_M_IBSS:    IBSS (adhoc) station
+ * @IEEE80211_M_HOSTAP:  Software Access Point
+ *
+ * opmode value should be same as mlme ieee80211_opmode enum.
+ */
+enum dfs_ieee80211_opmode {
+	IEEE80211_M_STA         = 1,
+	IEEE80211_M_IBSS        = 0,
+	IEEE80211_M_HOSTAP      = 6,
+};
+
+/* Channel attributes */
+/* Turbo channel */
+#define IEEE80211_CHAN_TURBO            0x00000010
+
+/* CCK channel */
+#define IEEE80211_CHAN_CCK              0x00000020
+
+/* OFDM channel */
+#define IEEE80211_CHAN_OFDM             0x00000040
+
+/* 2 GHz spectrum channel. */
+#define IEEE80211_CHAN_2GHZ             0x00000080
+
+/* 5 GHz spectrum channel */
+#define IEEE80211_CHAN_5GHZ             0x00000100
+
+/* Only passive scan allowed */
+#define IEEE80211_CHAN_PASSIVE          0x00000200
+
+/* Dynamic CCK-OFDM channel */
+#define IEEE80211_CHAN_DYN              0x00000400
+
+/* GFSK channel (FHSS PHY) */
+#define IEEE80211_CHAN_GFSK             0x00000800
+
+/* Radar found on channel */
+#define IEEE80211_CHAN_DFS_RADAR        0x00001000
+
+/* 11a static turbo channel only */
+#define IEEE80211_CHAN_STURBO           0x00002000
+
+/* Half rate channel */
+#define IEEE80211_CHAN_HALF             0x00004000
+
+/* Quarter rate channel */
+#define IEEE80211_CHAN_QUARTER          0x00008000
+
+/* HT 20 channel */
+#define IEEE80211_CHAN_HT20             0x00010000
+
+/* HT 40 with extension channel above */
+#define IEEE80211_CHAN_HT40PLUS         0x00020000
+
+/* HT 40 with extension channel below */
+#define IEEE80211_CHAN_HT40MINUS        0x00040000
+
+/* HT 40 Intolerant */
+#define IEEE80211_CHAN_HT40INTOL        0x00080000
+
+/* VHT 20 channel */
+#define IEEE80211_CHAN_VHT20            0x00100000
+
+/* VHT 40 with extension channel above */
+#define IEEE80211_CHAN_VHT40PLUS        0x00200000
+
+/* VHT 40 with extension channel below */
+#define IEEE80211_CHAN_VHT40MINUS       0x00400000
+
+/* VHT 80 channel */
+#define IEEE80211_CHAN_VHT80            0x00800000
+
+/* HT 40 Intolerant mark bit for ACS use */
+#define IEEE80211_CHAN_HT40INTOLMARK    0x01000000
+
+/* channel temporarily blocked due to noise */
+#define IEEE80211_CHAN_BLOCKED          0x02000000
+
+/* VHT 160 channel */
+#define IEEE80211_CHAN_VHT160           0x04000000
+
+/* VHT 80_80 channel */
+#define IEEE80211_CHAN_VHT80_80         0x08000000
+
+/* flagext */
+#define IEEE80211_CHAN_DFS_RADAR_FOUND    0x01
+
+/* DFS required on channel */
+#define IEEE80211_CHAN_DFS              0x0002
+
+/* DFS required on channel for 2nd band of 80+80*/
+#define IEEE80211_CHAN_DFS_CFREQ2       0x0004
+
+/* if channel has been checked for DFS */
+#define IEEE80211_CHAN_DFS_CLEAR        0x0008
+
+/* excluded in 11D */
+#define IEEE80211_CHAN_11D_EXCLUDED     0x0010
+
+/* Channel Switch Announcement received on this channel */
+#define IEEE80211_CHAN_CSA_RECEIVED     0x0020
+
+/* ad-hoc is not allowed */
+#define IEEE80211_CHAN_DISALLOW_ADHOC   0x0040
+
+/* Station only channel */
+#define IEEE80211_CHAN_DISALLOW_HOSTAP  0x0080
+
+/* DFS radar history for slave device(STA mode) */
+#define IEEE80211_CHAN_HISTORY_RADAR    0x0100
+
+/* DFS CAC valid for  slave device(STA mode) */
+#define IEEE80211_CHAN_CAC_VALID        0x0200
+
+/* CONF: block the use of DFS channels */
+#define IEEE80211_FEXT_BLKDFSCHAN          0x00000200
+
+/* Useful combinations of channel characteristics. */
+#define IEEE80211_CHAN_FHSS \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
+
+#define IEEE80211_CHAN_A \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
+
+#define IEEE80211_CHAN_B \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
+
+#define IEEE80211_CHAN_PUREG \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
+
+#define IEEE80211_CHAN_G \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
+
+#define IEEE80211_CHAN_108A \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+
+#define IEEE80211_CHAN_108G \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+
+#define IEEE80211_CHAN_ST \
+	(IEEE80211_CHAN_108A | IEEE80211_CHAN_STURBO)
+
+#define IEEE80211_CHAN_11NG_HT20 \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_HT20)
+
+#define IEEE80211_CHAN_11NA_HT20 \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_HT20)
+
+#define IEEE80211_CHAN_11NG_HT40PLUS \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_HT40PLUS)
+
+#define IEEE80211_CHAN_11NG_HT40MINUS \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_HT40MINUS)
+
+#define IEEE80211_CHAN_11NA_HT40PLUS \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_HT40PLUS)
+
+#define IEEE80211_CHAN_11NA_HT40MINUS \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_HT40MINUS)
+
+#define IEEE80211_CHAN_ALL \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | \
+	IEEE80211_CHAN_GFSK | IEEE80211_CHAN_CCK | \
+	IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | \
+	IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40PLUS | \
+	IEEE80211_CHAN_HT40MINUS | IEEE80211_CHAN_VHT20 | \
+	IEEE80211_CHAN_VHT40PLUS | IEEE80211_CHAN_VHT40MINUS | \
+	IEEE80211_CHAN_VHT80 | IEEE80211_CHAN_VHT160 | \
+	IEEE80211_CHAN_VHT80_80 | IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)
+
+#define IEEE80211_CHAN_ALLTURBO \
+	(IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)
+
+#define IEEE80211_IS_CHAN_FHSS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
+
+#define IEEE80211_IS_CHAN_A(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
+
+#define IEEE80211_IS_CHAN_B(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
+
+#define IEEE80211_IS_CHAN_PUREG(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
+
+#define IEEE80211_IS_CHAN_G(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
+
+#define IEEE80211_IS_CHAN_ANYG(_c) \
+	(IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c))
+
+#define IEEE80211_IS_CHAN_ST(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)
+
+#define IEEE80211_IS_CHAN_108A(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A)
+
+#define IEEE80211_IS_CHAN_108G(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)
+
+#define IEEE80211_IS_CHAN_2GHZ(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
+
+#define IEEE80211_IS_CHAN_5GHZ(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
+
+#define IEEE80211_IS_CHAN_OFDM(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0)
+
+#define IEEE80211_IS_CHAN_CCK(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0)
+
+#define IEEE80211_IS_CHAN_GFSK(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
+
+#define IEEE80211_IS_CHAN_TURBO(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_TURBO) != 0)
+
+#define IEEE80211_IS_CHAN_WEATHER_RADAR(_c) \
+	((((_c)->ic_freq >= 5600) && ((_c)->ic_freq <= 5650)) || \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40PLUS) && (5580 == (_c)->ic_freq)))
+
+#define IEEE80211_IS_CHAN_STURBO(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_STURBO) != 0)
+
+#define IEEE80211_IS_CHAN_DTURBO(_c) \
+	(((_c)->ic_flags & \
+	(IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) == IEEE80211_CHAN_TURBO)
+
+#define IEEE80211_IS_CHAN_HALF(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HALF) != 0)
+
+#define IEEE80211_IS_CHAN_QUARTER(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_QUARTER) != 0)
+
+#define IEEE80211_IS_CHAN_PASSIVE(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_PASSIVE) != 0)
+
+#define IEEE80211_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(_c) \
+	(IEEE80211_IS_CHAN_DFS(_c->ic_curchan) ||       \
+	((IEEE80211_IS_CHAN_11AC_VHT160(_c->ic_curchan) || \
+	 IEEE80211_IS_CHAN_11AC_VHT80_80(_c->ic_curchan)) \
+	&& IEEE80211_IS_CHAN_DFS_CFREQ2(_c->ic_curchan)))
+
+#define IEEE80211_IS_CHAN_DFS(_c) \
+	(((_c)->ic_flagext & \
+	(IEEE80211_CHAN_DFS|IEEE80211_CHAN_DFS_CLEAR)) == IEEE80211_CHAN_DFS)
+
+#define IEEE80211_IS_CHAN_DFS_CFREQ2(_c) \
+	(((_c)->ic_flagext & \
+	(IEEE80211_CHAN_DFS_CFREQ2|IEEE80211_CHAN_DFS_CLEAR)) == \
+	IEEE80211_CHAN_DFS_CFREQ2)
+
+#define IEEE80211_IS_CHAN_DFSFLAG(_c) \
+	(((_c)->ic_flagext & IEEE80211_CHAN_DFS) == IEEE80211_CHAN_DFS)
+
+#define IEEE80211_IS_CHAN_DFSFLAG_CFREQ2(_c) \
+	(((_c)->ic_flagext & IEEE80211_CHAN_DFS_CFREQ2) == \
+	IEEE80211_CHAN_DFS_CFREQ2)
+
+#define IEEE80211_IS_CHAN_DISALLOW_ADHOC(_c) \
+	(((_c)->ic_flagext & IEEE80211_CHAN_DISALLOW_ADHOC) != 0)
+
+#define IEEE80211_IS_CHAN_11D_EXCLUDED(_c) \
+	(((_c)->ic_flagext & IEEE80211_CHAN_11D_EXCLUDED) != 0)
+
+#define IEEE80211_IS_CHAN_CSA(_c) \
+	(((_c)->ic_flagext & IEEE80211_CHAN_CSA_RECEIVED) != 0)
+
+#define IEEE80211_IS_CHAN_ODD(_c) \
+	(((_c)->ic_freq == 5170) || ((_c)->ic_freq == 5190) || \
+	((_c)->ic_freq == 5210) || ((_c)->ic_freq == 5230))
+
+#define IEEE80211_IS_CHAN_DISALLOW_HOSTAP(_c) \
+	(((_c)->ic_flagext & IEEE80211_CHAN_DISALLOW_HOSTAP) != 0)
+
+#define IEEE80211_IS_CHAN_11NG_HT20(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11NG_HT20) == \
+	 IEEE80211_CHAN_11NG_HT20)
+
+#define IEEE80211_IS_CHAN_11NA_HT20(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11NA_HT20) == \
+	 IEEE80211_CHAN_11NA_HT20)
+
+#define IEEE80211_IS_CHAN_11NG_HT40PLUS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11NG_HT40PLUS) == \
+	IEEE80211_CHAN_11NG_HT40PLUS)
+
+#define IEEE80211_IS_CHAN_11NG_HT40MINUS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11NG_HT40MINUS) == \
+	IEEE80211_CHAN_11NG_HT40MINUS)
+
+#define IEEE80211_IS_CHAN_11NA_HT40PLUS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11NA_HT40PLUS) == \
+	IEEE80211_CHAN_11NA_HT40PLUS)
+
+#define IEEE80211_IS_CHAN_11NA_HT40MINUS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11NA_HT40MINUS) == \
+	IEEE80211_CHAN_11NA_HT40MINUS)
+
+#define IEEE80211_IS_CHAN_11N(_c) \
+	(((_c)->ic_flags & (IEEE80211_CHAN_HT20 | \
+					  IEEE80211_CHAN_HT40PLUS | \
+					  IEEE80211_CHAN_HT40MINUS)) != 0)
+
+#define IEEE80211_IS_CHAN_11N_HT20(_c) \
+	(((_c)->ic_flags & (IEEE80211_CHAN_HT20)) != 0)
+
+#define IEEE80211_IS_CHAN_11N_HT40(_c) \
+	(((_c)->ic_flags & (IEEE80211_CHAN_HT40PLUS | \
+					IEEE80211_CHAN_HT40MINUS)) != 0)
+
+#define IEEE80211_IS_CHAN_11NG(_c) \
+	(IEEE80211_IS_CHAN_2GHZ((_c)) && IEEE80211_IS_CHAN_11N((_c)))
+
+#define IEEE80211_IS_CHAN_11NA(_c) \
+	(IEEE80211_IS_CHAN_5GHZ((_c)) && IEEE80211_IS_CHAN_11N((_c)))
+
+#define IEEE80211_IS_CHAN_11N_HT40PLUS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40PLUS) != 0)
+
+#define IEEE80211_IS_CHAN_11N_HT40MINUS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40MINUS) != 0)
+
+#define IEEE80211_IS_CHAN_HT20_CAPABLE(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT20) == IEEE80211_CHAN_HT20)
+
+#define IEEE80211_IS_CHAN_HT40PLUS_CAPABLE(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40PLUS) == IEEE80211_CHAN_HT40PLUS)
+
+#define IEEE80211_IS_CHAN_HT40MINUS_CAPABLE(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40MINUS) == \
+	 IEEE80211_CHAN_HT40MINUS)
+
+#define IEEE80211_IS_CHAN_HT40_CAPABLE(_c) \
+	(IEEE80211_IS_CHAN_HT40PLUS_CAPABLE(_c) || \
+	IEEE80211_IS_CHAN_HT40MINUS_CAPABLE(_c))
+
+#define IEEE80211_IS_CHAN_HT_CAPABLE(_c) \
+	(IEEE80211_IS_CHAN_HT20_CAPABLE(_c) || \
+	IEEE80211_IS_CHAN_HT40_CAPABLE(_c))
+
+#define IEEE80211_IS_CHAN_11N_CTL_CAPABLE(_c) \
+	IEEE80211_IS_CHAN_HT20_CAPABLE(_c)
+
+#define IEEE80211_IS_CHAN_11N_CTL_U_CAPABLE(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40PLUS) == IEEE80211_CHAN_HT40PLUS)
+
+#define IEEE80211_IS_CHAN_11N_CTL_L_CAPABLE(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_HT40MINUS) == \
+	 IEEE80211_CHAN_HT40MINUS)
+
+#define IEEE80211_IS_CHAN_11N_CTL_40_CAPABLE(_c) \
+	(IEEE80211_IS_CHAN_11N_CTL_U_CAPABLE((_c)) || \
+	IEEE80211_IS_CHAN_11N_CTL_L_CAPABLE((_c)))
+
+#define IEEE80211_IS_CHAN_VHT(_c) \
+	(((_c)->ic_flags & (IEEE80211_CHAN_VHT20 | \
+			    IEEE80211_CHAN_VHT40PLUS | \
+			    IEEE80211_CHAN_VHT40MINUS | \
+			    IEEE80211_CHAN_VHT80 | \
+			    IEEE80211_CHAN_VHT160 | \
+			    IEEE80211_CHAN_VHT80_80)) != 0)
+
+#define IEEE80211_IS_CHAN_11AC(_c) \
+	(IEEE80211_IS_CHAN_5GHZ((_c)) && IEEE80211_IS_CHAN_VHT((_c)))
+
+#define IEEE80211_CHAN_11AC_VHT20 \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_VHT20)
+
+#define IEEE80211_CHAN_11AC_VHT40 \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_VHT40PLUS | \
+	 IEEE80211_CHAN_VHT40MINUS)
+
+#define IEEE80211_CHAN_11AC_VHT40PLUS \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_VHT40PLUS)
+
+#define IEEE80211_CHAN_11AC_VHT40MINUS \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_VHT40MINUS)
+
+#define IEEE80211_CHAN_11AC_VHT80 \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_VHT80)
+
+#define IEEE80211_CHAN_11AC_VHT160 \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_VHT160)
+
+#define IEEE80211_CHAN_11AC_VHT80_80 \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_VHT80_80)
+
+#define IEEE80211_IS_CHAN_11AC_VHT20(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11AC_VHT20) == \
+	 IEEE80211_CHAN_11AC_VHT20)
+
+#define IEEE80211_IS_CHAN_11AC_VHT40(_c) \
+	(((_c)->ic_flags & (IEEE80211_CHAN_VHT40PLUS | \
+			    IEEE80211_CHAN_VHT40MINUS)) != 0)
+
+#define IEEE80211_IS_CHAN_11AC_VHT40PLUS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11AC_VHT40PLUS) == \
+	IEEE80211_CHAN_11AC_VHT40PLUS)
+
+#define IEEE80211_IS_CHAN_11AC_VHT40MINUS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11AC_VHT40MINUS) == \
+	IEEE80211_CHAN_11AC_VHT40MINUS)
+
+#define IEEE80211_IS_CHAN_11AC_VHT80(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11AC_VHT80) == \
+	 IEEE80211_CHAN_11AC_VHT80)
+
+#define IEEE80211_IS_CHAN_11AC_VHT160(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11AC_VHT160) == \
+	 IEEE80211_CHAN_11AC_VHT160)
+
+#define IEEE80211_IS_CHAN_11AC_VHT80_80(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_11AC_VHT80_80) == \
+	IEEE80211_CHAN_11AC_VHT80_80)
+
+#define IEEE80211_CH_HOPPING_SET_CHAN_BLOCKED(_c)    \
+	((_c)->ic_flags |= IEEE80211_CHAN_BLOCKED)
+
+#define IEEE80211_CH_HOPPING_IS_CHAN_BLOCKED(_c)    \
+	(((_c)->ic_flags & IEEE80211_CHAN_BLOCKED) == IEEE80211_CHAN_BLOCKED)
+
+#define IEEE80211_CH_HOPPING_CLEAR_CHAN_BLOCKED(_c)    \
+	((_c)->ic_flags &= ~IEEE80211_CHAN_BLOCKED)
+
+#define IEEE80211_IS_CHAN_RADAR(_c)    \
+	(((_c)->ic_flags & IEEE80211_CHAN_DFS_RADAR) == \
+	 IEEE80211_CHAN_DFS_RADAR)
+
+#define IEEE80211_CHAN_SET_RADAR(_c)    \
+	((_c)->ic_flags |= IEEE80211_CHAN_DFS_RADAR)
+
+#define IEEE80211_CHAN_CLR_RADAR(_c)    \
+	((_c)->ic_flags &= ~IEEE80211_CHAN_DFS_RADAR)
+
+#define IEEE80211_CHAN_SET_DISALLOW_ADHOC(_c)   \
+	((_c)->ic_flagext |= IEEE80211_CHAN_DISALLOW_ADHOC)
+
+#define IEEE80211_CHAN_SET_DISALLOW_HOSTAP(_c)   \
+	((_c)->ic_flagext |= IEEE80211_CHAN_DISALLOW_HOSTAP)
+
+#define IEEE80211_CHAN_SET_DFS(_c)  \
+	((_c)->ic_flagext |= IEEE80211_CHAN_DFS)
+
+#define IEEE80211_CHAN_SET_DFS_CLEAR(_c)  \
+	((_c)->ic_flagext |= IEEE80211_CHAN_DFS_CLEAR)
+
+#define IEEE80211_CHAN_EXCLUDE_11D(_c)  \
+	((_c)->ic_flagext |= IEEE80211_CHAN_11D_EXCLUDED)
+
+#define IEEE80211_IS_CHAN_HISTORY_RADAR(_c)    \
+	(((_c)->ic_flagext & IEEE80211_CHAN_HISTORY_RADAR) == \
+	IEEE80211_CHAN_HISTORY_RADAR)
+
+#define IEEE80211_CHAN_SET_HISTORY_RADAR(_c)    \
+	((_c)->ic_flagext |= IEEE80211_CHAN_HISTORY_RADAR)
+
+#define IEEE80211_CHAN_CLR_HISTORY_RADAR(_c)    \
+	((_c)->ic_flagext &= ~IEEE80211_CHAN_HISTORY_RADAR)
+
+#define IEEE80211_IS_CHAN_CAC_VALID(_c)    \
+	(((_c)->ic_flagext & IEEE80211_CHAN_CAC_VALID) == \
+	 IEEE80211_CHAN_CAC_VALID)
+
+#define IEEE80211_CHAN_SET_CAC_VALID(_c)    \
+	((_c)->ic_flagext |= IEEE80211_CHAN_CAC_VALID)
+
+#define IEEE80211_CHAN_CLR_CAC_VALID(_c)    \
+	((_c)->ic_flagext &= ~IEEE80211_CHAN_CAC_VALID)
+
+#define IEEE80211_CHAN_ANY      (-1)    /* token for ``any channel'' */
+
+#define IEEE80211_CHAN_ANYC \
+	((struct dfs_ieee80211_channel *) IEEE80211_CHAN_ANY)
+
+#endif /*  _NET80211__IEEE80211_H_ */

+ 59 - 0
umac/dfs/core/src/dfs_internal.h

@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008 Atheros Communications, 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.
+ */
+
+/**
+ * DOC: The structurs and functions in this file are used only within DFS
+ * component.
+ */
+
+#ifndef _DFS_INTERNAL_H_
+#define _DFS_INTERNAL_H_
+
+#include <qdf_timer.h>
+#include "dfs.h"
+
+/**
+ * enum DFS_DOMAIN - These defines should match the table from ah_internal.h
+ * @DFS_UNINIT_DOMAIN: Uninitialized dfs domain.
+ * @DFS_FCC_DOMAIN:    FCC3 dfs domain.
+ * @DFS_ETSI_DOMAIN:   ETSI dfs domain.
+ * @DFS_MKK4_DOMAIN:   Japan dfs domain.
+ */
+enum DFS_DOMAIN {
+	DFS_UNINIT_DOMAIN = 0,
+	DFS_FCC_DOMAIN    = 1,
+	DFS_ETSI_DOMAIN   = 2,
+	DFS_MKK4_DOMAIN   = 3
+};
+
+/* CAPABILITY: the device support STA DFS */
+#define IEEE80211_CEXT_STADFS 0x00000040
+
+#define IEEE80211_SELECT_NONDFS_AND_DFS 0
+#define IEEE80211_SELECT_NONDFS_ONLY    1
+#define IEEE80211_SELECT_NXT_CH_POST_RADAR_DETECT   0
+#define IEEE80211_SELECT_APRIORI_NXT_CH             1
+
+/**
+ * dfs_ieee80211_chan2freq() - Convert channel to frequency value.
+ * @chan: Pointer to dfs_ieee80211_channel structure.
+ */
+u_int dfs_ieee80211_chan2freq(struct dfs_ieee80211_channel *chan);
+
+#endif /*  _DFS_INTERNAL_H_ */

+ 122 - 0
umac/dfs/core/src/dfs_ioctl.h

@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2011, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010, Atheros Communications Inc.
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+/**
+ * DOC: This file has dfs 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_GET_CAC_VALID_TIME   20
+#define DFS_SET_CAC_VALID_TIME   21
+#define DFS_SHOW_NOLHISTORY      22
+
+/*
+ * Spectral IOCTLs use DFS_LAST_IOCTL as the base.
+ * This must always be the last IOCTL in DFS and have
+ * the highest value.
+ */
+#define DFS_LAST_IOCTL 26
+
+#ifndef IEEE80211_CHAN_MAX
+#define IEEE80211_CHAN_MAX 1023
+#endif
+
+/**
+ * struct dfsreq_nolelem - NOL elements.
+ * @nol_freq:          NOL channel frequency.
+ * @nol_chwidth:       NOL channel width.
+ * @nol_start_ticks:   OS ticks when the NOL timer started.
+ * @nol_timeout_ms:    Nol timeout value in msec.
+ */
+
+struct dfsreq_nolelem {
+	uint16_t        nol_freq;
+	uint16_t        nol_chwidth;
+	unsigned long   nol_start_ticks;
+	uint32_t        nol_timeout_ms;
+};
+
+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 - DFS ioctl params.
+ * @dfs_firpwr:     FIR pwr out threshold.
+ * @dfs_rrssi:      Radar rssi thresh.
+ * @dfs_height:     Pulse height thresh.
+ * @dfs_prssi:      Pulse rssi thresh.
+ * @dfs_inband:     Inband thresh.
+ * @dfs_relpwr:     Pulse relative pwr thresh.
+ * @dfs_relstep:    Pulse relative step thresh.
+ * @dfs_maxlen:     Pulse max duration.
+ */
+struct dfs_ioctl_params {
+	int32_t dfs_firpwr;
+	int32_t dfs_rrssi;
+	int32_t dfs_height;
+	int32_t dfs_prssi;
+	int32_t dfs_inband;
+	int32_t dfs_relpwr;
+	int32_t dfs_relstep;
+	int32_t dfs_maxlen;
+};
+
+#define DFS_IOCTL_PARAM_NOVAL  65535
+#define DFS_IOCTL_PARAM_ENABLE 0x8000
+
+#endif  /* _DFS_IOCTL_H_ */

+ 43 - 0
umac/dfs/core/src/dfs_ioctl_private.h

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010, Atheros Communications Inc.
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+/**
+ * DOC: This file has dfs param copy functions.
+ */
+
+#ifndef _DFS_IOCTL_PRIVATE_H_
+#define _DFS_IOCTL_PRIVATE_H_
+
+
+static inline void
+wlan_dfs_dfsparam_to_ioctlparam(struct wlan_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_ */

+ 179 - 0
umac/dfs/core/src/dfs_phyerr_tlv.h

@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2012, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * DOC: This file has Radar summary.
+ */
+
+#ifndef _DFS_PHYERR_TLV_H_
+#define _DFS_PHYERR_TLV_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
+
+#define SEARCH_FFT_REPORT_REG_3 0x02
+
+#define SEARCH_FFT_REPORT_SEG_ID   0x00000001
+#define SEARCH_FFT_REPORT_SEG_ID_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_TLV_H_ */

+ 212 - 0
umac/dfs/core/src/dfs_structs.h

@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2011-2012, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * DOC: This file has dfs capability, dfs pulse structures.
+ */
+
+#ifndef _DFS_STRUCTS_H_
+#define _DFS_STRUCTS_H_
+
+/**
+ * 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 WLAN_DFS_PHYERR_PARAM_NOVAL   0xFFFF
+#define WLAN_DFS_PHYERR_PARAM_ENABLE  0x8000
+
+/**
+ * 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 wlan_dfs_caps - DFS capability structure.
+ * @wlan_dfs_ext_chan_ok:          Can radar be detected on the extension chan?
+ * @wlan_dfs_combined_rssi_ok:     Can use combined radar RSSI?
+ * @wlan_dfs_use_enhancement:      This 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.
+ * @wlan_strong_signal_diversiry:  Strong Signal fast diversity count.
+ * @wlan_chip_is_bb_tlv:           Chip is BB TLV?
+ * @wlan_chip_is_over_sampled:     Is Over sampled.
+ * @wlan_chip_is_ht160:            IS VHT160?
+ * @wlan_chip_is_false_detect:     Is False detected?
+ * @wlan_fastdiv_val:              Goes with wlan_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.
+ */
+struct wlan_dfs_caps {
+	uint32_t wlan_dfs_ext_chan_ok:1,
+			 wlan_dfs_combined_rssi_ok:1,
+			 wlan_dfs_use_enhancement:1,
+			 wlan_strong_signal_diversiry:1,
+			 wlan_chip_is_bb_tlv:1,
+			 wlan_chip_is_over_sampled:1,
+			 wlan_chip_is_ht160:1,
+			 wlan_chip_is_false_detect:1;
+	uint32_t wlan_fastdiv_val;
+};
+
+/**
+ * struct dfs_pulse - DFS pulses.
+ * @rp_numpulses:         Num of pulses in radar burst.
+ * @rp_pulsedur:          Duration of each pulse in usecs.
+ * @rp_pulsefreq:         Frequency of pulses in burst.
+ * @rp_max_pulsefreq:     Frequency of pulses in burst.
+ * @rp_patterntype:       fixed or variable pattern type.
+ * @rp_pulsevar:          Time variation of pulse duration for matched
+ *                        filter (single-sided) in usecs.
+ * @rp_threshold:         Threshold for MF output to indicate radar match.
+ * @rp_mindur:            Min pulse duration to be considered for this pulse
+ *                        type.
+ * @rp_maxdur:            Min pulse duration to be considered for this pulse
+ *                        type.
+ * @rp_rssithresh:        Minimum rssi to be considered a radar pulse.
+ * @rp_meanoffset:        Offset for timing adjustment.
+ * @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.
+ * @rp_ignore_pri_window: Ignore PRI window.
+ * @rp_pulseid:           Unique ID for identifying filter.
+ */
+struct dfs_pulse {
+	uint32_t  rp_numpulses;
+	uint32_t  rp_pulsedur;
+	uint32_t  rp_pulsefreq;
+	uint32_t  rp_max_pulsefreq;
+	uint32_t  rp_patterntype;
+	uint32_t  rp_pulsevar;
+	uint32_t  rp_threshold;
+	uint32_t  rp_mindur;
+	uint32_t  rp_maxdur;
+	uint32_t  rp_rssithresh;
+	uint32_t  rp_meanoffset;
+	int32_t   rp_rssimargin;
+	uint32_t  rp_ignore_pri_window;
+	uint32_t  rp_pulseid;
+};
+
+/**
+ * struct dfs_bin5pulse - DFS bin5 pulse.
+ * @b5_threshold:    Number of bin5 pulses to indicate detection.
+ * @b5_mindur:       Min duration for a bin5 pulse.
+ * @b5_maxdur:       Max duration for a bin5 pulse.
+ * @b5_timewindow:   Window over which to count bin5 pulses.
+ * @b5_rssithresh:   Min rssi to be considered a pulse.
+ * @b5_rssimargin:   rssi threshold margin. In Turbo Mode HW reports rssi 3dB
+ */
+struct dfs_bin5pulse {
+	uint32_t  b5_threshold;
+	uint32_t  b5_mindur;
+	uint32_t  b5_maxdur;
+	uint32_t  b5_timewindow;
+	uint32_t  b5_rssithresh;
+	uint32_t  b5_rssimargin;
+};
+
+/**
+ * struct dfs_nol_chan_entry - DFS NOL representation.
+ * @nol_chfreq:      Centre frequency, MHz .
+ * @nol_chwidth:     Width, MHz.
+ * @nol_start_ticks: Start ticks, OS specific.
+ * @nol_timeout_ms:  Timeout, ms
+ */
+struct dfs_nol_chan_entry {
+	uint32_t      nol_chfreq;
+	uint32_t      nol_chwidth;
+	unsigned long nol_start_ticks;
+	uint32_t      nol_timeout_ms;
+};
+
+/**
+ * struct wlan_dfs_phyerr_param - DFS Phyerr structure.
+ * @pe_firpwr:     FIR pwr out threshold.
+ * @pe_rrssi:      Radar rssi thresh.
+ * @pe_height:     Pulse height thresh.
+ * @pe_prssi:      Pulse rssi thresh.
+ * @pe_inband:     Inband thresh.
+ * @pe_relpwr:     Relative power threshold in 0.5dB steps.
+ * @pe_relstep:    Pulse Relative step threshold in 0.5dB steps.
+ * @pe_maxlen:     Max length of radar sign in 0.8us units.
+ * @pe_usefir128:  Use the average in-band power measured over 128 cycles.
+ * @pe_blockradar: 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.
+ * @pe_enmaxrssi:  Enable to use the max rssi instead of the last rssi during
+ *                 fine gain changes for radar detection.
+ */
+struct wlan_dfs_phyerr_param {
+	int32_t    pe_firpwr;
+	int32_t    pe_rrssi;
+	int32_t    pe_height;
+	int32_t    pe_prssi;
+	int32_t    pe_inband;
+	uint32_t   pe_relpwr;
+	uint32_t   pe_relstep;
+	uint32_t   pe_maxlen;
+	bool       pe_usefir128;
+	bool       pe_blockradar;
+	bool       pe_enmaxrssi;
+};
+
+/**
+ * wlan_dfs_phyerr_init_noval() - Fill wlan_dfs_phyerr_param with 0xFF.
+ * @pe: Pointer to wlan_dfs_phyerr_param structure.
+ */
+static inline void wlan_dfs_phyerr_init_noval(struct wlan_dfs_phyerr_param *pe)
+{
+	pe->pe_firpwr = WLAN_DFS_PHYERR_PARAM_NOVAL;
+	pe->pe_rrssi = WLAN_DFS_PHYERR_PARAM_NOVAL;
+	pe->pe_height = WLAN_DFS_PHYERR_PARAM_NOVAL;
+	pe->pe_prssi = WLAN_DFS_PHYERR_PARAM_NOVAL;
+	pe->pe_inband = WLAN_DFS_PHYERR_PARAM_NOVAL;
+	pe->pe_relpwr = WLAN_DFS_PHYERR_PARAM_NOVAL;
+	pe->pe_relstep = WLAN_DFS_PHYERR_PARAM_NOVAL;
+	pe->pe_maxlen = WLAN_DFS_PHYERR_PARAM_NOVAL;
+}
+
+/**
+ * struct wlan_dfs_radar_tab_info - Radar table information.
+ * @dfsdomain:         DFS domain.
+ * @numradars:         Number of radars.
+ * @dfs_radars:        Pointer to dfs_pulse structure.
+ * @numb5radars:       NUM5 radars.
+ * @b5pulses:          BIN5 radars.
+ * @dfs_defaultparams: phyerr params.
+ */
+struct wlan_dfs_radar_tab_info {
+	uint32_t          dfsdomain;
+	int               numradars;
+	struct dfs_pulse *dfs_radars;
+	int               numb5radars;
+	struct dfs_bin5pulse *b5pulses;
+	struct wlan_dfs_phyerr_param dfs_defaultparams;
+};
+
+#endif  /* _DFS_STRUCTS_H_ */

+ 319 - 0
umac/dfs/core/src/filtering/dfs_ar.c

@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: This file contains now obsolete code which used to implement AR
+ * (Adaptive Radio) feature for older chipsets.
+ */
+
+#include "../dfs.h"
+
+#define UPDATE_TOP_THREE_PEAKS(_histo, _peakPtrList, _currWidth) \
+	do {	\
+		if ((_histo)[(_peakPtrList)[0]] < (_histo)[(_currWidth)]) { \
+			(_peakPtrList)[2] = \
+			(_currWidth != (_peakPtrList)[1]) ? \
+			(_peakPtrList)[1] : (_peakPtrList)[2];  \
+			(_peakPtrList)[1] = (_peakPtrList)[0]; \
+			(_peakPtrList)[0] = (_currWidth); \
+		} else if ((_currWidth != (_peakPtrList)[0]) \
+				&& ((_histo)[(_peakPtrList)[1]] <	\
+					(_histo)[(_currWidth)])) { \
+			(_peakPtrList)[2] = (_peakPtrList)[1]; \
+			(_peakPtrList)[1] = (_currWidth);      \
+		} else if ((_currWidth != (_peakPtrList)[1])   \
+				&& (_currWidth != (_peakPtrList)[0])  \
+				&& ((_histo)[(_peakPtrList)[2]] < \
+					(_histo)[(_currWidth)])) { \
+			(_peakPtrList)[2] = (_currWidth);  \
+		} \
+	} while (0)
+
+void dfs_process_ar_event(struct wlan_dfs *dfs,
+		struct dfs_ieee80211_channel *chan)
+{
+	struct dfs_ar_state *ar;
+	struct dfs_event *re = NULL;
+	uint32_t sumpeak = 0, numpeaks = 0;
+	uint32_t rssi = 0, width = 0;
+	uint32_t origregionsum = 0, i = 0;
+	uint16_t thistimestamp;
+	int empty;
+
+	if (dfs == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+			"%s: dfs is NULL\n", __func__);
+		return;
+	}
+
+	ar = (struct dfs_ar_state *) &(dfs->dfs_ar_state);
+	WLAN_ARQ_LOCK(dfs);
+	empty = STAILQ_EMPTY(&(dfs->dfs_arq));
+	WLAN_ARQ_UNLOCK(dfs);
+	while (!empty) {
+		WLAN_ARQ_LOCK(dfs);
+		re = STAILQ_FIRST(&(dfs->dfs_arq));
+		if (re != NULL)
+			STAILQ_REMOVE_HEAD(&(dfs->dfs_arq), re_list);
+		WLAN_ARQ_UNLOCK(dfs);
+		if (re == NULL)
+			return;
+
+		thistimestamp = re->re_ts;
+		rssi = re->re_rssi;
+		width = re->re_dur;
+
+		/* Return the dfs event to the free event list. */
+		qdf_mem_zero(re, sizeof(struct dfs_event));
+		WLAN_DFSEVENTQ_LOCK(dfs);
+		STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), re, re_list);
+		WLAN_DFSEVENTQ_UNLOCK(dfs);
+
+		/*
+		 * Determine if current radar is an extension of previous
+		 * radar.
+		 */
+		if (ar->ar_prevwidth == 255) {
+			/*
+			 * Tag on previous width for consideraion of low data
+			 * rate ACKs.
+			 */
+			ar->ar_prevwidth += width;
+			width = (width == 255) ? 255 : ar->ar_prevwidth;
+		} else if ((width == 255) &&
+				(ar->ar_prevwidth == 510 ||
+				 ar->ar_prevwidth == 765 ||
+				 ar->ar_prevwidth == 1020)) {
+			/*
+			 * Aggregate up to 5 consecuate max radar widths to
+			 * consider 11Mbps long preamble 1500-byte pkts.
+			 */
+			ar->ar_prevwidth += width;
+		} else if (ar->ar_prevwidth == 1275 && width != 255) {
+			/* Found 5th consecute maxed out radar, reset history */
+			width += ar->ar_prevwidth;
+			ar->ar_prevwidth = 0;
+		} else if (ar->ar_prevwidth > 255) {
+			/*
+			 * Ignore if there are less than 5 consecutive maxed
+			 * out radars.
+			 */
+			ar->ar_prevwidth = width;
+			width = 255;
+		} else {
+			ar->ar_prevwidth = width;
+		}
+
+		/*
+		 * For ignoring noises with radar duration in ranges
+		 * of 3-30: AP4x. Region 7 - 5.5Mbps (long pre)
+		 * ACK = 270 = 216 us.
+		 */
+		if ((width >= 257 && width <= 278) ||
+				/*
+				 * Region 8 - 2Mbps (long pre)
+				 * ACKC = 320 = 256us.
+				 */
+				(width >= 295 && width <= 325) ||
+				(width >= 1280 && width <= 1300)) {
+			uint16_t wraparoundadj = 0;
+			uint16_t base = (width >= 1280) ? 1275 : 255;
+
+			if (thistimestamp < ar->ar_prevtimestamp)
+				wraparoundadj = 32768;
+
+			if ((thistimestamp + wraparoundadj -
+				    ar->ar_prevtimestamp) != (width - base))
+				width = 1;
+		}
+		if (width <= 10) {
+			WLAN_ARQ_LOCK(dfs);
+			empty = STAILQ_EMPTY(&(dfs->dfs_arq));
+			WLAN_ARQ_UNLOCK(dfs);
+			continue;
+		}
+
+		/*
+		 * Overloading the width=2 in: Store a count of
+		 * radars w/max duration and high RSSI (not noise)
+		 */
+		if ((width == 255) && (rssi > DFS_AR_RSSI_THRESH_STRONG_PKTS))
+			width = 2;
+
+		/*
+		 * Overloading the width=3 bin:
+		 * Double and store a count of rdars of durtaion that matches
+		 * 11Mbps (long preamble) TCP ACKs or 1500-byte data packets.
+		 */
+		if ((width >= 1280 && width <= 1300) ||
+				(width >= 318 && width <= 325)) {
+			width = 3;
+			ar->ar_phyerrcount[3] += 2;
+			ar->ar_acksum += 2;
+		}
+
+		/* Build histogram of radar duration. */
+		if (width > 0 && width <= 510)
+			ar->ar_phyerrcount[width]++;
+		else {
+			/* Invalid radar width, throw it away. */
+			WLAN_ARQ_LOCK(dfs);
+			empty = STAILQ_EMPTY(&(dfs->dfs_arq));
+			WLAN_ARQ_UNLOCK(dfs);
+			continue;
+		}
+
+		/* Received radar of interest (i.e., signature match),
+		 * proceed to check if there is enough neighboring
+		 * traffic to drop out of Turbo.
+		 */
+		/* Region 0: 24Mbps ACK = 35 = 28us */
+		if ((width >= REG0_MIN_WIDTH && width <= REG0_MAX_WIDTH) ||
+			/* Region 1: 12Mbps ACK = 40 = 32us */
+			(width >= REG1_MIN_WIDTH && width <= REG1_MAX_WIDTH) ||
+			/* Region 2: 6Mbps ACK = 55 = 44us */
+			(width >= REG2_MIN_WIDTH && width <= REG2_MAX_WIDTH) ||
+			/* Region 3: 11Mbps ACK = 135 = 108us */
+			(width >= REG3_MIN_WIDTH && width <= REG3_MAX_WIDTH) ||
+			/* Region 4: 5.5Mbps ACK = 150 = 120us */
+			(width >= REG4_MIN_WIDTH && width <= REG4_MAX_WIDTH) ||
+			/* Region 5: 2Mbps ACK = 200 = 160us */
+			(width >= REG5_MIN_WIDTH && width <= REG5_MAX_WIDTH) ||
+			/* Region 6: 1Mbps ACK = 400 = 320us */
+			(width >= REG6_MIN_WIDTH && width <= REG6_MAX_WIDTH) ||
+			/* Region 7: 5.5Mbps (Long Pre) ACK = 270 = 216us. */
+			(width >= REG7_MIN_WIDTH && width <= REG7_MAX_WIDTH) ||
+			/* Region 8: 2Mbps (Long Pre) ACK = 320 = 256us. */
+			(width >= REG8_MIN_WIDTH && width <= REG8_MAX_WIDTH) ||
+			/*
+			 * Ignoring Region 9 due to overlap with 255 which is
+			 * same as board noise.
+			 */
+			/* Region 9: 11Mbps (Long Pre) ACK = 255 = 204us. */
+			(width == 3)) {
+			ar->ar_acksum++;
+			/*
+			 * Double the count for strong radars that match
+			 * one of the ACK signatures.
+			 */
+			if (rssi > DFS_AR_RSSI_DOUBLE_THRESHOLD) {
+				ar->ar_phyerrcount[width]++;
+				ar->ar_acksum++;
+			}
+			UPDATE_TOP_THREE_PEAKS(ar->ar_phyerrcount,
+				ar->ar_peaklist, width);
+			/* Sum the counts of these peaks. */
+			numpeaks = DFS_AR_MAX_NUM_PEAKS;
+			origregionsum = ar->ar_acksum;
+			for (i = 0; i < DFS_AR_MAX_NUM_PEAKS; i++) {
+				if (ar->ar_peaklist[i] > 0) {
+					if ((i == 0) &&
+						(ar->ar_peaklist[i] == 3) &&
+						(ar->ar_phyerrcount[3] <
+						 ar->ar_phyerrcount[2]) &&
+						(ar->ar_phyerrcount[3] > 6)) {
+						/*
+						 * If the top peak is one that
+						 * matches the 11Mbps long
+						 * preamble TCP Ack/1500-byte
+						 * data, include the count for
+						 * radars that hav emax duration
+						 * and high rssi (width = 2) to
+						 * boost the sum for the PAR
+						 * test that follows.
+						 */
+						sumpeak +=
+						    (ar->ar_phyerrcount[2] +
+						     ar->ar_phyerrcount[3]);
+						ar->ar_acksum +=
+						    (ar->ar_phyerrcount[2] +
+						     ar->ar_phyerrcount[3]);
+					} else {
+						sumpeak += ar->ar_phyerrcount[
+						    ar->ar_peaklist[i]];
+					}
+				} else
+					numpeaks--;
+			}
+			/*
+			 * If sum of patterns matches exceeds packet threshold,
+			 * perform comparison between peak-to-avg ratio against
+			 * parThreshold.
+			 */
+			if ((ar->ar_acksum > ar->ar_packetthreshold) &&
+				((sumpeak * DFS_AR_REGION_WIDTH) >
+				 (ar->ar_parthreshold * numpeaks *
+				  ar->ar_acksum))) {
+				/*
+				 * Neighboring traffic detected, get out of
+				 * Turbo.
+				 */
+				chan->ic_flagext |= CHANNEL_INTERFERENCE;
+				qdf_mem_zero(ar->ar_peaklist,
+						sizeof(ar->ar_peaklist));
+				ar->ar_acksum = 0;
+				qdf_mem_zero(ar->ar_phyerrcount,
+					sizeof(ar->ar_phyerrcount));
+			} else {
+				/*
+				 * Reset sum of matches to discount the count
+				 * of strong radars with max duration.
+				 */
+				ar->ar_acksum = origregionsum;
+			}
+		}
+		ar->ar_prevtimestamp = thistimestamp;
+		WLAN_ARQ_LOCK(dfs);
+		empty = STAILQ_EMPTY(&(dfs->dfs_arq));
+		WLAN_ARQ_UNLOCK(dfs);
+	}
+}
+
+void dfs_reset_ar(struct wlan_dfs *dfs)
+{
+	if (dfs == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s: sc_dfs is NULL\n",
+			__func__);
+		return;
+	}
+
+	qdf_mem_zero(&dfs->dfs_ar_state, sizeof(dfs->dfs_ar_state));
+	dfs->dfs_ar_state.ar_packetthreshold = DFS_AR_PKT_COUNT_THRESH;
+	dfs->dfs_ar_state.ar_parthreshold = DFS_AR_ACK_DETECT_PAR_THRESH;
+}
+
+void dfs_reset_arq(struct wlan_dfs *dfs)
+{
+	struct dfs_event *event;
+
+	if (dfs == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s: sc_dfs is NULL\n",
+			__func__);
+		return;
+	}
+
+	WLAN_ARQ_LOCK(dfs);
+	WLAN_DFSEVENTQ_LOCK(dfs);
+	while (!STAILQ_EMPTY(&(dfs->dfs_arq))) {
+		event = STAILQ_FIRST(&(dfs->dfs_arq));
+		STAILQ_REMOVE_HEAD(&(dfs->dfs_arq), re_list);
+		qdf_mem_zero(event, sizeof(struct dfs_event));
+		STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), event, re_list);
+	}
+	WLAN_DFSEVENTQ_UNLOCK(dfs);
+	WLAN_ARQ_UNLOCK(dfs);
+}

+ 489 - 0
umac/dfs/core/src/filtering/dfs_bindetects.c

@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: DFS specs specify various types of radars to be detected.
+ * Each separate type is called a Bin and has different characteristics.
+ * This file contains the functionality to look at a group of pulses and
+ * to detect whether we have detected a valid radar waveform. To do that,
+ * it must match the group against each different Bin's characteristics.
+ */
+
+#include "../dfs.h"
+
+int dfs_bin_fixedpattern_check(struct wlan_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;
+	uint64_t 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, WLAN_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, WLAN_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, WLAN_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, WLAN_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,
+							WLAN_DEBUG_DFS2,
+							"zero PRI: numpulses %u\n",
+							numpulses);
+						break;
+					}
+				}
+			}
+		}
+	}
+	if (numpulses >= dfs_get_filter_threshold(dfs, rf, ext_chan_flag)) {
+		DFS_DPRINTK(dfs, WLAN_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 wlan_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) == 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, WLAN_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, WLAN_DEBUG_DFS2, "dl firstElem = %d  lastElem = %d\n",
+			dl->dl_firstelem, dl->dl_lastelem);
+}
+
+int dfs_bin_check(struct wlan_dfs *dfs,
+		struct dfs_filter *rf,
+		uint32_t deltaT,
+		uint32_t width,
+		int ext_chan_flag)
+{
+	struct dfs_delayline *dl;
+	uint32_t refpri, refdur, searchpri, deltapri, deltapri_2, deltapri_3;
+	uint32_t averagerefpri, n, i, primargin, durmargin, highscore;
+	uint32_t highscoreindex;
+	int score[DFS_MAX_DL_SIZE], delayindex, dindex, found = 0;
+	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;
+	}
+
+	qdf_mem_zero(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, WLAN_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;
+}
+
+void count_the_other_delay_elements(struct wlan_dfs *dfs,
+		struct dfs_filter *rf,
+		struct dfs_delayline *dl,
+		uint32_t i,
+		uint32_t refpri,
+		uint32_t refdur,
+		uint32_t primargin,
+		uint32_t durmargin,
+		int *numpulses,
+		uint32_t *prev_good_timestamp,
+		int fundamentalpri
+		)
+{
+	int delayindex;
+	uint32_t searchpri, searchdur, deltadur, deltapri1, deltapri2;
+	uint32_t j = 0, delta_time_stamps, delta_ts_variance, deltapri;
+	int dindex, primatch, numpulsetochk = 2;
+
+	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;
+	deltadur = DFS_DIFF(searchdur, refdur);
+	deltapri = DFS_DIFF(searchpri, refpri);
+	deltapri1 = DFS_DIFF(searchpri, refpri);
+	deltapri2 = DFS_DIFF(searchpri, 2 * 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, WLAN_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);
+	}
+}
+
+int dfs_bin_pri_check(struct wlan_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, deltapri = 0;
+	uint32_t averagerefpri = 0, MatchCount = 0;
+	uint32_t prev_good_timestamp = 0;
+	int dindex;
+	uint32_t i, primargin, durmargin, highscore = score;
+	uint32_t highscoreindex = 0;
+	/*
+	 * First pulse in the burst is most likely being filtered out based on
+	 * maxfilterlen.
+	 */
+	int numpulses = 1;
+	int priscorechk = 1;
+
+	/* Use the adjusted PRI margin to reduce false alarms
+	 * For non fixed pattern types, rf->rf_patterntype=0.
+	 */
+	primargin = dfs_get_pri_margin(dfs, ext_chan_flag,
+			(rf->rf_patterntype == 1));
+
+	if ((refpri > rf->rf_maxpri) || (refpri < rf->rf_minpri)) {
+		numpulses = 0;
+		return numpulses;
+	}
+
+	if (rf->rf_maxdur < 10)
+		durmargin = 4;
+	else
+		durmargin = 6;
+
+	if ((!rf->rf_fixed_pri_radar_pulse)) {
+		if (rf->rf_ignore_pri_window == 1)
+			priscorechk = (rf->rf_threshold >> 1);
+		else
+			priscorechk = 1;
+
+		MatchCount = 0;
+		if (score > priscorechk) {
+			for (i = 0; i < dl->dl_numelems; i++) {
+				dindex = (dl->dl_firstelem + i) &
+					DFS_MAX_DL_MASK;
+				searchpri = dl->dl_elems[dindex].de_time;
+				deltapri = DFS_DIFF(searchpri, refpri);
+				if (deltapri < primargin) {
+					averagerefpri += searchpri;
+					MatchCount++;
+				}
+			}
+			if (rf->rf_patterntype != 2) {
+				if (MatchCount > 0)
+					refpri = (averagerefpri / MatchCount);
+			} 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, WLAN_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++)
+		count_the_other_delay_elements(dfs, rf, dl, i, refpri, refdur,
+			primargin, durmargin, &numpulses, &prev_good_timestamp,
+			fundamentalpri);
+
+	return numpulses;
+}

+ 84 - 0
umac/dfs/core/src/filtering/dfs_debug.c

@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: It contains useful print functions that can be used for debug.
+ * Add all debug related functionality into this file.
+ */
+#include "../dfs.h"
+#include "wlan_dfs_lmac_api.h"
+
+void dfs_print_delayline(struct wlan_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, WLAN_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 wlan_dfs *dfs, struct dfs_filter *rf)
+{
+	DFS_DPRINTK(dfs, WLAN_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 wlan_dfs *dfs)
+{
+	struct dfs_filtertype *ft = NULL;
+	struct dfs_filter *rf;
+	int i, j;
+
+	if (dfs == NULL) {
+		DFS_DPRINTK(dfs, WLAN_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_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+				"===========ft->ft_numfilters = %u===========\n",
+				ft->ft_numfilters);
+			for (j = 0; j < ft->ft_numfilters; j++) {
+				rf = &(ft->ft_filters[j]);
+				DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+					"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);
+			}
+		}
+	}
+}

+ 811 - 0
umac/dfs/core/src/filtering/dfs_fcc_bin5.c

@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: FCC Bin5 are special type of radars because they "chirp". Basically the
+ * pulses move across the frequency band and are called chirping pulses.
+ * dfs_check_chirping() actually examines the FFT data contained in the PHY
+ * error information to figure out whether the pulse is moving across
+ * frequencies.
+ */
+
+#include "../dfs.h"
+#include "wlan_dfs_mlme_api.h"
+#include "../dfs_channel.h"
+
+int dfs_bin5_check_pulse(struct wlan_dfs *dfs, struct dfs_event *re,
+		struct dfs_bin5radars *br)
+{
+	int b5_rssithresh = br->br_pulse.b5_rssithresh;
+
+	DFS_DPRINTK(dfs, WLAN_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, WLAN_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;
+	}
+
+#define CHANNEL_TURBO 0x00010
+	/* Adjust the filter threshold for rssi in non TURBO mode. */
+	if (!(dfs->dfs_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, WLAN_DEBUG_DFS_BIN5,
+			"%s: dur=%d, rssi=%d - adding!\n",
+			__func__, (int)re->re_dur, (int)re->re_rssi);
+		return 1;
+	}
+
+	DFS_DPRINTK(dfs, WLAN_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 wlan_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;  /* 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;
+}
+
+void bin5_rules_check_internal(struct wlan_dfs *dfs,
+		struct dfs_bin5radars *br,
+		uint32_t *bursts,
+		uint32_t *numevents,
+		uint32_t prev,
+		uint32_t i,
+		uint32_t this)
+{
+	uint64_t pri = 0;
+	uint32_t width_diff = 0, rssi_diff = 0;
+	int index[DFS_MAX_B5_SIZE];
+
+	/* 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 = br->br_elems[this].be_ts;
+	}
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_BIN5,
+		" pri=%llu this.ts=%llu this.dur=%d this.rssi=%d prev.ts=%llu\n",
+		(uint64_t)pri,
+		(uint64_t) br->br_elems[this].be_ts,
+		(int) br->br_elems[this].be_dur,
+		(int) br->br_elems[this].be_rssi,
+		(uint64_t)br->br_elems[prev].be_ts);
+
+	if (((pri >= DFS_BIN5_PRI_LOWER_LIMIT) &&
+		    /*pri: pulse repitition interval in us. */
+		    (pri <= DFS_BIN5_PRI_HIGHER_LIMIT))) {
+		/*
+		 * 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, WLAN_DEBUG_DFS_BIN5,
+					"%s %d Bin5 rssi_diff=%d\n",
+					__func__, __LINE__, rssi_diff);
+		} else
+			DFS_DPRINTK(dfs, WLAN_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. */
+		(*bursts)++;
+	} else{
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_BIN5,
+			"%s %d Bin5 PRI check fail pri=%llu\n",
+			__func__, __LINE__, (uint64_t)pri);
+	}
+}
+
+int dfs_bin5_check(struct wlan_dfs *dfs)
+{
+	struct dfs_bin5radars *br;
+	uint32_t n = 0, i = 0, i1 = 0, this = 0, prev = 0;
+	uint32_t bursts = 0, total_diff = 0, average_diff = 0;
+	uint32_t total_width = 0, average_width = 0, numevents = 0;
+
+	if (dfs == NULL) {
+		DFS_PRINTK("%s: 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, WLAN_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;
+			}
+			bin5_rules_check_internal(dfs, br, &bursts, &numevents,
+					prev, i, this);
+			prev = this;
+		}
+
+		DFS_DPRINTK(dfs, WLAN_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;
+
+			DFS_DPRINTK(dfs, WLAN_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;
+}
+
+/**
+ * dfs_check_chirping_sowl() - Chirp detection for Sowl/Howl.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer.
+ * @datalen: Phyerr buf length
+ * @is_ctl: detected on primary channel.
+ * @is_ext: detected on extension channel.
+ * @slope: Slope
+ * @is_dc: DC found
+ *
+ * Return: 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.
+ */
+static int dfs_check_chirping_sowl(struct wlan_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 = 0, ctl_low0 = 0, ctl_slope0 = 0;
+	int ext_high0 = 0, ext_low0 = 0, ext_slope0 = 0;
+	int ctl_high1 = 0, ctl_low1 = 0, ctl_slope1 = 0;
+	int ext_high1 = 0, ext_low1 = 0, 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, WLAN_DEBUG_DFS_BIN5_FFT,
+			"fft_data_ptr=0x%p\t", fft_data_ptr);
+		DFS_DPRINTK(dfs, WLAN_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, WLAN_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, WLAN_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, WLAN_DEBUG_DFS_BIN5_FFT | WLAN_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, WLAN_DEBUG_DFS_BIN5 | WLAN_DEBUG_DFS_BIN5_FFT |
+			WLAN_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
+}
+
+/**
+ * dfs_check_chirping_merlin() - Merlin (and Osprey, etc) chirp radar chirp
+ *                               detection.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer
+ * @datalen: Phyerr buf length
+ * @is_ctl: detected on primary channel.
+ * @is_ext: detected on extension channel.
+ * @slope: Slope
+ * @is_dc: DC found
+ */
+static int dfs_check_chirping_merlin(struct wlan_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)
+	/* This should be between 1 and 3. Default is 1. */
+#define DELTA_STEP 1
+	/* Number of Diffs to compute. valid range is 2-4. */
+#define NUM_DIFFS  3
+	/* Threshold for difference of delta peaks. */
+#define MAX_DIFF   2
+	/* Max. number of strong bins for narrow band. */
+#define BIN_COUNT_MAX 6
+
+	/* 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 (IEEE80211_IS_CHAN_11N_HT40(dfs->dfs_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 (IEEE80211_IS_CHAN_11N_HT40MINUS(dfs->dfs_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, WLAN_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, WLAN_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)) {
+		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 (!IEEE80211_IS_CHAN_11N_HT40(dfs->dfs_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, WLAN_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, WLAN_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, WLAN_DEBUG_DFS_BIN5_FFT,
+			"%s: CHIRPING_BEFORE_STRONGBIN_YES\n",
+			__func__);
+	} else {
+		DFS_DPRINTK(dfs, WLAN_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, WLAN_DEBUG_DFS_BIN5_FFT,
+			"i=%d, computed bin_count=%d\n",
+			i, bin_count);
+	}
+
+	if (chirp_found) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_BIN5_FFT |
+			WLAN_DEBUG_DFS_PHYERR_SUM,
+			"%s: CHIRPING_YES\n",
+			__func__);
+	} else {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_BIN5_FFT |
+			WLAN_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 wlan_dfs *dfs,
+		void *buf,
+		uint16_t datalen,
+		int is_ctl,
+		int is_ext,
+		int *slope,
+		int *is_dc)
+{
+	if (dfs->dfs_caps.wlan_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 wlan_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, WLAN_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, WLAN_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;
+}
+
+int dfs_get_random_bin5_dur(struct wlan_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;
+}

+ 353 - 0
umac/dfs/core/src/filtering/dfs_init.c

@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: This file contains initialization functions and functions that reset
+ * internal data structures.
+ */
+
+#include "../dfs.h"
+#include "wlan_dfs_lmac_api.h"
+
+void dfs_reset_alldelaylines(struct wlan_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) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+				"%s: sc_dfs is NULL\n", __func__);
+		return;
+	}
+	pl = dfs->pulses;
+
+	if (pl == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s: pl==NULL, dfs=%p\n",
+				__func__, dfs);
+		return;
+	}
+
+	if (dfs->dfs_b5radars == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s: pl==NULL, b5radars=%p\n",
+				__func__, 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];
+			for (j = 0; j < ft->ft_numfilters; j++) {
+				rf = &(ft->ft_filters[j]);
+				dl = &(rf->rf_dl);
+				if (dl != NULL) {
+					qdf_mem_zero(dl,
+						sizeof(struct dfs_delayline));
+					dl->dl_lastelem =
+						(0xFFFFFFFF) & DFS_MAX_DL_MASK;
+				}
+			}
+		}
+	}
+
+	for (i = 0; i < dfs->dfs_rinfo.rn_numbin5radars; i++) {
+		qdf_mem_zero(&(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;
+	}
+}
+
+void dfs_reset_delayline(struct dfs_delayline *dl)
+{
+	qdf_mem_zero(&(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)
+{
+	struct dfs_filter *df;
+	int i;
+
+	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 wlan_dfs *dfs)
+{
+	struct dfs_event *event;
+
+	if (dfs == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s: sc_dfs is NULL\n",
+				__func__);
+		return;
+	}
+
+	WLAN_DFSQ_LOCK(dfs);
+	WLAN_DFSEVENTQ_LOCK(dfs);
+	while (!STAILQ_EMPTY(&(dfs->dfs_radarq))) {
+		event = STAILQ_FIRST(&(dfs->dfs_radarq));
+		STAILQ_REMOVE_HEAD(&(dfs->dfs_radarq), re_list);
+		qdf_mem_zero(event, sizeof(struct dfs_event));
+		STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), event, re_list);
+	}
+	WLAN_DFSEVENTQ_UNLOCK(dfs);
+	WLAN_DFSQ_UNLOCK(dfs);
+}
+
+int dfs_init_radar_filters(struct wlan_dfs *dfs,
+		struct wlan_dfs_radar_tab_info *radar_info)
+{
+	struct dfs_filtertype *ft = NULL;
+	struct dfs_filter *rf = NULL;
+	struct dfs_pulse *dfs_radars;
+	struct dfs_bin5pulse *b5pulses = NULL;
+	uint32_t T, Tmax;
+	int32_t min_rssithresh = DFS_MAX_RSSI_VALUE;
+	uint32_t max_pulsedur = 0;
+	int numpulses, p, n, i;
+	int numradars = 0, numb5radars = 0;
+
+	if (dfs == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS, "dfs is NULL %s", __func__);
+		return 1;
+	}
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+			"%s: dfsdomain=%d, numradars=%d, numb5radars=%d\n",
+			__func__, radar_info->dfsdomain,
+			radar_info->numradars, radar_info->numb5radars);
+
+	/* Clear up the dfs domain flag first. */
+	dfs->wlan_dfs_isdfsregdomain = 0;
+
+	/*
+	 * 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) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s: Unknown dfs domain %d\n",
+				__func__, dfs->dfsdomain);
+		/* Disable radar detection since we don't have a radar domain.*/
+		dfs->dfs_proc_phyerr &= ~DFS_RADAR_EN;
+		dfs->dfs_proc_phyerr &= ~DFS_SECOND_SEGMENT_RADAR_EN;
+		return 0;
+	}
+
+	dfs->dfsdomain = radar_info->dfsdomain;
+	dfs_radars = radar_info->dfs_radars;
+	numradars = radar_info->numradars;
+	b5pulses = radar_info->b5pulses;
+	numb5radars = radar_info->numb5radars;
+
+	dfs->dfs_defaultparams = radar_info->dfs_defaultparams;
+
+	dfs->wlan_dfs_isdfsregdomain = 1;
+	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, WLAN_DEBUG_DFS,
+						"%s: Too many filter types\n",
+						__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, WLAN_DEBUG_DFS,
+							"%s: Too many overlapping radar filters\n",
+							__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;
+
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+				"minprf = %d maxprf = %d pulsevar = %d thresh=%d\n",
+				dfs_radars[p].rp_pulsefreq,
+				dfs_radars[p].rp_max_pulsefreq,
+				dfs_radars[p].rp_pulsevar,
+				rf->rf_threshold);
+
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+				"minpri = %d maxpri = %d filterlen = %d filterID = %d\n",
+				rf->rf_minpri, rf->rf_maxpri,
+				rf->rf_filterlen, rf->rf_pulseid);
+	}
+
+	dfs_print_filters(dfs);
+
+	dfs->dfs_rinfo.rn_numbin5radars  = numb5radars;
+	if (dfs->dfs_b5radars != NULL)
+		qdf_mem_free(dfs->dfs_b5radars);
+
+	dfs->dfs_b5radars = (struct dfs_bin5radars *)qdf_mem_malloc(
+			numb5radars * sizeof(struct dfs_bin5radars));
+	if (dfs->dfs_b5radars == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+				"%s: cannot allocate memory for bin5 radars\n",
+				__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;
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+			"DFS min filter rssiThresh = %d\n",
+			min_rssithresh);
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+			"DFS max pulse dur = %d ticks\n",
+			dfs->dfs_rinfo.rn_maxpulsedur);
+
+	return 0;
+
+bad4:
+	return 1;
+}
+
+void dfs_clear_stats(struct wlan_dfs *dfs)
+{
+	if (dfs == NULL)
+		return;
+
+	qdf_mem_zero(&dfs->wlan_dfs_stats, sizeof(struct dfs_stats));
+	dfs->wlan_dfs_stats.last_reset_tstamp =
+	    lmac_get_tsf64(dfs->dfs_pdev_obj);
+}

+ 179 - 0
umac/dfs/core/src/filtering/dfs_misc.c

@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: This file really does contain miscellaneous functions that didn't fit
+ * in anywhere else.
+ */
+
+#include "../dfs.h"
+#include "wlan_dfs_lmac_api.h"
+#include "wlan_dfs_mlme_api.h"
+#include "../dfs_internal.h"
+
+/**
+ * dfs_adjust_pri_per_chan_busy() - Calculates adjust_pri.
+ * @ext_chan_busy: Extension channel PRI.
+ * @pri_margin: Primary margin.
+ *
+ * Calculates the adjust_pri using ext_chan_busy, DFS_EXT_CHAN_LOADING_THRESH
+ * and pri_margin.
+ *
+ * Return: adjust_pri.
+ */
+static int dfs_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;
+}
+
+/**
+ * dfs_adjust_thresh_per_chan_busy() - Calculates adjust_thresh.
+ * @ext_chan_busy: Extension channel PRI.
+ * @thresh: Threshold value.
+ *
+ * Calculates the adjust_thresh using ext_chan_busy, DFS_EXT_CHAN_LOADING_THRESH
+ * and thresh.
+ *
+ * Return: adjust_thresh.
+ */
+static int dfs_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;
+}
+
+int dfs_get_pri_margin(struct wlan_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 (IEEE80211_IS_CHAN_11N_HT40(dfs->dfs_curchan)) {
+		ext_chan_busy = lmac_get_ext_busy(dfs->dfs_pdev_obj);
+		if (ext_chan_busy >= 0) {
+			dfs->dfs_rinfo.ext_chan_busy_ts =
+				lmac_get_tsf64(dfs->dfs_pdev_obj);
+			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, WLAN_DEBUG_DFS2,
+						"PRI Use cached copy of ext_chan_busy extchanbusy=%d\n",
+						ext_chan_busy);
+				}
+			}
+		}
+		adjust_pri = dfs_adjust_pri_per_chan_busy(ext_chan_busy,
+			pri_margin);
+		pri_margin -= adjust_pri;
+	}
+
+	return pri_margin;
+}
+
+int dfs_get_filter_threshold(struct wlan_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 (IEEE80211_IS_CHAN_11N_HT40(dfs->dfs_curchan)) {
+		ext_chan_busy = lmac_get_ext_busy(dfs->dfs_pdev_obj);
+		if (ext_chan_busy >= 0) {
+			dfs->dfs_rinfo.ext_chan_busy_ts =
+				lmac_get_tsf64(dfs->dfs_pdev_obj);
+			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, WLAN_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,
+						(uint64_t)
+						dfs->dfs_rinfo.rn_lastfull_ts,
+						(uint64_t)
+						dfs->dfs_rinfo.ext_chan_busy_ts
+						);
+				}
+			}
+		}
+
+		adjust_thresh =
+			dfs_adjust_thresh_per_chan_busy(ext_chan_busy, thresh);
+		DFS_DPRINTK(dfs, WLAN_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;
+}

+ 753 - 0
umac/dfs/core/src/filtering/dfs_phyerr_tlv.c

@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 2012, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * DOC: This file contains TLV frame processing functions.
+ */
+
+#include "../dfs.h"
+#include "../dfs_channel.h"
+#include "../dfs_phyerr_tlv.h"
+#include "wlan_dfs_mlme_api.h"
+#include "../dfs_internal.h"
+
+#define AGC_MB_GAIN_THRESH1    68
+#define AGC_OTHER_GAIN_THRESH1 40
+#define AGC_MB_GAIN_THRESH2    80
+#define AGC_OTHER_GAIN_THRESH2 60
+#define AGC_GAIN_RSSI_THRESH   25
+
+/*
+ * Until "fastclk" is stored in the DFS configuration.
+ */
+#define PERE_IS_OVERSAMPLING(_dfs) \
+	(_dfs->dfs_caps.wlan_chip_is_over_sampled ? 1 : 0)
+
+/**
+ * dfs_sign_extend_32() - Calculates extended 32bit value.
+ * @v: Value.
+ * @nb: Offset.
+ *
+ * Return: Returns Extend vale.
+ */
+static int32_t dfs_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;
+}
+
+/**
+ * dfs_calc_freq_offset() - Calculate the frequency offset.
+ * @sindex: signed bin index.
+ * @is_oversampling: oversampling mode
+ *
+ * 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 dfs_calc_freq_offset(int sindex, int is_oversampling)
+{
+	if (is_oversampling)
+		return sindex * (44000 / 128);
+	else
+		return sindex * (40000 / 128);
+}
+
+/**
+ * dfs_radar_summary_print() - Prints the Radar summary.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rsu: Pointer rx_radar_status structure.
+ */
+static void dfs_radar_summary_print(struct wlan_dfs *dfs,
+		struct rx_radar_status *rsu)
+{
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    pulsedur=%d\n", rsu->pulse_duration);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    rssi=%d\n", rsu->rssi);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    ischirp=%d\n", rsu->is_chirp);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    sidx=%d\n", rsu->sidx);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    raw tsf=%d\n", rsu->raw_tsf);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    tsf_offset=%d\n", rsu->tsf_offset);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    cooked tsf=%d\n", rsu->raw_tsf - rsu->tsf_offset);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    frequency offset=%d.%d MHz (oversampling=%d)\n",
+		(int)(rsu->freq_offset / 1000),
+		(int)abs(rsu->freq_offset % 1000),
+		PERE_IS_OVERSAMPLING(dfs));
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    agc_total_gain=%d\n", rsu->agc_total_gain);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"    agc_mb_gain=%d\n", rsu->agc_mb_gain);
+}
+
+/**
+ * dfs_radar_summary_parse() - Parse the radar summary frame.
+ * @dfs: pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer.
+ * @len: Phyerr buflen.
+ * @rsu: Pointer to rx_radar_status structure.
+ *
+ * The frame contents _minus_ the TLV are passed in.
+ */
+static void dfs_radar_summary_parse(struct wlan_dfs *dfs,
+		const char *buf,
+		size_t len,
+		struct rx_radar_status *rsu)
+{
+	uint32_t rs[3];
+
+	/* Drop out if we have < 2 DWORDs available. */
+	if (len < sizeof(rs)) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR |
+			WLAN_DEBUG_DFS_PHYERR_SUM,
+			"%s: len (%zu) < expected (%zu)!\n",
+			__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.
+	 */
+	qdf_mem_copy(rs, buf, sizeof(rs));
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: two 32 bit values are: %08x %08x\n",
+		__func__, rs[0], rs[1]);
+
+	/* 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 = dfs_sign_extend_32(MS(rs[RADAR_REPORT_PULSE_REG_1],
+				RADAR_REPORT_PULSE_SIDX),
+			10);
+	rsu->freq_offset =
+		dfs_calc_freq_offset(rsu->sidx, PERE_IS_OVERSAMPLING(dfs));
+
+	/* These are only relevant if the pulse is a chirp. */
+	rsu->delta_peak = dfs_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);
+	rsu->agc_total_gain =
+		MS(rs[RADAR_REPORT_PULSE_REG_1], RADAR_REPORT_AGC_TOTAL_GAIN);
+	rsu->agc_mb_gain = MS(rs[RADAR_REPORT_PULSE_REG_2],
+		RADAR_REPORT_PULSE_AGC_MB_GAIN);
+}
+
+/**
+ * dfs_radar_fft_search_report_parse () - Parse FFT report.
+ * @dfs: pointer to wlan_dfs structure.
+ * @buf: Phyerr buffer.
+ * @len: Phyerr buflen.
+ * @rsu: Pointer to rx_radar_status structure.
+ */
+static void dfs_radar_fft_search_report_parse(struct wlan_dfs *dfs,
+		const char *buf,
+		size_t len,
+		struct rx_search_fft_report *rsfr)
+{
+	uint32_t rs[3];
+
+	/* Drop out if we have < 2 DWORDs available. */
+	if (len < sizeof(rs)) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR |
+			WLAN_DEBUG_DFS_PHYERR_SUM,
+			"%s: len (%zu) < expected (%zu)!\n",
+			__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.
+	 */
+	qdf_mem_copy(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 = dfs_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, WLAN_DEBUG_DFS_PHYERR,
+		"%s: two 32 bit values are: %08x %08x\n",
+		__func__, rs[0], rs[1]);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: rsfr->total_gain_db = %d\n",
+		__func__, rsfr->total_gain_db);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: rsfr->base_pwr_db = %d\n",
+		__func__, rsfr->base_pwr_db);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: rsfr->fft_chn_idx = %d\n",
+		__func__, rsfr->fft_chn_idx);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: rsfr->peak_sidx = %d\n",
+		__func__, rsfr->peak_sidx);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: rsfr->relpwr_db = %d\n",
+		__func__, rsfr->relpwr_db);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: rsfr->avgpwr_db = %d\n",
+		__func__, rsfr->avgpwr_db);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: rsfr->peak_mag = %d\n",
+		__func__, rsfr->peak_mag);
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+		"%s: rsfr->num_str_bins_ib = %d\n",
+		__func__, rsfr->num_str_bins_ib);
+
+	if (dfs->dfs_caps.wlan_chip_is_ht160) {
+		rsfr->seg_id =
+		    MS(rs[SEARCH_FFT_REPORT_REG_3], SEARCH_FFT_REPORT_SEG_ID);
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+			"%s: rsfr->seg_id = %d\n", __func__, rsfr->seg_id);
+	}
+}
+
+/**
+ * dfs_tlv_parse_frame () - Parse a Peregrine BB TLV frame.
+ * @dfs: pointer to wlan_dfs structure.
+ * @rs: pointer to rx_radar_status structure.
+ * @rsfr: Pointer to rx_search_fft_report structure.
+ * @buf: Phyerr buffer.
+ * @len: Phyerr buflen.
+ * @rssi: RSSI.
+ * @first_short_fft_peak_mag: first short FFT peak_mag.
+ *
+ * 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 dfs_tlv_parse_frame(struct wlan_dfs *dfs,
+		struct rx_radar_status *rs,
+		struct rx_search_fft_report *rsfr,
+		const char *buf,
+		size_t len,
+		uint8_t rssi,
+		int *first_short_fft_peak_mag)
+{
+	int i = 0;
+	uint32_t tlv_hdr[1];
+	bool first_tlv = true;
+	bool false_detect = false;
+	bool is_ht160 = false;
+	bool is_false_detect = false;
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+			"%s: total length = %zu bytes\n",
+			__func__, len);
+	while ((i < len) && (false_detect == false)) {
+		/* Ensure we at least have four bytes. */
+		if ((len - i) < sizeof(tlv_hdr)) {
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR |
+				WLAN_DEBUG_DFS_PHYERR_SUM,
+				"%s: ran out of bytes, len=%zu, i=%d\n",
+				__func__, len, i);
+			DFS_PRINTK("%s: ran out of bytes, len=%zu, i=%d\n",
+				__func__, len, i);
+			return 0;
+		}
+
+		/*
+		 * Copy the offset into the header, so the DWORD style access
+		 * macros can be used.
+		 */
+		qdf_mem_copy(&tlv_hdr, buf + i, sizeof(tlv_hdr));
+
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+			"%s: HDR: TLV SIG=0x%x, TAG=0x%x, LEN=%d bytes\n",
+			__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, WLAN_DEBUG_DFS_PHYERR,
+				"%s: TLV oversize: TLV LEN=%d, available=%zu, i=%d\n",
+				__func__, MS(tlv_hdr[TLV_REG], TLV_LEN),
+				len, i);
+			break;
+		}
+
+		/* Skip the TLV header - one DWORD. */
+		i += sizeof(tlv_hdr);
+
+		/* Handle the payload. */
+		switch (MS(tlv_hdr[TLV_REG], TLV_SIG)) {
+		case TAG_ID_RADAR_PULSE_SUMMARY: /* Radar pulse summary */
+			dfs_radar_summary_parse(dfs, buf + i,
+					MS(tlv_hdr[TLV_REG], TLV_LEN), rs);
+
+			/*
+			 * Check for possible false detection on
+			 * beeliner this may also work for Cascade but
+			 * parameters (e.g. AGC_MB_GAIN_THRESH1) may
+			 * be different for Cascade.
+			 */
+			is_ht160 = dfs->dfs_caps.wlan_chip_is_ht160;
+			is_false_detect =
+			    dfs->dfs_caps.wlan_chip_is_false_detect;
+
+			if ((dfs->dfs_caps.wlan_chip_is_over_sampled == 0) &&
+					(is_ht160 == 0 && is_false_detect)
+			   ) {
+				if ((rs->agc_mb_gain > AGC_MB_GAIN_THRESH1) &&
+					((rs->agc_total_gain - rs->agc_mb_gain)
+					 < AGC_OTHER_GAIN_THRESH1)) {
+					false_detect = true;
+					DFS_DPRINTK(dfs,
+						WLAN_DEBUG_DFS_PHYERR,
+						"%s: setting false_detect to TRUE because of mb/total_gain, agc_mb_gain=%d, agc_total_gain=%d, rssi=%d\n",
+						__func__,
+						rs->agc_mb_gain,
+						rs->agc_total_gain,
+						rssi);
+				}
+
+				if ((rs->agc_mb_gain > AGC_MB_GAIN_THRESH2) &&
+					((rs->agc_total_gain - rs->agc_mb_gain)
+					 > AGC_OTHER_GAIN_THRESH2) &&
+					(rssi > AGC_GAIN_RSSI_THRESH)
+				   ) {
+					false_detect = true;
+					DFS_DPRINTK(dfs,
+						WLAN_DEBUG_DFS_PHYERR,
+						"%s: setting false_detect to TRUE because of mb/total_gain/rssi, agc_mb_gain=%d, agc_total_gain=%d, rssi=%d\n",
+						__func__,
+						rs->agc_mb_gain,
+						rs->agc_total_gain,
+						rssi);
+				}
+			}
+			break;
+		case TAG_ID_SEARCH_FFT_REPORT:
+			dfs_radar_fft_search_report_parse(dfs, buf + i,
+					MS(tlv_hdr[TLV_REG], TLV_LEN), rsfr);
+
+			/* we are interested in the first short FFT report's
+			 * peak_mag for this value to be reliable, we must
+			 * ensure that
+			 * BB_srch_fft_ctrl_4.radar_fft_short_rpt_scl is set to
+			 * 0.
+			 */
+			if (first_tlv)
+				*first_short_fft_peak_mag = rsfr->peak_mag;
+
+			/*
+			 * Check for possible false detection on Peregrine.
+			 * 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->wlan_dfs_false_rssI_thres
+			 * (default 50) and peak_mag is less than
+			 * 2 * dfs->wlan_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 (PERE_IS_OVERSAMPLING(dfs) && (first_tlv == true) &&
+				(rssi == dfs->wlan_dfs_false_rssi_thres) &&
+				(rsfr->peak_mag < (2 * dfs->wlan_dfs_peak_mag))
+				) {
+				false_detect = true;
+				DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+					"%s: setting false_detect to TRUE because of false_rssi_thres\n",
+					__func__);
+			}
+			break;
+		default:
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+				"%s: unknown entry, SIG=0x%02x\n",
+				__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, WLAN_DEBUG_DFS_PHYERR, "%s: done\n\n", __func__);
+
+	return false_detect ? 0 : 1;
+}
+
+/**
+ * dfs_tlv_calc_freq_info() - Calculate the channel centre in MHz.
+ * @dfs: pointer to wlan_dfs structure.
+ * @rs: pointer to rx_radar_status structure.
+ *
+ * Return: Returns the channel center.
+ */
+static int dfs_tlv_calc_freq_info(struct wlan_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->dfs_curchan == NULL) {
+		DFS_PRINTK("%s: dfs_curchan is null\n", __func__);
+		return 0;
+		/*
+		 * For now, the only 11ac channel with freq1/freq2 setup is
+		 * VHT80. Should have a flag macro to check this!
+		 */
+	} else if (IEEE80211_IS_CHAN_11AC_VHT80(dfs->dfs_curchan)) {
+		/*
+		 * 11AC, so cfreq1/cfreq2 are setup.
+		 * If it's 80+80 this won't work - need to use seg
+		 * appropriately!
+		 */
+		chan_centre = dfs_mlme_ieee2mhz(dfs->dfs_pdev_obj,
+				dfs->dfs_curchan->ic_vhtop_ch_freq_seg1,
+				dfs->dfs_curchan->ic_flags);
+	} else {
+		/*
+		 * HT20/HT40.
+		 * This is hard-coded - it should be 5 or 10 for half/quarter
+		 * appropriately.
+		 */
+		chan_width = 20;
+
+		/* Grab default channel centre. */
+		chan_centre = dfs_ieee80211_chan2freq(dfs->dfs_curchan);
+
+		/* Calculate offset based on HT40U/HT40D and VHT40U/VHT40D. */
+		if (IEEE80211_IS_CHAN_11N_HT40PLUS(dfs->dfs_curchan) ||
+			dfs->dfs_curchan->ic_flags & IEEE80211_CHAN_VHT40PLUS)
+			chan_offset = chan_width;
+		else if (IEEE80211_IS_CHAN_11N_HT40MINUS(dfs->dfs_curchan) ||
+			dfs->dfs_curchan->ic_flags & IEEE80211_CHAN_VHT40MINUS)
+			chan_offset = -chan_width;
+		else
+			chan_offset = 0;
+
+		/* Calculate new _real_ channel centre. */
+		chan_centre += (chan_offset / 2);
+	}
+
+	/* Return ev_chan_centre in MHz. */
+	return chan_centre;
+}
+
+/**
+ * dfs_tlv_calc_event_freq_pulse() - Calculate the centre frequency and
+ *                                   low/high range for a radar pulse event.
+ * @dfs: pointer to wlan_dfs structure.
+ * @rs: pointer to rx_radar_status structure.
+ * @freq_centre: center frequency
+ * @freq_lo: lower bounds of frequency.
+ * @freq_hi: upper bounds of frequency.
+ *
+ * XXX TODO: Handle half/quarter rates correctly!
+ * XXX TODO: handle VHT160 correctly!
+ * XXX TODO: handle VHT80+80 correctly!
+ *
+ * Return: Returns 1.
+ */
+static int dfs_tlv_calc_event_freq_pulse(struct wlan_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 = dfs_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;
+}
+
+/**
+ * dfs_tlv_calc_event_freq_chirp() - Calculate the event freq.
+ * @dfs: pointer to wlan_dfs structure.
+ * @rs: pointer to rx_radar_status structure.
+ * @freq_centre: center frequency
+ * @freq_lo: lower bounds of frequency.
+ * @freq_hi: upper bounds of frequency.
+ *
+ * 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.
+ *
+ * Return: Returns 1
+ */
+static int dfs_tlv_calc_event_freq_chirp(struct wlan_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 = (OVER_SAMPLING_FREQ * HUNDRED) / NUM_BINS;
+	else
+		bin_resolution = (SAMPLING_FREQ * HUNDRED) / NUM_BINS;
+
+	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, WLAN_DEBUG_DFS_PHYERR | WLAN_DEBUG_DFS_PHYERR_SUM,
+		"%s: delta_peak=%d, pulse_duration=%d, bin_resolution=%d.%dKHz, radar_fft_long_period=%d, total_bw=%d.%ldKHz\n",
+		__func__,
+		delta_peak, pulse_duration, bin_resolution / THOUSAND,
+		bin_resolution % THOUSAND, radar_fft_long_period,
+		total_bw / HUNDRED,
+		(long)abs(total_bw % HUNDRED));
+
+	total_bw /= HUNDRED; /* back to KHz */
+	/* Grab the channel centre frequency in MHz. */
+	chan_centre = dfs_tlv_calc_freq_info(dfs, rs);
+
+	/* Early abort! */
+	if (chan_centre == 0) {
+		(*freq_centre) = 0;
+		return 0;
+	}
+
+	/* Convert to KHz. */
+	chan_centre *= THOUSAND;
+
+	/*
+	 * 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;
+}
+
+/**
+ * dfs_tlv_calc_event_freq() - Calculate the centre and band edge frequencies
+ *                             of the given radar event.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @rs: Pointer to rx_radar_status structure.
+ * @freq_centre: Center frequency
+ * @freq_lo: Lower bounds of frequency.
+ * @freq_hi: Upper bounds of frequency.
+ */
+static int dfs_tlv_calc_event_freq(struct wlan_dfs *dfs,
+		struct rx_radar_status *rs,
+		uint32_t *freq_centre,
+		uint32_t *freq_lo,
+		uint32_t *freq_hi)
+{
+	if (rs->is_chirp)
+		return dfs_tlv_calc_event_freq_chirp(dfs, rs, freq_centre,
+				freq_lo, freq_hi);
+	else
+		return dfs_tlv_calc_event_freq_pulse(dfs, rs, freq_centre,
+				freq_lo, freq_hi);
+}
+
+int dfs_process_phyerr_bb_tlv(struct wlan_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)
+{
+	struct rx_radar_status rs;
+	struct rx_search_fft_report rsfr;
+	int first_short_fft_peak_mag = 0;
+
+	qdf_mem_zero(&rs, sizeof(rs));
+	qdf_mem_zero(&rsfr, sizeof(rsfr));
+
+	/*
+	 * 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 (!dfs_tlv_parse_frame(dfs, &rs, &rsfr, buf, datalen, rssi,
+				&first_short_fft_peak_mag))
+		return 0;
+
+	/* For debugging, print what we have parsed. */
+	dfs_radar_summary_print(dfs, &rs);
+
+	/* Populate dfs_phy_err from rs. */
+	qdf_mem_set(e, 0, sizeof(*e));
+	e->rssi = rs.rssi;
+	e->dur = rs.pulse_duration;
+	e->is_pri = 1; /* 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)dfs_tlv_calc_event_freq(dfs, &rs, &e->freq, &e->freq_lo,
+			&e->freq_hi);
+
+	e->seg_id = rsfr.seg_id;
+	e->sidx = rs.sidx;
+	e->freq_offset_khz = rs.freq_offset;
+	e->peak_mag = first_short_fft_peak_mag;
+	e->total_gain = rs.agc_total_gain;
+	e->mb_gain = rs.agc_mb_gain;
+	e->relpwr_db = rsfr.relpwr_db;
+
+	DFS_DPRINTK(dfs, WLAN_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\n",
+		__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);
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_FALSE_DET,
+		"ts=%u, dur=%d, rssi=%d, freq_offset=%d.%dMHz, is_chirp=%d, seg_id=%d, peak_mag=%d, total_gain=%d, mb_gain=%d, relpwr_db=%d\n",
+		e->rs_tstamp,
+		rs.pulse_duration,
+		rs.rssi,
+		(int)e->freq_offset_khz / 1000,
+		(int)abs(e->freq_offset_khz) % 1000,
+		(int)rs.is_chirp,
+		rsfr.seg_id,
+		rsfr.peak_mag,
+		rs.agc_total_gain,
+		rs.agc_mb_gain,
+		rsfr.relpwr_db);
+
+	return 1;
+}

+ 818 - 0
umac/dfs/core/src/filtering/dfs_process_phyerr.c

@@ -0,0 +1,818 @@
+/*
+ * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: For each radar pulse that the HW detects, a single radar PHY error is
+ * reported to the driver. This PHY error contains information like the RSSI,
+ * the pulse duration, the pulse location (primary/extension/DC) and possibly
+ * FFT data.
+ */
+
+#include "../dfs.h"
+#include "../dfs_channel.h"
+#include "wlan_dfs_mlme_api.h"
+#include "../dfs_internal.h"
+
+/**
+ * dfs_get_event_freqwidth() - Get frequency width.
+ * @dfs: Pointer to wlan_dfs structure.
+ *
+ * Return: 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 wlan_dfs *dfs)
+{
+	/* Handle edge cases during startup/transition, shouldn't happen! */
+	if (dfs == NULL)
+		return 0;
+
+	if (dfs->dfs_curchan == NULL)
+		return 0;
+
+	/*
+	 * For now, assume 20MHz wide - but this is incorrect when operating in
+	 * half/quarter mode!
+	 */
+	return 20;
+}
+
+/**
+ * dfs_get_event_freqcentre() - Get event frequency centre.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @is_pri: detected on primary channel.
+ * @is_ext: detected on extension channel.
+ * @is_dc: detected at DC.
+ *
+ * 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 wlan_dfs *dfs,
+		int is_pri,
+		int is_ext,
+		int is_dc)
+{
+	int chan_offset = 0, chan_width;
+
+	/* Handle edge cases during startup/transition, shouldn't happen! */
+	if (dfs == NULL)
+		return 0;
+	if (dfs->dfs_curchan == NULL)
+		return 0;
+
+	/*
+	 * 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(dfs->dfs_curchan))
+		chan_offset = chan_width;
+	else if (IEEE80211_IS_CHAN_11N_HT40MINUS(dfs->dfs_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 dfs_ieee80211_chan2freq(
+				dfs->dfs_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 dfs_ieee80211_chan2freq(dfs->dfs_curchan);
+
+	if (is_ext)
+		return dfs_ieee80211_chan2freq(dfs->dfs_curchan) + chan_width;
+
+	return dfs_ieee80211_chan2freq(dfs->dfs_curchan);
+}
+
+int dfs_process_phyerr_owl(struct wlan_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;
+
+	dfs->wlan_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->wlan_dfs_stats.datalen_discards++;
+	return 0;
+
+	/* Fill out dfs_phy_err with the information we have at hand. */
+	qdf_mem_set(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, WLAN_DEBUG_DFS_PHYERR_SUM,
+		"%s: rssi=%u dur=%u, freq=%d MHz, freq_lo=%d MHz, freq_hi=%d MHz\n",
+		__func__,
+		rssi,
+		dur,
+		e->freq/1000,
+		e->freq_lo/1000,
+		e->freq_hi / 1000);
+
+	return 1;
+}
+
+int dfs_process_phyerr_sowl(struct wlan_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->wlan_dfs_stats.datalen_discards++;
+		return 0;
+	}
+
+	/* Ensure that we have at least three bytes of payload. */
+	if (datalen < 3) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+			"%s: short error frame (%d bytes)\n",
+			__func__, datalen);
+		dfs->wlan_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, differentpri 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->wlan_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->wlan_dfs_stats.ext_phy_errors++;
+		}
+		if (pulse_bw_info & PRI_CH_RADAR_FOUND) {
+			dur = pulse_length_pri;
+			pri_found = 1;
+			ext_found = 0;
+			dfs->wlan_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->wlan_dfs_stats.early_ext_phy_errors++;
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+				"EARLY ext channel dur=%u rssi=%u datalen=%d\n",
+				dur, rssi, datalen);
+		}
+		if (!pulse_bw_info) {
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+				"ERROR channel dur=%u rssi=%u pulse_bw_info=0x%x datalen MOD 4 = %d\n",
+				dur, rssi, pulse_bw_info,
+				(datalen & 0x3));
+			/*
+			 * Bogus bandwidth info received in descriptor, so
+			 * ignore this PHY error.
+			 */
+			dfs->wlan_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. */
+	qdf_mem_set(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, WLAN_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);
+#undef EXT_CH_RADAR_FOUND
+#undef PRI_CH_RADAR_FOUND
+#undef EXT_CH_RADAR_EARLY_FOUND
+
+	return 1;
+}
+
+int dfs_process_phyerr_merlin(struct wlan_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, WLAN_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, WLAN_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;
+}
+
+/**
+ * dfs_dump_phyerr_contents() - Dump the phyerr contents.
+ * @d: Phyerr buffer.
+ * @len: Phyerr buf length.
+ */
+
+static void dfs_dump_phyerr_contents(const char *d, int len)
+{
+	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) {
+			DFS_PRINTK("%s: %s\n", __func__, buf);
+			n = 0;
+			buf[0] = '\0';
+		}
+	}
+
+	/* Print the final line if we didn't print it above. */
+	if (n != 0)
+		DFS_PRINTK("%s: %s\n", __func__, buf);
+}
+
+void dfs_process_phyerr(struct wlan_dfs *dfs, void *buf, uint16_t datalen,
+		uint8_t r_rssi, uint8_t r_ext_rssi, uint32_t r_rs_tstamp,
+		uint64_t r_fulltsf)
+{
+	struct dfs_event *event;
+	struct dfs_phy_err e;
+	int empty;
+
+	if (dfs == NULL) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+			"%s: sc_dfs is NULL\n", __func__);
+		return;
+	}
+
+	if (dfs->dfs_ignore_dfs) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+			"%s: ignoring dfs\n", __func__);
+		return;
+	}
+
+	/*
+	 * EV 129487: If radar detection is disabled, do not process PHY error
+	 * data.
+	 */
+
+	if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+			"%s: DFS_RADAR_EN not set in dfs->dfs_proc_phyerr\n",
+			__func__);
+		return;
+	}
+
+	/*
+	 * 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 : this may be done for us from the legacy phy error path in
+	 * wlan_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 & WLAN_DEBUG_DFS_PHYERR_PKT)
+		dfs_dump_phyerr_contents(buf, datalen);
+
+	if (IEEE80211_IS_CHAN_RADAR(dfs->dfs_curchan)) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+			"%s: Radar already found in the channel, do not queue radar data\n",
+			__func__);
+		return;
+	}
+
+	dfs->wlan_dfs_stats.total_phy_errors++;
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2, "%s : %d phyerr %d len %d\n",
+		__func__, __LINE__,
+		dfs->wlan_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;
+
+	qdf_mem_set(&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.wlan_chip_is_bb_tlv) {
+		if (dfs_process_phyerr_bb_tlv(dfs, buf, datalen, r_rssi,
+			    r_ext_rssi, r_rs_tstamp, r_fulltsf, &e) == 0) {
+			dfs->dfs_phyerr_reject_count++;
+			return;
+		}
+
+		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.wlan_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.wlan_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;
+	}
+
+	/*
+	 * 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.wlan_chip_is_bb_tlv) &&
+			dfs->dfs_caps.wlan_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
+			 */
+			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, WLAN_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, WLAN_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, WLAN_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)) {
+				/*
+				 * 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 this!
+	 */
+	WLAN_DFSEVENTQ_LOCK(dfs);
+	empty = STAILQ_EMPTY(&(dfs->dfs_eventq));
+	WLAN_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 ((dfs->dfs_curchan->ic_flags & CHANNEL_108G) == CHANNEL_108G) {
+		if (!(dfs->dfs_proc_phyerr & DFS_AR_EN)) {
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+				"%s: DFS_AR_EN not enabled\n",
+				__func__);
+			return;
+		}
+		WLAN_DFSEVENTQ_LOCK(dfs);
+		event = STAILQ_FIRST(&(dfs->dfs_eventq));
+		if (event == NULL) {
+			WLAN_DFSEVENTQ_UNLOCK(dfs);
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+				"%s: no more events space left\n",
+				__func__);
+			return;
+		}
+		STAILQ_REMOVE_HEAD(&(dfs->dfs_eventq), re_list);
+		WLAN_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;
+
+		/* 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;
+		}
+
+		WLAN_ARQ_LOCK(dfs);
+		STAILQ_INSERT_TAIL(&(dfs->dfs_arq), event, re_list);
+		WLAN_ARQ_UNLOCK(dfs);
+	} else {
+		if ((IEEE80211_IS_CHAN_DFS(dfs->dfs_curchan) ||
+		    ((IEEE80211_IS_CHAN_11AC_VHT160(dfs->dfs_curchan) ||
+		      IEEE80211_IS_CHAN_11AC_VHT80_80(dfs->dfs_curchan)) &&
+		     IEEE80211_IS_CHAN_DFS_CFREQ2(dfs->dfs_curchan))) ||
+			(dfs_is_precac_timer_running(dfs))) {
+			if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) {
+				DFS_DPRINTK(dfs, WLAN_DEBUG_DFS3,
+					"%s: DFS_RADAR_EN not enabled\n",
+					__func__);
+				return;
+			}
+			/*
+			 * Rssi is not accurate for short pulses, so donot
+			 * filter based on that for short duration pulses.
+			 */
+			if (dfs->dfs_caps.wlan_dfs_ext_chan_ok) {
+				if ((e.rssi < dfs->dfs_rinfo.rn_minrssithresh &&
+					    (e.dur > 4)) || e.dur >
+					(dfs->dfs_rinfo.rn_maxpulsedur)) {
+					dfs->wlan_dfs_stats.rssi_discards++;
+					DFS_DPRINTK(dfs, WLAN_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) {
+					dfs->wlan_dfs_stats.rssi_discards++;
+					DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+						"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;
+				}
+			}
+
+			if ((e.seg_id == SEG_ID_SECONDARY) &&
+					!(dfs->dfs_proc_phyerr &
+						DFS_SECOND_SEGMENT_RADAR_EN)){
+				DFS_DPRINTK(dfs, WLAN_DEBUG_DFS3,
+						"%s: Do not process PHY error data from Second segment, DFS_SECOND_SEGMENT_RADAR_EN is not enabled\n",
+						__func__);
+				return;
+			}
+
+			/* Add the event to the list, if there's space. */
+			WLAN_DFSEVENTQ_LOCK(dfs);
+			event = STAILQ_FIRST(&(dfs->dfs_eventq));
+			if (event == NULL) {
+				WLAN_DFSEVENTQ_UNLOCK(dfs);
+				DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+					"%s: no more events space left\n",
+					__func__);
+				return;
+			}
+			STAILQ_REMOVE_HEAD(&(dfs->dfs_eventq), re_list);
+			WLAN_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->re_seg_id = e.seg_id;
+			event->re_sidx = e.sidx;
+			event->re_freq_offset_khz = e.freq_offset_khz;
+			event->re_peak_mag = e.peak_mag;
+			event->re_total_gain = e.total_gain;
+			event->re_mb_gain = e.mb_gain;
+			event->re_relpwr_db = e.relpwr_db;
+			/* 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, WLAN_DEBUG_DFS_PHYERR,
+						"%s - phyerr on ext channel\n",
+						__func__);
+				}
+				event->re_chanindex = dfs->dfs_extchan_radindex;
+				DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_PHYERR,
+					"%s New extension channel event is added to queue\n",
+					__func__);
+			}
+			WLAN_DFSQ_LOCK(dfs);
+			STAILQ_INSERT_TAIL(&(dfs->dfs_radarq), event, re_list);
+			WLAN_DFSQ_UNLOCK(dfs);
+		}
+	}
+
+	/*
+	 * Schedule the radar/AR task as appropriate.
+	 * XXX isn't a lock needed for wlan_radar_tasksched?
+	 */
+	if (!STAILQ_EMPTY(&dfs->dfs_arq)) {
+		/* XXX shouldn't this be a task/timer too? */
+		dfs_process_ar_event(dfs, dfs->dfs_curchan);
+	}
+	if (!STAILQ_EMPTY(&dfs->dfs_radarq) && !dfs->wlan_radar_tasksched) {
+		dfs->wlan_radar_tasksched = 1;
+		OS_SET_TIMER(&dfs->wlan_dfs_task_timer, 0);
+	}
+#undef EXT_CH_RADAR_FOUND
+#undef PRI_CH_RADAR_FOUND
+#undef EXT_CH_RADAR_EARLY_FOUND
+}

+ 722 - 0
umac/dfs/core/src/filtering/dfs_process_radarevent.c

@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: This contains the functionality to process the radar event generated
+ * for a pulse. This will group together pulses and call various detection
+ * functions to figure out whether a valid radar has been detected.
+ */
+
+#include "../dfs.h"
+#include "../dfs_channel.h"
+#include "../dfs_internal.h"
+
+#define FREQ_5500_MHZ  5500
+#define FREQ_5500_MHZ       5500
+
+#define DFS_MAX_FREQ_SPREAD            (1375 * 1)
+#define DFS_LARGE_PRI_MULTIPLIER       4
+#define DFS_W53_DEFAULT_PRI_MULTIPLIER 2
+
+static char debug_dup[33];
+static int debug_dup_cnt;
+
+/**
+ * dfs_process_pulse_dur() - Process pulse duration.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @re_dur: Duration.
+ *
+ * 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.)
+ *
+ * Return: Returns the duration.
+ */
+static inline uint8_t dfs_process_pulse_dur(struct wlan_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.wlan_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));
+}
+
+/*
+ * dfs_print_radar_events() - Prints the Radar events.
+ * @dfs: Pointer to wlan_dfs structure.
+ */
+static void dfs_print_radar_events(struct wlan_dfs *dfs)
+{
+	int i;
+
+	DFS_PRINTK("%s:#Phyerr=%d, #false detect=%d, #queued=%d\n",
+		__func__,
+		dfs->dfs_phyerr_count,
+		dfs->dfs_phyerr_reject_count,
+		dfs->dfs_phyerr_queued_count);
+
+	DFS_PRINTK("%s:dfs_phyerr_freq_min=%d, dfs_phyerr_freq_max=%d\n",
+		__func__,
+		dfs->dfs_phyerr_freq_min,
+		dfs->dfs_phyerr_freq_max);
+
+	DFS_PRINTK(
+		"%s:Total radar events detected=%d, entries in the radar queue follows:\n",
+		__func__,
+		dfs->dfs_event_log_count);
+
+	for (i = 0; (i < DFS_EVENT_LOG_SIZE) && (i < dfs->dfs_event_log_count);
+			i++) {
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+			"ts=%llu diff_ts=%u rssi=%u dur=%u, is_chirp=%d, seg_id=%d, sidx=%d, freq_offset=%d.%dMHz, peak_mag=%d, total_gain=%d, mb_gain=%d, relpwr_db=%d\n",
+			dfs->radar_log[i].ts,
+			dfs->radar_log[i].diff_ts,
+			dfs->radar_log[i].rssi,
+			dfs->radar_log[i].dur,
+			dfs->radar_log[i].is_chirp,
+			dfs->radar_log[i].seg_id,
+			dfs->radar_log[i].sidx,
+			(int)dfs->radar_log[i].freq_offset_khz/1000,
+			(int)abs(dfs->radar_log[i].freq_offset_khz)%1000,
+			dfs->radar_log[i].peak_mag,
+			dfs->radar_log[i].total_gain,
+			dfs->radar_log[i].mb_gain,
+			dfs->radar_log[i].relpwr_db);
+	}
+	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;
+}
+
+void __dfs_process_radarevent(struct wlan_dfs *dfs,
+		struct dfs_filtertype *ft,
+		struct dfs_filter *rf,
+		struct dfs_event *re,
+		uint64_t this_ts,
+		int *found)
+{
+	int p;
+	uint64_t deltaT = 0;
+	int ext_chan_event_flag = 0;
+
+	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, WLAN_DEBUG_DFS2,
+					"filterID %d : Rejecting on individual filter min PRI deltaT=%lld rf->rf_minpri=%u\n",
+					rf->rf_pulseid,
+					(uint64_t)deltaT,
+					rf->rf_minpri);
+				continue;
+			}
+
+			if (rf->rf_ignore_pri_window > 0) {
+				if (deltaT < rf->rf_minpri) {
+					DFS_DPRINTK(dfs,
+						WLAN_DEBUG_DFS2,
+						"filterID %d : Rejecting on individual filter max PRI deltaT=%lld rf->rf_minpri=%u\n",
+						rf->rf_pulseid,
+						(uint64_t)
+						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,
+						WLAN_DEBUG_DFS2,
+						"filterID %d : Rejecting on individual filter max PRI deltaT=%lld rf->rf_minpri=%u\n",
+						rf->rf_pulseid,
+						(uint64_t)
+						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 & WLAN_DEBUG_DFS2)
+				dfs_print_delayline(dfs, &rf->rf_dl);
+
+			rf->rf_dl.dl_last_ts = this_ts;
+		}
+	}
+}
+
+int dfs_process_radarevent(struct wlan_dfs *dfs,
+	struct dfs_ieee80211_channel *chan)
+{
+	struct dfs_event re, *event;
+	struct dfs_state *rs = NULL;
+	struct dfs_filtertype *ft;
+	struct dfs_filter *rf;
+	int found, retval = 0, p, empty;
+	int events_processed = 0;
+	uint32_t tabledepth, index;
+	uint64_t deltafull_ts = 0, this_ts, deltaT;
+	struct dfs_ieee80211_channel *thischan;
+	struct dfs_pulseline *pl;
+	static uint32_t  test_ts;
+	static uint32_t  diff_ts;
+	int i;
+	uint8_t   seg_id = 0;
+
+	pl = dfs->pulses;
+
+	if (!(dfs->dfs_second_segment_bangradar ||
+				dfs_is_precac_timer_running(dfs)))
+		if (!(IEEE80211_IS_CHAN_DFS(chan) ||
+			    ((IEEE80211_IS_CHAN_11AC_VHT160(chan) ||
+			      IEEE80211_IS_CHAN_11AC_VHT80_80(chan)) &&
+			     IEEE80211_IS_CHAN_DFS_CFREQ2(chan)))) {
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+				"%s: radar event on non-DFS chan\n",
+				__func__);
+			dfs_reset_radarq(dfs);
+			dfs_reset_alldelaylines(dfs);
+			dfs->dfs_bangradar = 0;
+			return 0;
+		}
+
+	/*
+	 * 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, WLAN_DEBUG_DFS, "%s: bangradar\n", __func__);
+		retval = 1;
+		goto dfsfound;
+	}
+
+	if (dfs->dfs_second_segment_bangradar) {
+		if (dfs_is_precac_timer_running(dfs) ||
+				IEEE80211_IS_CHAN_11AC_VHT160(chan) ||
+				IEEE80211_IS_CHAN_11AC_VHT80_80(chan)) {
+			dfs->is_radar_found_on_secondary_seg = 1;
+			rs = &dfs->dfs_radar[dfs->dfs_curchan_radindex];
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+					"%s: second segment bangradar on cfreq = %u\n",
+					__func__,
+					dfs->dfs_precac_secondary_freq);
+			retval = 1;
+			seg_id = SEG_ID_SECONDARY;
+		} else {
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+					"%s: Do not process the second segment bangradar\n",
+					__func__);
+		}
+		dfs->dfs_second_segment_bangradar = 0; /* Reset */
+		goto dfsfound;
+	}
+
+	/*
+	 * 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.wlan_chip_is_bb_tlv) &&
+		(chan->ic_freq < FREQ_5500_MHZ)) {
+
+		dfs->dfs_pri_multiplier = DFS_W53_DEFAULT_PRI_MULTIPLIER;
+		/*
+		 * Do not process W53 pulses unless we have a minimum number
+		 * of them.
+		 */
+		if (dfs->dfs_phyerr_w53_counter >= 5) {
+			/*
+			 * For chips that support frequency information, we
+			 * can relax PRI restriction if the frequency spread
+			 * is narrow.
+			 */
+			if ((dfs->dfs_phyerr_freq_max -
+				    dfs->dfs_phyerr_freq_min) <
+				DFS_MAX_FREQ_SPREAD) {
+				dfs->dfs_pri_multiplier =
+				    DFS_LARGE_PRI_MULTIPLIER;
+			}
+
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+				"%s: w53_counter=%d, freq_max=%d, freq_min=%d, pri_multiplier=%d\n",
+				__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, WLAN_DEBUG_DFS1,
+			"%s: pri_multiplier=%d\n",
+			__func__, dfs->dfs_pri_multiplier);
+
+	WLAN_DFSQ_LOCK(dfs);
+	empty = STAILQ_EMPTY(&(dfs->dfs_radarq));
+	WLAN_DFSQ_UNLOCK(dfs);
+
+	while ((!empty) && (!retval) && (events_processed < MAX_EVENTS)) {
+		WLAN_DFSQ_LOCK(dfs);
+		event = STAILQ_FIRST(&(dfs->dfs_radarq));
+		if (event != NULL)
+			STAILQ_REMOVE_HEAD(&(dfs->dfs_radarq), re_list);
+		WLAN_DFSQ_UNLOCK(dfs);
+
+		if (event == NULL) {
+			empty = 1;
+			break;
+		}
+		events_processed++;
+		re = *event;
+
+		qdf_mem_zero(event, sizeof(struct dfs_event));
+		WLAN_DFSEVENTQ_LOCK(dfs);
+		STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), event, re_list);
+		WLAN_DFSEVENTQ_UNLOCK(dfs);
+
+		seg_id = re.re_seg_id;
+		found = 0;
+		if (re.re_chanindex < DFS_NUM_RADAR_STATES)
+			rs = &dfs->dfs_radar[re.re_chanindex];
+		else {
+			WLAN_DFSQ_LOCK(dfs);
+			empty = STAILQ_EMPTY(&(dfs->dfs_radarq));
+			WLAN_DFSQ_UNLOCK(dfs);
+			continue;
+		}
+		if (rs->rs_chan.ic_flagext & CHANNEL_INTERFERENCE) {
+			WLAN_DFSQ_LOCK(dfs);
+			empty = STAILQ_EMPTY(&(dfs->dfs_radarq));
+			WLAN_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, WLAN_DEBUG_DFS1,
+					"\n %s deltaT is 0\n", __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.wlan_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, WLAN_DEBUG_DFS1,
+			"ts%u %u %u diff %u pl->pl_lastelem.p_time=%llu\n",
+			(uint32_t)this_ts, re.re_dur,
+			re.re_rssi, diff_ts,
+			(uint64_t)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->radar_log[i].seg_id = re.re_seg_id;
+			dfs->radar_log[i].sidx = re.re_sidx;
+			dfs->radar_log[i].freq_offset_khz =
+				re.re_freq_offset_khz;
+			dfs->radar_log[i].peak_mag = re.re_peak_mag;
+			dfs->radar_log[i].total_gain = re.re_total_gain;
+			dfs->radar_log[i].mb_gain = re.re_mb_gain;
+			dfs->radar_log[i].relpwr_db = re.re_relpwr_db;
+			dfs->radar_log[i].is_chirp = DFS_EVENT_NOTCHIRP(&re) ?
+				0 : 1;
+			dfs->dfs_event_log_count++;
+		}
+
+		/* 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;
+
+		/* 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,
+						WLAN_DEBUG_DFS_BIN5_PULSE,
+						"%s not a BIN5 pulse (dur=%d)\n",
+						__func__, re.re_dur);
+			}
+		}
+
+		if (found) {
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
+				"%s: Found bin5 radar\n", __func__);
+			retval |= found;
+			goto dfsfound;
+		}
+
+		tabledepth = 0;
+		rf = NULL;
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+			"  *** chan freq (%d): ts %llu dur %u rssi %u\n",
+			rs->rs_chan.ic_freq,
+			(uint64_t)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, WLAN_DEBUG_DFS2,
+				"  ** RD (%d): ts %x dur %u rssi %u\n",
+				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, WLAN_DEBUG_DFS2,
+					"%s : Rejecting on rssi rssi=%u thresh=%u\n",
+					__func__,
+					re.re_rssi,
+					ft->ft_rssithresh);
+				tabledepth++;
+				WLAN_DFSQ_LOCK(dfs);
+				empty = STAILQ_EMPTY(&(dfs->dfs_radarq));
+				WLAN_DFSQ_UNLOCK(dfs);
+				continue;
+			}
+			deltaT = this_ts - ft->ft_last_ts;
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+				"deltaT = %lld (ts: 0x%llx) (last ts: 0x%llx)\n",
+				(uint64_t)deltaT,
+				(uint64_t)this_ts,
+				(uint64_t)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, WLAN_DEBUG_DFS2,
+					"%s: Rejecting on pri pri=%lld minpri=%u\n",
+					__func__,
+					(uint64_t)deltaT,
+					ft->ft_minpri);
+				tabledepth++;
+				continue;
+			}
+
+			__dfs_process_radarevent(dfs, ft, rf, &re, this_ts,
+					&found);
+			ft->ft_last_ts = this_ts;
+			retval |= found;
+			if (found) {
+				DFS_PRINTK(
+					"Found on channel minDur = %d, filterId = %d\n",
+					ft->ft_mindur,
+					rf != NULL ?  rf->rf_pulseid : -1);
+			}
+			tabledepth++;
+		}
+		WLAN_DFSQ_LOCK(dfs);
+		empty = STAILQ_EMPTY(&(dfs->dfs_radarq));
+		WLAN_DFSQ_UNLOCK(dfs);
+	}
+dfsfound:
+	if (retval) {
+
+		/*
+		 * TODO: Instead of discarding the radar, create a workqueue
+		 * if the channel change is happenning through userspace and
+		 * process the radar event once the channel change is completed.
+		 */
+
+		/* Collect stats */
+		dfs->wlan_dfs_stats.num_radar_detects++;
+		thischan = &rs->rs_chan;
+		if ((seg_id == SEG_ID_SECONDARY) &&
+				(dfs_is_precac_timer_running(dfs))) {
+			dfs->is_radar_during_precac = 1;
+			DFS_PRINTK(
+				"Radar found on second segment VHT80 freq=%d MHz\n",
+				dfs->dfs_precac_secondary_freq);
+		} else {
+			DFS_PRINTK(
+				"Radar found on channel %d (%d MHz)\n",
+				thischan->ic_ieee, thischan->ic_freq);
+		}
+
+		/*
+		 * If event log is on then dump the radar event queue on
+		 * filter match. This can be used to collect information
+		 * on false radar detection.
+		 */
+		if (dfs->dfs_event_log_on)
+			dfs_print_radar_events(dfs);
+
+		dfs_reset_radarq(dfs);
+		dfs_reset_alldelaylines(dfs);
+
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+			"Primary channel freq = %u flags=0x%x\n",
+			chan->ic_freq, chan->ic_flagext);
+
+		if (chan->ic_freq != thischan->ic_freq)
+			DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+				"Ext channel freq = %u flags=0x%x\n",
+				thischan->ic_freq,
+				thischan->ic_flagext);
+
+		dfs->dfs_phyerr_freq_min = 0x7fffffff;
+		dfs->dfs_phyerr_freq_max = 0;
+		dfs->dfs_phyerr_w53_counter = 0;
+		if (seg_id == SEG_ID_SECONDARY) {
+			dfs->wlan_dfs_stats.num_seg_two_radar_detects++;
+			dfs->is_radar_found_on_secondary_seg = 1;
+		}
+	}
+
+	return retval;
+}

+ 269 - 0
umac/dfs/core/src/filtering/dfs_staggered.c

@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications 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.
+ */
+
+/**
+ * DOC: ETSI 1.5.1 introduced new waveforms which use staggered PRIs within
+ * the same waveform. This file contains the detection implementation for
+ * these specific types of radars. This logic is different from the other
+ * detection because it must detect waveforms that may have 2 or more
+ * different PRIs (pulse repetition intervals).
+ */
+
+#include "../dfs.h"
+
+/**
+ * dfs_is_pri_multiple() - Is PRI is multiple.
+ * @sample_pri: Sample PRI.
+ * @refpri: Reference PRI.
+ */
+static int dfs_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))
+			return 1;
+	}
+
+	return 0;
+#undef MAX_ALLOWED_MISSED
+}
+
+/**
+ * dfs_is_unique_pri() - Check for the unique PRI.
+ * @highestpri: Highest PRI.
+ * @midpri: MID PRI.
+ * @lowestpri: Lowest PRI.
+ * @refpri: Reference PRI.
+ */
+static int dfs_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;
+
+	if ((dfs_is_pri_multiple(refpri, highestpri)) ||
+			(dfs_is_pri_multiple(refpri, lowestpri)) ||
+			(dfs_is_pri_multiple(refpri, midpri)))
+		return 0;
+#undef DFS_STAGGERED_PRI_MARGIN_MIN
+#undef DFS_STAGGERED_PRI_MARGIN_MAX
+
+	return 0;
+}
+
+int dfs_staggered_check(struct wlan_dfs *dfs, struct dfs_filter *rf,
+		uint32_t deltaT, uint32_t width)
+{
+	uint32_t refpri, refdur, searchpri = 0, deltapri;
+	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;
+	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, WLAN_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, WLAN_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;
+
+	qdf_mem_zero(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, WLAN_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) && (dfs_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) &&
+					(dfs_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) &&
+					(dfs_is_unique_pri(highestpri, midpri,
+							   lowestpri, refpri))
+					) {
+				lowestscore = score[n];
+				lowestpri = refpri;
+				lowestscoreindex = n;
+			}
+		}
+	}
+
+	if (midscore == 0)
+		return 0;
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+			"FINAL highestscore=%d highestscoreindex = %d highestpri = %d\n",
+			highestscore, highestscoreindex, highestpri);
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS1,
+			"FINAL lowestscore=%d lowestscoreindex=%d lowpri=%d\n",
+			lowestscore, lowestscoreindex, lowestpri);
+
+	DFS_DPRINTK(dfs, WLAN_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, WLAN_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, WLAN_DEBUG_DFS1,
+			"midscoreindex=%d refdur=%d refpri=%d\n",
+			midscoreindex, refdur, refpri);
+
+	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;
+
+	DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+			"numpulseshigh=%d, numpulsesmid=%d\n",
+			numpulseshigh, numpulsesmid);
+
+	if ((numpulseshigh >= rf->rf_threshold) &&
+			(numpulsesmid >= rf->rf_threshold)) {
+		found = 1;
+		DFS_DPRINTK(dfs, WLAN_DEBUG_DFS2,
+				"MATCH filter=%u numpulseshigh=%u numpulsesmid= %u thresh=%u\n",
+				rf->rf_pulseid, numpulseshigh,
+				numpulsesmid, rf->rf_threshold);
+	}
+
+	return found;
+}