diff --git a/umac/dfs/core/src/dfs.h b/umac/dfs/core/src/dfs.h new file mode 100644 index 000000000000..005f87331f85 --- /dev/null +++ b/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_NBUF_EXEMPT_NO_EXEMPTION, etc. */ +#include /* QDF_NBUF_EXEMPT_NO_EXEMPTION, etc. */ +#include /* qdf_nbuf_t, etc. */ +#include /* qdf_assert */ +#include /* qdf_spinlock */ +#include +#include + +#include "dfs_structs.h" +#include "dfs_channel.h" +#include "dfs_ioctl.h" +#include "dfs_ioctl_private.h" +#include /* For qdf_packed*/ +#include /* For STAILQ_ENTRY */ +#include +#include +#include + +#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_ */ diff --git a/umac/dfs/core/src/dfs_channel.h b/umac/dfs/core/src/dfs_channel.h new file mode 100644 index 000000000000..aec5378ee10a --- /dev/null +++ b/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_ */ diff --git a/umac/dfs/core/src/dfs_internal.h b/umac/dfs/core/src/dfs_internal.h new file mode 100644 index 000000000000..e0c3ba9b09f4 --- /dev/null +++ b/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 +#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_ */ diff --git a/umac/dfs/core/src/dfs_ioctl.h b/umac/dfs/core/src/dfs_ioctl.h new file mode 100644 index 000000000000..cf2e23eb6fd2 --- /dev/null +++ b/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_ */ diff --git a/umac/dfs/core/src/dfs_ioctl_private.h b/umac/dfs/core/src/dfs_ioctl_private.h new file mode 100644 index 000000000000..5609633be107 --- /dev/null +++ b/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_ */ diff --git a/umac/dfs/core/src/dfs_phyerr_tlv.h b/umac/dfs/core/src/dfs_phyerr_tlv.h new file mode 100644 index 000000000000..0fad819bde94 --- /dev/null +++ b/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_ */ diff --git a/umac/dfs/core/src/dfs_structs.h b/umac/dfs/core/src/dfs_structs.h new file mode 100644 index 000000000000..23a6a0274726 --- /dev/null +++ b/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_ */ diff --git a/umac/dfs/core/src/filtering/dfs_ar.c b/umac/dfs/core/src/filtering/dfs_ar.c new file mode 100644 index 000000000000..dcf03f08fc9b --- /dev/null +++ b/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); +} diff --git a/umac/dfs/core/src/filtering/dfs_bindetects.c b/umac/dfs/core/src/filtering/dfs_bindetects.c new file mode 100644 index 000000000000..1af6066d4e49 --- /dev/null +++ b/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; +} diff --git a/umac/dfs/core/src/filtering/dfs_debug.c b/umac/dfs/core/src/filtering/dfs_debug.c new file mode 100644 index 000000000000..32318ce04cea --- /dev/null +++ b/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); + } + } + } +} diff --git a/umac/dfs/core/src/filtering/dfs_fcc_bin5.c b/umac/dfs/core/src/filtering/dfs_fcc_bin5.c new file mode 100644 index 000000000000..e90f18ea47a1 --- /dev/null +++ b/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; +} diff --git a/umac/dfs/core/src/filtering/dfs_init.c b/umac/dfs/core/src/filtering/dfs_init.c new file mode 100644 index 000000000000..193dbc04cc7e --- /dev/null +++ b/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); +} diff --git a/umac/dfs/core/src/filtering/dfs_misc.c b/umac/dfs/core/src/filtering/dfs_misc.c new file mode 100644 index 000000000000..e71053f4e69a --- /dev/null +++ b/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; +} diff --git a/umac/dfs/core/src/filtering/dfs_phyerr_tlv.c b/umac/dfs/core/src/filtering/dfs_phyerr_tlv.c new file mode 100644 index 000000000000..26c1ad4f9eeb --- /dev/null +++ b/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; +} diff --git a/umac/dfs/core/src/filtering/dfs_process_phyerr.c b/umac/dfs/core/src/filtering/dfs_process_phyerr.c new file mode 100644 index 000000000000..7513038ae508 --- /dev/null +++ b/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 +} diff --git a/umac/dfs/core/src/filtering/dfs_process_radarevent.c b/umac/dfs/core/src/filtering/dfs_process_radarevent.c new file mode 100644 index 000000000000..1b181fb09bf8 --- /dev/null +++ b/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; +} diff --git a/umac/dfs/core/src/filtering/dfs_staggered.c b/umac/dfs/core/src/filtering/dfs_staggered.c new file mode 100644 index 000000000000..507923cb32f1 --- /dev/null +++ b/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; +}