mirror of
https://github.com/proski/madwifi
synced 2024-11-25 07:49:43 +03:00
fffa8d29b7
git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@4042 0192ed92-7a03-0410-a25b-9323aeb14dbd
1734 lines
51 KiB
C
1734 lines
51 KiB
C
/*
|
|
* This software is distributed under the terms of the
|
|
* GNU General Public License ("GPL") version 2 as published by the Free
|
|
* Software Foundation.
|
|
*
|
|
* NO WARRANTY
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
|
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGES.
|
|
*
|
|
* $Id: if_ath_radar.c 2464 2007-06-15 22:51:56Z mtaylor $
|
|
*/
|
|
#include "opt_ah.h"
|
|
|
|
#include "if_ath_debug.h"
|
|
|
|
#ifndef AUTOCONF_INCLUDED
|
|
#include <linux/config.h>
|
|
#endif
|
|
#include <linux/version.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/random.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/cache.h>
|
|
#include <linux/sysctl.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/time.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/param.h>
|
|
|
|
#include "if_ethersubr.h" /* for ETHER_IS_MULTICAST */
|
|
#include "if_media.h"
|
|
#include "if_llc.h"
|
|
|
|
#include <net80211/ieee80211_radiotap.h>
|
|
#include <net80211/ieee80211_var.h>
|
|
#include <net80211/ieee80211_monitor.h>
|
|
#include <net80211/ieee80211_rate.h>
|
|
|
|
#ifdef USE_HEADERLEN_RESV
|
|
#include <net80211/if_llc.h>
|
|
#endif
|
|
|
|
#include "net80211/if_athproto.h"
|
|
#include "if_athvar.h"
|
|
|
|
#include "ah_desc.h"
|
|
|
|
#include "ah_devid.h" /* XXX to identify chipset */
|
|
|
|
#ifdef ATH_PCI /* PCI BUS */
|
|
#include "if_ath_pci.h"
|
|
#endif /* PCI BUS */
|
|
#ifdef ATH_AHB /* AHB BUS */
|
|
#include "if_ath_ahb.h"
|
|
#endif /* AHB BUS */
|
|
|
|
#undef MAX
|
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
|
|
#undef MIN
|
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
|
|
#include "ah.h"
|
|
#include "if_ath_hal.h"
|
|
|
|
#ifdef ATH_TX99_DIAG
|
|
#include "ath_tx99.h"
|
|
#endif
|
|
|
|
#include "ah_os.h"
|
|
#include "if_ath_radar.h"
|
|
|
|
#define sizetab(t) (sizeof(t)/sizeof(t[0]))
|
|
#define nofloat_pct(_value, _pct) \
|
|
( (_value * (1000 + _pct)) / 1000 )
|
|
|
|
struct radar_pattern_specification {
|
|
/* The name of the rule/specification (i.e. what did we detect) */
|
|
const char *name;
|
|
/* Interval MIN = 1000000 / FREQ - 2%
|
|
* (a.k.a. Pulse/Burst Repetition Interval) */
|
|
u_int32_t min_rep_int;
|
|
/* Interval MAX = 1000000 / FREQ + 2%
|
|
* (a.k.a. Pulse/Burst Repetition Interval) */
|
|
u_int32_t max_rep_int;
|
|
/* Do we adjust the min/max interval values dynamically
|
|
* based upon running mean interval? */
|
|
HAL_BOOL dyn_ints;
|
|
/* Fuzz factor dynamic matching, as unsigned integer percentage
|
|
* of variation (i.e. 2 for +/- 2% timing) */
|
|
u_int32_t fuzz_pct;
|
|
/* Match MIN (Minimum Pulse/Burst events required) */
|
|
u_int32_t min_pulse;
|
|
/* Match MIN duration (Minimum Pulse/Burst events
|
|
* required including missed) */
|
|
u_int32_t min_evts;
|
|
/* Match MAX duration (Maximum Pulse/Burst events
|
|
* required including missed) */
|
|
u_int32_t max_evts;
|
|
/* Maximum consecutive missing pulses */
|
|
u_int32_t max_consecutive_missing;
|
|
/* Maximum missing pulses */
|
|
u_int32_t max_missing;
|
|
/* Match on absolute distance to PRI/PRF midpoint */
|
|
HAL_BOOL match_midpoint;
|
|
};
|
|
|
|
static struct radar_pattern_specification radar_patterns[] = {
|
|
#ifdef DFS_DOMAIN_ETSI
|
|
{"ETSI - PRF200", 4900, 5100, AH_FALSE, 20, 3, 4, 10, 4, 8, AH_TRUE},
|
|
{"ETSI - PRF300", 3267, 3399, AH_FALSE, 20, 3, 4, 10, 4, 6, AH_TRUE},
|
|
{"ETSI - PRF500", 1960, 2040, AH_FALSE, 20, 4, 4, 10, 4, 8, AH_TRUE},
|
|
{"ETSI - PRF750", 1307, 1359, AH_FALSE, 20, 5, 4, 15, 4, 13, AH_TRUE},
|
|
{"ETSI - PRF800", 1225, 1275, AH_FALSE, 20, 4, 4, 10, 4, 8, AH_TRUE},
|
|
{"ETSI - PRF1000", 980, 1020, AH_FALSE, 20, 4, 4, 10, 4, 8, AH_TRUE},
|
|
{"ETSI - PRF1200", 817, 849, AH_FALSE, 20, 5, 4, 15, 4, 13, AH_TRUE},
|
|
{"ETSI - PRF1500", 653, 679, AH_FALSE, 20, 5, 4, 15, 4, 6, AH_TRUE},
|
|
{"ETSI - PRF1600", 613, 637, AH_FALSE, 20, 5, 4, 15, 4, 7, AH_TRUE},
|
|
{"ETSI - PRF2000", 490, 510, AH_FALSE, 20, 7, 4, 20, 4, 10, AH_TRUE},
|
|
{"ETSI - PRF2300", 426, 442, AH_FALSE, 20, 9, 4, 25, 6, 20, AH_TRUE},
|
|
{"ETSI - PRF3000", 327, 339, AH_FALSE, 20, 7, 4, 20, 5, 20, AH_TRUE},
|
|
{"ETSI - PRF3500", 280, 290, AH_FALSE, 20, 9, 4, 25, 2, 20, AH_TRUE},
|
|
{"ETSI - PRF4000", 245, 255, AH_FALSE, 20, 7, 4, 20, 5, 20, AH_TRUE},
|
|
#endif
|
|
#ifdef DFS_DOMAIN_FCC
|
|
{"FCC - Type 1", 1399, 1714, AH_TRUE, 10, 5, 10, 18, 4, 6, AH_FALSE},
|
|
{"FCC - Type 2", 147, 235, AH_TRUE, 10, 8, 10, 29, 6, 12, AH_FALSE},
|
|
{"FCC - Type 3-4", 196, 273, AH_TRUE, 10, 8, 8, 18, 2, 16, AH_FALSE},
|
|
{"FCC - Type 3-4", 275, 352, AH_TRUE, 10, 8, 8, 18, 2, 16, AH_FALSE},
|
|
{"FCC - Type 3-4", 354, 431, AH_TRUE, 10, 8, 8, 18, 2, 16, AH_FALSE},
|
|
{"FCC - Type 3-4", 433, 510, AH_TRUE, 10, 8, 8, 18, 2, 16, AH_FALSE},
|
|
{"FCC - Type 3-4", 235, 313, AH_TRUE, 10, 8, 8, 18, 2, 16, AH_FALSE},
|
|
{"FCC - Type 3-4", 314, 392, AH_TRUE, 10, 8, 8, 18, 2, 16, AH_FALSE},
|
|
{"FCC - Type 3-4", 393, 471, AH_TRUE, 10, 8, 8, 18, 2, 16, AH_FALSE}
|
|
#endif
|
|
};
|
|
|
|
#ifdef AR_DEBUG
|
|
static u_int32_t interval_to_frequency(u_int32_t pri);
|
|
#endif /* AR_DEBUG */
|
|
|
|
/* Returns true if radar detection is enabled. */
|
|
int ath_radar_is_enabled(struct ath_softc *sc)
|
|
{
|
|
struct ath_hal *ah = sc->sc_ah;
|
|
if (ar_device(sc) >= 5211)
|
|
return ((OS_REG_READ(ah, AR5K_AR5212_PHY_ERR_FIL) &
|
|
AR5K_AR5212_PHY_ERR_FIL_RADAR) &&
|
|
(sc->sc_imask & HAL_INT_RXPHY) &&
|
|
(ath_hal_intrget(ah) & HAL_INT_RXPHY));
|
|
else
|
|
return ((sc->sc_imask & HAL_INT_RXPHY) &&
|
|
(ath_hal_intrget(ah) & HAL_INT_RXPHY));
|
|
return 0;
|
|
}
|
|
|
|
/* Read the radar pulse detection parameters. */
|
|
void ath_radar_get_params(struct ath_softc *sc, RADAR_PARAM *rp)
|
|
{
|
|
u_int32_t radar = ath_reg_read(sc, AR5K_PHY_RADAR);
|
|
rp->rp_fir_filter_output_power_thr =
|
|
(radar & AR5K_PHY_RADAR_FIRPWROUTTHR) >>
|
|
AR5K_PHY_RADAR_FIRPWROUTTHR_S;
|
|
rp->rp_radar_rssi_thr =
|
|
(radar & AR5K_PHY_RADAR_PULSERSSITHR) >>
|
|
AR5K_PHY_RADAR_PULSERSSITHR_S;
|
|
rp->rp_pulse_height_thr =
|
|
(radar & AR5K_PHY_RADAR_PULSEHEIGHTTHR) >>
|
|
AR5K_PHY_RADAR_PULSEHEIGHTTHR_S;
|
|
rp->rp_pulse_rssi_thr =
|
|
(radar & AR5K_PHY_RADAR_RADARRSSITHR) >>
|
|
AR5K_PHY_RADAR_RADARRSSITHR_S;
|
|
rp->rp_inband_thr =
|
|
(radar & AR5K_PHY_RADAR_INBANDTHR) >>
|
|
AR5K_PHY_RADAR_INBANDTHR_S;
|
|
}
|
|
|
|
/* Update the radar pulse detection parameters.
|
|
* If rp is NULL, defaults are used for all fields.
|
|
* If any member of rp is set to RADAR_PARAM_USE_DEFAULT, the default
|
|
* is used for that field. */
|
|
void ath_radar_set_params(struct ath_softc *sc, RADAR_PARAM *rp)
|
|
{
|
|
#define BUILD_PHY_RADAR_FIELD(_MASK,_SHIFT,_FIELD) \
|
|
((NULL == rp || (rp->_FIELD == RADAR_PARAM_USE_DEFAULT)) ? \
|
|
((AR5K_PHY_RADAR_ENABLED_AR5213 & (_MASK))) : \
|
|
((rp->_FIELD << (_SHIFT)) & (_MASK)))
|
|
ath_reg_write(sc, AR5K_PHY_RADAR,
|
|
BUILD_PHY_RADAR_FIELD(AR5K_PHY_RADAR_FIRPWROUTTHR,
|
|
AR5K_PHY_RADAR_FIRPWROUTTHR_S,
|
|
rp_fir_filter_output_power_thr) |
|
|
BUILD_PHY_RADAR_FIELD(AR5K_PHY_RADAR_RADARRSSITHR,
|
|
AR5K_PHY_RADAR_RADARRSSITHR_S,
|
|
rp_pulse_rssi_thr) |
|
|
BUILD_PHY_RADAR_FIELD(AR5K_PHY_RADAR_PULSEHEIGHTTHR,
|
|
AR5K_PHY_RADAR_PULSEHEIGHTTHR_S,
|
|
rp_pulse_height_thr) |
|
|
BUILD_PHY_RADAR_FIELD(AR5K_PHY_RADAR_PULSERSSITHR,
|
|
AR5K_PHY_RADAR_PULSERSSITHR_S, rp_radar_rssi_thr) |
|
|
BUILD_PHY_RADAR_FIELD(AR5K_PHY_RADAR_INBANDTHR,
|
|
AR5K_PHY_RADAR_INBANDTHR_S,
|
|
rp_inband_thr)
|
|
);
|
|
#undef BUILD_PHY_RADAR_FIELD
|
|
}
|
|
|
|
/* This is called on channel change to enable radar detection for 5211+ chips.
|
|
* NOTE: AR5210 doesn't have radar pulse detection support. */
|
|
int ath_radar_update(struct ath_softc *sc)
|
|
{
|
|
|
|
struct ath_hal *ah = sc->sc_ah;
|
|
struct ieee80211com *ic = &sc->sc_ic;
|
|
int required = 0;
|
|
|
|
/* Do not attempt to change radar state when bg scanning is
|
|
* the cause */
|
|
if (ic->ic_flags & IEEE80211_F_SCAN)
|
|
return 1;
|
|
|
|
/* Update the DFS flags (as a sanity check) */
|
|
if (ath_radar_correct_dfs_flags(sc, &sc->sc_curchan))
|
|
DPRINTF(sc, ATH_DEBUG_DOTH, "%s: %s: channel required "
|
|
"corrections to private flags.\n",
|
|
SC_DEV_NAME(sc), __func__);
|
|
required = ath_radar_is_dfs_required(sc, &sc->sc_curchan) &&
|
|
(ic->ic_flags & IEEE80211_F_DOTH);
|
|
/* configure radar pulse detector register using default values, but do
|
|
* not toggle the enable bit. XXX: allow tweaking?? */
|
|
ath_radar_set_params(sc, NULL);
|
|
if (ar_device(sc) >= 5211) {
|
|
HAL_INT old_ier = ath_hal_intrget(ah);
|
|
HAL_INT new_ier = old_ier;
|
|
unsigned int old_radar = OS_REG_READ(ah, AR5K_PHY_RADAR);
|
|
unsigned int old_filter =
|
|
OS_REG_READ(ah, AR5K_AR5212_PHY_ERR_FIL);
|
|
unsigned int old_rxfilt = ath_hal_getrxfilter(ah);
|
|
unsigned int old_mask = sc->sc_imask;
|
|
unsigned int new_radar = old_radar;
|
|
unsigned int new_filter = old_filter;
|
|
unsigned int new_mask = old_mask;
|
|
unsigned int new_rxfilt = old_rxfilt;
|
|
|
|
ath_hal_intrset(ah, old_ier & ~HAL_INT_GLOBAL);
|
|
if (required) {
|
|
new_radar |= AR5K_PHY_RADAR_ENABLE;
|
|
new_filter |= AR5K_AR5212_PHY_ERR_FIL_RADAR;
|
|
new_rxfilt |= (HAL_RX_FILTER_PHYERR |
|
|
HAL_RX_FILTER_PHYRADAR);
|
|
new_mask |= HAL_INT_RXPHY;
|
|
new_ier |= HAL_INT_RXPHY;
|
|
} else {
|
|
new_radar &= ~AR5K_PHY_RADAR_ENABLE;
|
|
new_filter &= ~AR5K_AR5212_PHY_ERR_FIL_RADAR;
|
|
new_rxfilt &= ~HAL_RX_FILTER_PHYRADAR;
|
|
new_mask &= ~HAL_INT_RXPHY;
|
|
new_ier &= ~HAL_INT_RXPHY;
|
|
}
|
|
|
|
if (old_filter != new_filter)
|
|
OS_REG_WRITE(ah, AR5K_AR5212_PHY_ERR_FIL, new_filter);
|
|
if (old_radar != new_radar)
|
|
OS_REG_WRITE(ah, AR5K_PHY_RADAR, new_radar);
|
|
if (old_rxfilt != new_rxfilt)
|
|
ath_hal_setrxfilter(ah, new_rxfilt);
|
|
|
|
sc->sc_imask = new_mask;
|
|
if (DFLAG_ISSET(sc, ATH_DEBUG_DOTH) &&
|
|
((old_radar != new_radar) ||
|
|
(old_filter != new_filter) ||
|
|
(old_rxfilt != new_rxfilt) ||
|
|
(old_mask != new_mask) ||
|
|
(old_ier != new_ier))) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTH,
|
|
"%s: %s: Radar detection %s.\n", SC_DEV_NAME(sc),
|
|
__func__, required ? "enabled" : "disabled");
|
|
}
|
|
ath_hal_intrset(ah, new_ier);
|
|
}
|
|
|
|
return (required == ath_radar_is_enabled(sc));
|
|
}
|
|
|
|
/* Update channel's DFS flags based upon whether DFS is required. Return
|
|
* true if the value was repaired. */
|
|
int ath_radar_correct_dfs_flags(struct ath_softc *sc, HAL_CHANNEL *hchan)
|
|
{
|
|
u_int32_t old_channelFlags = hchan->channelFlags;
|
|
u_int32_t old_privFlags = hchan->privFlags;
|
|
if (ath_radar_is_dfs_required(sc, hchan)) {
|
|
hchan->channelFlags |= CHANNEL_PASSIVE;
|
|
hchan->privFlags |= CHANNEL_DFS;
|
|
} else {
|
|
hchan->channelFlags &= ~CHANNEL_PASSIVE;
|
|
hchan->privFlags &= ~CHANNEL_DFS;
|
|
}
|
|
return ((old_privFlags != hchan->privFlags) ||
|
|
(old_channelFlags != hchan->channelFlags));
|
|
}
|
|
|
|
/* Returns true if DFS is required for the regulatory domain, country and
|
|
* combination in use.
|
|
* XXX: Need to add regulatory rules in here. This is too conservative! */
|
|
int ath_radar_is_dfs_required(struct ath_softc *sc, HAL_CHANNEL *hchan)
|
|
{
|
|
/* For FCC: 5250 to 5350MHz (channel 52 to 60) and for Europe added
|
|
* 5470 to 5725 MHz (channel 100 to 140).
|
|
* Being conservative, go with the entire band from 5250-5725 MHz. */
|
|
return ((hchan->channel >= 5250) && (hchan->channel <= 5725)) ? 1 : 0;
|
|
}
|
|
|
|
static struct ath_rp *pulse_head(struct ath_softc *sc)
|
|
{
|
|
return list_entry(sc->sc_rp_list.next,
|
|
struct ath_rp, list);
|
|
}
|
|
|
|
static struct ath_rp *pulse_tail(struct ath_softc *sc)
|
|
{
|
|
return list_entry(sc->sc_rp_list.prev,
|
|
struct ath_rp, list);
|
|
}
|
|
|
|
static struct ath_rp *pulse_prev(struct ath_rp *pulse)
|
|
{
|
|
return list_entry(pulse->list.prev,
|
|
struct ath_rp, list);
|
|
}
|
|
|
|
#define CR_FALLTHROUGH 0
|
|
#define CR_NULL 1
|
|
#define CR_EXCESS_INTERVALS 2
|
|
#define CR_INTERVALS 3
|
|
#define CR_EXCESS_DURATION 4
|
|
#define CR_DURATION 5
|
|
#define CR_PULSES 6
|
|
#define CR_MISSES 7
|
|
#define CR_MIDPOINT_A 8
|
|
#define CR_MIDPOINT_B 9
|
|
#define CR_MIDPOINT_C 10
|
|
#define CR_NOISE 11
|
|
|
|
#define MR_MATCH 0
|
|
#define MR_FAIL_MIN_INTERVALS 1
|
|
#define MR_FAIL_REQD_MATCHES 2
|
|
#define MR_FAIL_MAX_MISSES 3
|
|
#define MR_FAIL_MIN_PERIOD 4
|
|
#define MR_FAIL_MAX_PERIOD 5
|
|
|
|
#ifdef AR_DEBUG
|
|
static const char *get_match_result_desc(u_int32_t code)
|
|
{
|
|
switch (code) {
|
|
case MR_MATCH:
|
|
return "MATCH";
|
|
case MR_FAIL_MIN_INTERVALS:
|
|
return "TOO-SHORT";
|
|
case MR_FAIL_REQD_MATCHES:
|
|
return "TOO-FEW";
|
|
case MR_FAIL_MAX_MISSES:
|
|
return "TOO-LOSSY";
|
|
case MR_FAIL_MIN_PERIOD:
|
|
return "PRI<MIN";
|
|
case MR_FAIL_MAX_PERIOD:
|
|
return "PRI>MAX";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
#endif /* AR_DEBUG */
|
|
|
|
static int32_t match_radar(
|
|
u_int32_t matched,
|
|
u_int32_t missed,
|
|
u_int32_t mean_period,
|
|
u_int32_t noise,
|
|
u_int32_t min_evts,
|
|
u_int32_t max_evts,
|
|
u_int32_t min_rep_int,
|
|
u_int32_t max_rep_int,
|
|
u_int32_t min_pulse,
|
|
u_int32_t max_misses)
|
|
{
|
|
/* Not a match: insufficient overall burst length */
|
|
if ( (matched + missed) < min_evts)
|
|
return MR_FAIL_MIN_INTERVALS;
|
|
|
|
/* Not a match: insufficient match count */
|
|
if (matched < min_pulse)
|
|
return MR_FAIL_REQD_MATCHES;
|
|
|
|
/* Not a match: too many missies */
|
|
if (missed > max_misses)
|
|
return MR_FAIL_MAX_MISSES;
|
|
|
|
/* Not a match, PRI out of range */
|
|
if (mean_period < min_rep_int)
|
|
return MR_FAIL_MIN_PERIOD;
|
|
|
|
/* Not a match, PRI out of range */
|
|
if (mean_period > max_rep_int)
|
|
return MR_FAIL_MAX_PERIOD;
|
|
|
|
return MR_MATCH;
|
|
}
|
|
|
|
static int32_t compare_radar_matches(
|
|
int32_t a_matched,
|
|
int32_t a_missed,
|
|
int32_t a_mean_period,
|
|
int32_t a_noise,
|
|
int32_t a_min_evts,
|
|
int32_t a_max_evts,
|
|
int32_t a_min_rep_int,
|
|
int32_t a_max_rep_int,
|
|
int32_t a_min_pulse,
|
|
int32_t a_max_misses,
|
|
HAL_BOOL a_match_midpoint,
|
|
int32_t b_matched,
|
|
int32_t b_missed,
|
|
int32_t b_mean_period,
|
|
int32_t b_noise,
|
|
int32_t b_min_evts,
|
|
int32_t b_max_evts,
|
|
int32_t b_min_rep_int,
|
|
int32_t b_max_rep_int,
|
|
int32_t b_min_pulse,
|
|
int32_t b_max_misses,
|
|
HAL_BOOL b_match_midpoint
|
|
)
|
|
{
|
|
/* Intermediate calculations */
|
|
int32_t a_total = a_matched + a_missed;
|
|
int32_t b_total = b_matched + b_missed;
|
|
int32_t a_excess_total =
|
|
MAX((int32_t)(a_total - (int32_t)a_max_evts), 0);
|
|
int32_t b_excess_total =
|
|
MAX((int32_t)(b_total - (int32_t)b_max_evts), 0);
|
|
u_int64_t a_duration = a_total * a_mean_period;
|
|
u_int64_t b_duration = b_total * b_mean_period;
|
|
u_int64_t a_excess_duration = a_excess_total * a_mean_period;
|
|
u_int64_t b_excess_duration = b_excess_total * b_mean_period;
|
|
u_int64_t a_dist_from_pri_mid = labs(a_mean_period -
|
|
(a_min_rep_int +
|
|
((a_max_rep_int - a_min_rep_int) / 2)));
|
|
u_int64_t b_dist_from_pri_mid = labs(b_mean_period -
|
|
(b_min_rep_int +
|
|
((b_max_rep_int - b_min_rep_int) / 2)));
|
|
/* Did one radar have fewer excess total pulse intervals than the
|
|
* other? */
|
|
if (a_excess_total != b_excess_total)
|
|
return ((a_excess_total < b_excess_total) ? 1 : -1) *
|
|
CR_EXCESS_INTERVALS;
|
|
/* Was one pulse longer chronologically, even though totals matched? */
|
|
else if (a_excess_duration != b_excess_duration)
|
|
return ((a_excess_duration < b_excess_duration) ? 1 : -1) *
|
|
CR_EXCESS_DURATION;
|
|
/* Did one get more matches? */
|
|
if (a_matched != b_matched)
|
|
return (a_matched > b_matched ? 1 : -1) * CR_PULSES;
|
|
/* Both waveforms are the same length, same total.
|
|
* Did one get more misses? */
|
|
if (a_missed != b_missed)
|
|
return (a_missed < b_missed ? 1 : -1) * CR_MISSES;
|
|
/* Did one get more noise? */
|
|
if (a_noise != b_noise)
|
|
return (a_noise < b_noise ? 1 : -1) * CR_NOISE;
|
|
/* If both waveforms were not too long in terms of intervals */
|
|
if (0 == (a_excess_total+b_excess_total)) {
|
|
/* Did one waveform have to match more events than the other? */
|
|
if (a_total != b_total)
|
|
return ((a_total > b_total) ? 1 : -1) * CR_INTERVALS;
|
|
/* Was one waveform longer than the other */
|
|
if (a_duration != b_duration)
|
|
return ((a_duration > b_duration) ? 1 : -1) * CR_DURATION;
|
|
}
|
|
/* both durations are legal, but one is closer to the original PRF/PRI */
|
|
if (a_dist_from_pri_mid != b_dist_from_pri_mid) {
|
|
if (a_match_midpoint && b_match_midpoint) {
|
|
/* Which pattern is closer to midpoint? */
|
|
return ((a_dist_from_pri_mid < b_dist_from_pri_mid) ? 1 : -1) *
|
|
CR_MIDPOINT_A;
|
|
}
|
|
else if (a_match_midpoint) {
|
|
/* If not within spitting distance of midpoint, reject */
|
|
return ((a_dist_from_pri_mid < 3) ? 1 : -1) *
|
|
CR_MIDPOINT_B;
|
|
|
|
}
|
|
else if (b_match_midpoint) {
|
|
/* If not within spitting distance of midpoint, reject */
|
|
return ((b_dist_from_pri_mid >= 3) ? 1 : -1) *
|
|
CR_MIDPOINT_C;
|
|
}
|
|
}
|
|
return -CR_FALLTHROUGH;
|
|
}
|
|
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
|
|
struct lp_burst {
|
|
u_int32_t lpb_num_pulses;
|
|
u_int32_t lpb_num_noise;
|
|
u_int32_t lpb_tsf_delta;
|
|
u_int64_t lpb_tsf_rel;
|
|
u_int64_t lpb_min_possible_tsf; /* noise vs real pulses */
|
|
u_int64_t lpb_max_possible_tsf; /* noise vs real pulses */
|
|
};
|
|
|
|
static const u_int32_t LP_MIN_BC = 8;
|
|
static const u_int32_t LP_MAX_BC = 20;
|
|
static const u_int32_t LP_NUM_BC = 13; /* (LP_MAX_BC - LP_MIN_BC + 1); */
|
|
static const u_int64_t LP_TSF_FUZZ_US = 32768; /* (1<<15) because rs_tstamp
|
|
* rollover errors */
|
|
static const u_int32_t LP_MIN_PRI = 1000;
|
|
static const u_int32_t LP_MAX_PRI = 2000;
|
|
|
|
static void rp_analyze_long_pulse_bscan(
|
|
struct ath_softc *sc,
|
|
struct ath_rp *last_pulse,
|
|
u_int32_t *num_bursts,
|
|
size_t bursts_buflen,
|
|
struct lp_burst *bursts)
|
|
{
|
|
int i = 0;
|
|
struct ath_rp *newer = NULL;
|
|
struct ath_rp *cur = last_pulse;
|
|
struct ath_rp *older = pulse_prev(last_pulse);
|
|
u_int32_t waveform_num_bursts = 0;
|
|
|
|
if (num_bursts)
|
|
*num_bursts = 0;
|
|
|
|
for (;;) {
|
|
/* check if we are at the end of the list */
|
|
if (&cur->list == &sc->sc_rp_head)
|
|
break;
|
|
if (!cur->rp_allocated)
|
|
break;
|
|
|
|
if (NULL != newer) {
|
|
u_int64_t tsf_delta = 0;
|
|
u_int64_t tsf_adjustment = 0;
|
|
|
|
/* Figure out TSF delta, taking into account
|
|
* up to one multiple of (1<<15) of clock jitter
|
|
* due to interrupt latency */
|
|
tsf_delta = newer->rp_tsf - cur->rp_tsf;
|
|
if ((tsf_delta - LP_TSF_FUZZ_US) >= LP_MIN_PRI &&
|
|
(tsf_delta - LP_TSF_FUZZ_US) <= LP_MAX_PRI) {
|
|
tsf_adjustment = LP_TSF_FUZZ_US;
|
|
tsf_delta -= tsf_adjustment;
|
|
}
|
|
|
|
/* If we are in range for pulse, assume it is a pulse. */
|
|
if ((tsf_delta >= LP_MIN_PRI) && (tsf_delta <= LP_MAX_PRI)) {
|
|
bursts[waveform_num_bursts].lpb_num_pulses++;
|
|
bursts[waveform_num_bursts].lpb_min_possible_tsf =
|
|
cur->rp_tsf - tsf_adjustment;
|
|
}
|
|
else if (tsf_delta < LP_MIN_PRI) {
|
|
bursts[waveform_num_bursts].lpb_num_noise++;
|
|
/* It may have been THE pulse after all... */
|
|
bursts[waveform_num_bursts].lpb_min_possible_tsf =
|
|
cur->rp_tsf - tsf_adjustment;
|
|
}
|
|
else /* tsf_delta > LP_MAX_PRI */ {
|
|
bursts[waveform_num_bursts].lpb_num_pulses++;
|
|
bursts[waveform_num_bursts].lpb_min_possible_tsf =
|
|
cur->rp_tsf;
|
|
/* Do not overrun bursts_buflen */
|
|
if ((waveform_num_bursts+1) >= bursts_buflen) {
|
|
break;
|
|
}
|
|
waveform_num_bursts++;
|
|
bursts[waveform_num_bursts].lpb_tsf_delta = tsf_delta;
|
|
bursts[waveform_num_bursts].lpb_min_possible_tsf =
|
|
cur->rp_tsf;
|
|
bursts[waveform_num_bursts].lpb_max_possible_tsf =
|
|
cur->rp_tsf;
|
|
}
|
|
}
|
|
else {
|
|
bursts[waveform_num_bursts].lpb_max_possible_tsf =
|
|
cur->rp_tsf;
|
|
}
|
|
|
|
/* advance to next pulse */
|
|
newer = cur;
|
|
cur = pulse_prev(cur);
|
|
older = pulse_prev(cur);
|
|
}
|
|
if (num_bursts) {
|
|
bursts[waveform_num_bursts].lpb_num_pulses++;
|
|
waveform_num_bursts++;
|
|
*num_bursts = waveform_num_bursts;
|
|
}
|
|
for (i = 0; i < waveform_num_bursts; i++)
|
|
bursts[i].lpb_tsf_rel =
|
|
bursts[i].lpb_max_possible_tsf -
|
|
bursts[waveform_num_bursts-1].lpb_min_possible_tsf;
|
|
}
|
|
|
|
static HAL_BOOL rp_analyze_long_pulse(
|
|
struct ath_softc *sc, struct ath_rp *last_pulse,
|
|
u_int32_t *bc,
|
|
u_int32_t *matched, u_int32_t *missed,
|
|
u_int32_t *noise, u_int32_t *pulses)
|
|
{
|
|
int i;
|
|
int32_t found_radar = 0;
|
|
int32_t found_burst_count = 0;
|
|
int32_t matching_burst_count = 0;
|
|
u_int32_t best_bc = 0;
|
|
u_int32_t best_matched = 0;
|
|
u_int32_t best_missed = 0;
|
|
u_int32_t best_noise = 0;
|
|
u_int32_t best_pulses = 0;
|
|
|
|
struct lp_burst bursts[LP_MAX_BC];
|
|
memset(&bursts, 0, sizeof(bursts));
|
|
|
|
if (bc)
|
|
*bc = 0;
|
|
if (matched)
|
|
*matched = 0;
|
|
if (missed)
|
|
*missed = 0;
|
|
if (noise)
|
|
*noise = 0;
|
|
|
|
rp_analyze_long_pulse_bscan(sc, last_pulse,
|
|
&found_burst_count,
|
|
LP_MAX_BC, &bursts[0]);
|
|
/* Find the matches */
|
|
for (matching_burst_count = LP_MAX_BC;
|
|
matching_burst_count >= LP_MIN_BC;
|
|
matching_burst_count--) {
|
|
int32_t first_matched_index = -1;
|
|
int32_t last_matched_index = -1;
|
|
int32_t match_burst_index = 0;
|
|
int32_t found_burst_index = 0;
|
|
int32_t burst_period = (12000000 / matching_burst_count);
|
|
int32_t waveform_offset = 0;
|
|
int32_t total_big_gaps = 0;
|
|
int32_t matched_span = 0;
|
|
int32_t missed_bursts = 0;
|
|
int32_t matched_bursts = 0;
|
|
for (i = 0; i < matching_burst_count; i++) {
|
|
int32_t d = bursts[i].lpb_tsf_delta;
|
|
while (d >= burst_period)
|
|
d -= burst_period;
|
|
total_big_gaps += d;
|
|
waveform_offset = MAX(waveform_offset, d);
|
|
}
|
|
waveform_offset *= -1;
|
|
|
|
found_burst_index = 0;
|
|
for (match_burst_index = 0;
|
|
match_burst_index < matching_burst_count;
|
|
match_burst_index++) {
|
|
int64_t limit_high = (burst_period *
|
|
(matching_burst_count - 1 -
|
|
match_burst_index + 1)) +
|
|
(2 * LP_TSF_FUZZ_US);
|
|
int64_t limit_low = (burst_period *
|
|
(matching_burst_count - 1 -
|
|
match_burst_index)) -
|
|
(2 * LP_TSF_FUZZ_US);
|
|
/* If the burst is too old, skip it... it's noise too... */
|
|
while ((((int64_t)bursts[found_burst_index].lpb_tsf_rel +
|
|
waveform_offset) >
|
|
limit_high)) {
|
|
if (found_burst_index < (found_burst_count - 1))
|
|
found_burst_index++;
|
|
else
|
|
break;
|
|
}
|
|
if ((((int64_t)bursts[found_burst_index].lpb_tsf_rel +
|
|
waveform_offset) <=
|
|
limit_high) &&
|
|
(((int64_t)bursts[found_burst_index].lpb_tsf_rel +
|
|
waveform_offset) >= limit_low)) {
|
|
if (-1 == first_matched_index) {
|
|
first_matched_index = match_burst_index;
|
|
matched_span = 1;
|
|
}
|
|
if (last_matched_index < match_burst_index) {
|
|
last_matched_index = match_burst_index;
|
|
}
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"LP %2dp] [%2d/%2d] %10lld "
|
|
"in {%lld:%lld}] PASS\n",
|
|
matching_burst_count,
|
|
found_burst_index,
|
|
match_burst_index,
|
|
(int64_t)bursts[found_burst_index].lpb_tsf_rel -
|
|
waveform_offset,
|
|
limit_low, limit_high);
|
|
matched_bursts++;
|
|
found_burst_index++;
|
|
}
|
|
else {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"LP %2dp] [%2d/%2d] %10lld "
|
|
"in {%lld:%lld}] MISSED\n",
|
|
matching_burst_count,
|
|
match_burst_index,
|
|
found_burst_index,
|
|
(int64_t)bursts[found_burst_index].lpb_tsf_rel -
|
|
waveform_offset,
|
|
limit_low, limit_high);
|
|
missed_bursts++;
|
|
}
|
|
}
|
|
matched_span = last_matched_index - first_matched_index;
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT, "LP %2dp] burst_period=%10d, "
|
|
"waveform_offset=%10d, matches=%2d/%2d, "
|
|
"result=%s\n",
|
|
matching_burst_count, burst_period, waveform_offset,
|
|
matched_span, matching_burst_count,
|
|
(matching_burst_count == matched_span) ?
|
|
"MATCH" : "MISMATCH"
|
|
);
|
|
/* XXX - Add comparison logic rather than taking first/last
|
|
* match based upon ATH_DEBUG_DOTHFILTNOSC? */
|
|
if (matched_span >= (matching_burst_count - 4)) {
|
|
found_radar++;
|
|
best_bc = matching_burst_count;
|
|
best_matched = matched_bursts;
|
|
best_missed = missed_bursts;
|
|
best_noise = 0;
|
|
best_pulses = 0;
|
|
for (i = 0; i <= found_burst_index; i++) {
|
|
best_noise += bursts[match_burst_index].lpb_num_noise;
|
|
best_pulses += bursts[match_burst_index].lpb_num_pulses;
|
|
}
|
|
if (!DFLAG_ISSET(sc, ATH_DEBUG_DOTHFILTNOSC))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bc)
|
|
*bc = best_bc;
|
|
if (matched)
|
|
*matched = best_matched;
|
|
if (missed)
|
|
*missed = best_missed;
|
|
if (noise)
|
|
*noise = best_noise;
|
|
if (pulses)
|
|
*pulses = best_pulses;
|
|
|
|
return found_radar ? AH_TRUE : AH_FALSE;
|
|
}
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
|
|
static HAL_BOOL rp_analyze_short_pulse(
|
|
struct ath_softc *sc, struct ath_rp *last_pulse,
|
|
u_int32_t *index, u_int32_t *pri, u_int32_t *matching_pulses,
|
|
u_int32_t *missed_pulses, u_int32_t *noise_pulses)
|
|
{
|
|
int i;
|
|
int best_index = -1;
|
|
unsigned int best_matched = 0;
|
|
unsigned int best_noise = 0;
|
|
unsigned int best_missed = 0;
|
|
unsigned int best_pri = 0;
|
|
unsigned int best_cr = 0;
|
|
|
|
u_int64_t t0_min, t0_max, t1, t_min, t_max;
|
|
u_int32_t noise = 0, matched = 0, missed = 0, partial_miss = 0;
|
|
struct ath_rp *pulse;
|
|
u_int32_t pulse_count_minimum = 0;
|
|
struct radar_pattern_specification *pattern = NULL;
|
|
struct radar_pattern_specification *best_pattern = NULL;
|
|
|
|
u_int32_t new_period = 0;
|
|
u_int64_t last_tsf = 0;
|
|
u_int32_t last_seen_period = 0;
|
|
u_int32_t sum_periods = 0;
|
|
u_int32_t mean_period = 0;
|
|
u_int32_t adjusted_max_rep_int = 0;
|
|
u_int32_t adjusted_min_rep_int = 0;
|
|
|
|
if (index)
|
|
*index = 0;
|
|
if (pri)
|
|
*pri = 0;
|
|
if (noise_pulses)
|
|
*noise_pulses = 0;
|
|
if (matching_pulses)
|
|
*matching_pulses = 0;
|
|
if (missed_pulses)
|
|
*missed_pulses = 0;
|
|
|
|
/* we need at least sc_rp_min (>2 pulses) */
|
|
pulse_count_minimum = sc->sc_rp_min;
|
|
if ((sc->sc_rp_num < pulse_count_minimum) ||
|
|
(sc->sc_rp_num < 2))
|
|
return 0;
|
|
|
|
/* Search algorithm:
|
|
*
|
|
* - since we have a limited and known number of radar patterns, we
|
|
* loop on all possible radar pulse period
|
|
*
|
|
* - we start the search from the last timestamp (t1), going
|
|
* backward in time, up to the point for the first possible radar
|
|
* pulse, ie t0_min - PERIOD * BURST_MAX
|
|
*
|
|
* - on this timescale, we matched the number of hit/missed using T -
|
|
* PERIOD * n taking into account the 2% error margin (using
|
|
* min_rep_int, max_rep_int)
|
|
*
|
|
* At the end, we have a number of pulse hit for each PRF
|
|
*
|
|
* TSF will roll over after just over 584,542 years of operation
|
|
* without restart.
|
|
*
|
|
* This exceeds all known Atheros MTBF so, forget about TSF roll over.
|
|
*/
|
|
|
|
/* t1 is the timestamp of the last radar pulse */
|
|
t1 = (u_int64_t)last_pulse->rp_tsf;
|
|
|
|
/* loop through all patterns */
|
|
for (i = 0; i < sizetab(radar_patterns); i++) {
|
|
pattern = &radar_patterns[i];
|
|
|
|
/* underflow */
|
|
if ((pattern->min_rep_int *
|
|
(pattern->min_evts - 1)) >= t1) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %s skipped (last pulse isn't old enough to"
|
|
" match this pattern). %10llu >= %10llu.\n",
|
|
SC_DEV_NAME(sc), pattern->name,
|
|
(unsigned long long)(
|
|
pattern->min_rep_int *
|
|
pattern->min_evts),
|
|
(unsigned long long)t1);
|
|
continue;
|
|
}
|
|
|
|
/* this min formula is to check for underflow. It's the
|
|
* minimum needed duration to gather specified number of
|
|
* matches, assuming minimum match interval. */
|
|
t0_min = (pattern->min_rep_int *
|
|
pattern->min_evts) < t1 ?
|
|
t1 - (pattern->min_rep_int *
|
|
pattern->min_evts) : 0;
|
|
|
|
/* this max formula is to stop when we exceed maximum time
|
|
* period for the pattern. It's the oldest possible TSF that
|
|
* could match. */
|
|
t0_max = (pattern->max_rep_int *
|
|
pattern->max_evts) < t1 ?
|
|
t1 - (pattern->max_rep_int *
|
|
pattern->max_evts) : 0;
|
|
|
|
/* we directly start with the timestamp before t1 */
|
|
pulse = pulse_prev(last_pulse);
|
|
|
|
/* initial values for t_min, t_max */
|
|
t_min = pattern->max_rep_int < t1 ?
|
|
t1 - pattern->max_rep_int : 0;
|
|
t_max = pattern->min_rep_int < t1 ?
|
|
t1 - pattern->min_rep_int : 0;
|
|
|
|
last_tsf = t1;
|
|
last_seen_period = 0;
|
|
sum_periods = 0;
|
|
matched = 0;
|
|
noise = 0;
|
|
missed = 0;
|
|
partial_miss = 0;
|
|
mean_period = 0;
|
|
adjusted_max_rep_int =
|
|
pattern->max_rep_int;
|
|
adjusted_min_rep_int =
|
|
pattern->min_rep_int;
|
|
|
|
for (;;) {
|
|
if (mean_period && pattern->dyn_ints) {
|
|
u_int32_t fuzz_pct = pattern->fuzz_pct;
|
|
adjusted_max_rep_int =
|
|
MIN(nofloat_pct(mean_period, fuzz_pct),
|
|
pattern->max_rep_int);
|
|
|
|
adjusted_min_rep_int =
|
|
MAX(nofloat_pct(mean_period, -fuzz_pct),
|
|
pattern->min_rep_int);
|
|
}
|
|
else {
|
|
adjusted_max_rep_int = pattern->max_rep_int;
|
|
adjusted_min_rep_int = pattern->min_rep_int;
|
|
}
|
|
|
|
/* check if we are at the end of the list */
|
|
if (&pulse->list == &sc->sc_rp_list)
|
|
break;
|
|
if (!pulse->rp_allocated)
|
|
break;
|
|
|
|
/* Do not go too far... this is an optimization to not
|
|
* keep checking after we hit maximum time span for the
|
|
* pattern. */
|
|
if (pulse->rp_tsf < t0_max) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %s matching stopped (pulse->rp_tsf"
|
|
" < t0_max). t1=%10llu t0_max=%10llu "
|
|
"t_min=%10llu t_max=%10llu matched=%u "
|
|
"missed=%u\n", SC_DEV_NAME(sc),
|
|
pattern->name,
|
|
(unsigned long long)t1,
|
|
(unsigned long long)t0_max,
|
|
(unsigned long long)t_min,
|
|
(unsigned long long)t_max,
|
|
matched, missed);
|
|
break;
|
|
}
|
|
/* if we missed more than specified number of pulses,
|
|
* we stop searching */
|
|
if (partial_miss >
|
|
pattern->max_consecutive_missing) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %s matching stopped (too many "
|
|
"consecutive pulses missing). %d>%d "
|
|
"matched=%u. missed=%u.\n",
|
|
SC_DEV_NAME(sc), pattern->name,
|
|
partial_miss,
|
|
pattern->max_consecutive_missing,
|
|
matched, missed);
|
|
break;
|
|
}
|
|
|
|
new_period = (u_int64_t)
|
|
(last_tsf && last_tsf > pulse->rp_tsf) ?
|
|
last_tsf - pulse->rp_tsf : 0;
|
|
if (pulse->rp_tsf > t_max) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %-17s [%2d] %5s [**:**] tsf: "
|
|
"%10llu [range: %5llu-%5llu]. width: "
|
|
"%3d. period: %4llu. last_period: %4llu"
|
|
". mean_period: %4llu. last_tsf: %10llu"
|
|
".\n",
|
|
SC_DEV_NAME(sc),
|
|
pattern->name,
|
|
pulse->rp_index,
|
|
"noise",
|
|
(unsigned long long)pulse->rp_tsf,
|
|
(unsigned long long)t_min,
|
|
(unsigned long long)t_max,
|
|
(u_int8_t)pulse->rp_width,
|
|
(unsigned long long)new_period,
|
|
(unsigned long long)last_seen_period,
|
|
(unsigned long long)mean_period,
|
|
(unsigned long long)last_tsf);
|
|
/* this event is noise, ignore it */
|
|
pulse = pulse_prev(pulse);
|
|
noise++;
|
|
} else if (pulse->rp_tsf >= t_min) {
|
|
/* we found a match */
|
|
matched++;
|
|
if (partial_miss)
|
|
new_period /= (partial_miss + 1);
|
|
missed += partial_miss;
|
|
partial_miss = 0;
|
|
sum_periods += new_period;
|
|
mean_period = matched ?
|
|
(sum_periods / matched) :
|
|
0;
|
|
if (mean_period &&
|
|
pattern->dyn_ints &&
|
|
(mean_period >
|
|
pattern->max_rep_int ||
|
|
mean_period <
|
|
pattern->min_rep_int)) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %s mean period deviated "
|
|
"from original range [period: "
|
|
"%4u, range: %4u-%4u]\n",
|
|
SC_DEV_NAME(sc),
|
|
pattern->name,
|
|
mean_period,
|
|
pattern->min_rep_int,
|
|
pattern->max_rep_int);
|
|
break;
|
|
}
|
|
|
|
/* Remember we are scanning backwards... */
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %-17s [%2d] %5s [%2d:%-2d] tsf: "
|
|
"%10llu [range: %5llu-%5llu]. width: "
|
|
"%3d. period: %4llu. last_period: %4llu"
|
|
". mean_period: %4llu. last_tsf: %10llu"
|
|
".\n",
|
|
SC_DEV_NAME(sc),
|
|
pattern->name,
|
|
pulse->rp_index,
|
|
"match",
|
|
MAX(matched + missed + partial_miss - 1,
|
|
0),
|
|
(matched + missed + partial_miss),
|
|
(unsigned long long)pulse->rp_tsf,
|
|
(unsigned long long)t_min,
|
|
(unsigned long long)t_max,
|
|
(u_int8_t)pulse->rp_width,
|
|
(unsigned long long)new_period,
|
|
(unsigned long long)last_seen_period,
|
|
(unsigned long long)mean_period,
|
|
(unsigned long long)last_tsf);
|
|
|
|
/* record tsf and period */
|
|
last_seen_period = new_period;
|
|
last_tsf = pulse->rp_tsf;
|
|
|
|
/* advance to next pulse */
|
|
pulse = pulse_prev(pulse);
|
|
|
|
/* update bounds */
|
|
t_min = adjusted_max_rep_int < last_tsf ?
|
|
last_tsf - adjusted_max_rep_int :
|
|
0;
|
|
t_max = adjusted_min_rep_int < last_tsf ?
|
|
last_tsf - adjusted_min_rep_int :
|
|
0;
|
|
} else {
|
|
partial_miss++;
|
|
/* if we missed more than specified number of
|
|
* pulses, we stop searching */
|
|
if ((missed + partial_miss) >
|
|
pattern->max_missing) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %s matching stopped (too "
|
|
"many total pulses missing). "
|
|
"%d>%d matched=%u. missed=%u."
|
|
"\n",
|
|
SC_DEV_NAME(sc),
|
|
pattern->name,
|
|
missed,
|
|
pattern->max_missing,
|
|
matched,
|
|
missed);
|
|
break;
|
|
}
|
|
/* Default mean period to approximate center
|
|
* of range. Remember we are scanning
|
|
* backwards... */
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %-17s [**] %5s [%2d:%-2d] tsf: "
|
|
"(missing) [range: %5llu-%5llu]. "
|
|
"width: ***. period: ****. last_period:"
|
|
" %4llu. mean_period: %4llu. last_tsf: "
|
|
"%10llu.\n",
|
|
SC_DEV_NAME(sc),
|
|
pattern->name,
|
|
"missed",
|
|
MAX(matched + missed + partial_miss - 1,
|
|
0),
|
|
(matched + missed + partial_miss),
|
|
(unsigned long long)t_min,
|
|
(unsigned long long)t_max,
|
|
(unsigned long long)last_seen_period,
|
|
(unsigned long long)mean_period,
|
|
(unsigned long long)last_tsf);
|
|
|
|
/* update bounds */
|
|
t_min = adjusted_max_rep_int < t_min ?
|
|
t_min - adjusted_max_rep_int :
|
|
0;
|
|
t_max = adjusted_min_rep_int < t_max ?
|
|
t_max - adjusted_min_rep_int :
|
|
0;
|
|
}
|
|
}
|
|
|
|
/* print counters for this PRF */
|
|
if (matched > 1) {
|
|
int compare_result = CR_FALLTHROUGH;
|
|
int match_result = MR_MATCH;
|
|
/* we add one to the matched since we counted only the
|
|
* time differences */
|
|
/* matched++; not sure... */
|
|
|
|
/* check if PRF counters match a known radar, if we are
|
|
* confident enought */
|
|
if (MR_MATCH == (match_result = match_radar(
|
|
matched,
|
|
missed,
|
|
mean_period,
|
|
noise,
|
|
pattern->min_evts,
|
|
pattern->max_evts,
|
|
pattern->min_rep_int,
|
|
pattern->max_rep_int,
|
|
pattern->min_pulse,
|
|
pattern->max_missing))) {
|
|
compare_result = (NULL == best_pattern) ? CR_NULL :
|
|
compare_radar_matches(
|
|
matched,
|
|
missed,
|
|
mean_period,
|
|
noise,
|
|
pattern->min_evts,
|
|
pattern->max_evts,
|
|
pattern->min_rep_int,
|
|
pattern->max_rep_int,
|
|
pattern->min_pulse,
|
|
pattern->max_missing,
|
|
pattern->match_midpoint,
|
|
best_matched,
|
|
best_missed,
|
|
best_pri,
|
|
best_noise,
|
|
best_pattern->min_evts,
|
|
best_pattern->max_evts,
|
|
best_pattern->min_rep_int,
|
|
best_pattern->max_rep_int,
|
|
best_pattern->min_pulse,
|
|
best_pattern->max_missing,
|
|
best_pattern->match_midpoint);
|
|
}
|
|
if (DFLAG_ISSET(sc, ATH_DEBUG_DOTHFILT)) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: [%02d] %13s: %-17s [match=%2u {%2u"
|
|
"..%2u},missed=%2u/%2u,dur=%2d {%2u.."
|
|
"%2u},noise=%2u/%2u,cr:%d]\n",
|
|
SC_DEV_NAME(sc),
|
|
last_pulse->rp_index,
|
|
compare_result > CR_FALLTHROUGH ?
|
|
"NEW-BEST" :
|
|
get_match_result_desc(match_result),
|
|
pattern->name,
|
|
matched,
|
|
pattern->min_pulse,
|
|
pattern->max_evts,
|
|
missed,
|
|
pattern->max_missing,
|
|
matched + missed,
|
|
pattern->min_evts,
|
|
pattern->max_evts,
|
|
noise,
|
|
matched + noise,
|
|
compare_result);
|
|
}
|
|
|
|
if (compare_result > CR_FALLTHROUGH) {
|
|
best_matched = matched;
|
|
best_missed = missed;
|
|
best_index = i;
|
|
best_pattern = pattern;
|
|
best_pri = mean_period;
|
|
best_noise = noise;
|
|
best_cr = compare_result;
|
|
}
|
|
else if (compare_result <= CR_FALLTHROUGH) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %s match not better than best so "
|
|
"far. cr: %d matched: %d missed: "
|
|
"%d min_evts: %d\n",
|
|
SC_DEV_NAME(sc),
|
|
pattern->name,
|
|
compare_result,
|
|
matched,
|
|
missed,
|
|
pattern->min_evts);
|
|
}
|
|
}
|
|
}
|
|
if (-1 != best_index) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: [%02d] %10s: %-17s [match=%2u {%2u..%2u},missed="
|
|
"%2u/%2u,dur=%2d {%2u..%2u},noise=%2u/%2u,cr=%2d] "
|
|
"RI=%-9u RF=%-4u\n",
|
|
SC_DEV_NAME(sc),
|
|
last_pulse->rp_index,
|
|
"BEST/PULSE",
|
|
best_pattern->name,
|
|
best_matched,
|
|
best_pattern->min_pulse,
|
|
best_pattern->max_evts,
|
|
best_missed,
|
|
best_pattern->max_missing,
|
|
(best_matched + best_missed),
|
|
best_pattern->min_evts,
|
|
best_pattern->max_evts,
|
|
best_noise,
|
|
(best_matched + best_noise),
|
|
best_cr,
|
|
best_pri,
|
|
interval_to_frequency(best_pri));
|
|
if (index)
|
|
*index = best_index;
|
|
if (pri)
|
|
*pri = best_pri;
|
|
if (matching_pulses)
|
|
*matching_pulses = best_matched;
|
|
if (noise_pulses)
|
|
*noise_pulses = best_noise;
|
|
if (missed_pulses)
|
|
*missed_pulses = best_missed;
|
|
}
|
|
|
|
return (-1 != best_index) ? AH_TRUE : AH_FALSE;
|
|
}
|
|
|
|
#ifdef AR_DEBUG
|
|
static u_int32_t interval_to_frequency(u_int32_t interval)
|
|
{
|
|
/* Calculate BRI from PRI */
|
|
u_int32_t frequency = interval ? (1000000 / interval) : 0;
|
|
/* Round to nearest multiple of 50 */
|
|
return frequency + ((frequency % 50) >= 25 ? 50 : 0) - (frequency % 50);
|
|
}
|
|
#endif /* AR_DEBUG */
|
|
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
static const char *get_longpulse_desc(int lp)
|
|
{
|
|
switch (lp) {
|
|
case 8: return "FCC [5, 8 pulses]";
|
|
case 9: return "FCC [5, 9 pulses]";
|
|
case 10: return "FCC [5, 10 pulses]";
|
|
case 11: return "FCC [5, 11 pulses]";
|
|
case 12: return "FCC [5, 12 pulses]";
|
|
case 13: return "FCC [5, 13 pulses]";
|
|
case 14: return "FCC [5, 14 pulses]";
|
|
case 15: return "FCC [5, 15 pulses]";
|
|
case 16: return "FCC [5, 16 pulses]";
|
|
case 17: return "FCC [5, 17 pulses]";
|
|
case 18: return "FCC [5, 18 pulses]";
|
|
case 19: return "FCC [5, 19 pulses]";
|
|
case 20: return "FCC [5, 20 pulses]";
|
|
default: return "FCC [5, invalid pulses]";
|
|
}
|
|
}
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
|
|
static HAL_BOOL rp_analyze(struct ath_softc *sc)
|
|
{
|
|
HAL_BOOL radar = 0;
|
|
struct ath_rp *pulse;
|
|
|
|
/* Best short pulse match */
|
|
int32_t best_index = -1;
|
|
u_int32_t best_pri = 0;
|
|
u_int32_t best_matched = 0;
|
|
u_int32_t best_missed = 0;
|
|
u_int32_t best_noise = 0;
|
|
int32_t best_cr = 0;
|
|
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
/* Best long pulse match */
|
|
u_int32_t best_lp_bc = 0;
|
|
u_int32_t best_lp_matched = 0;
|
|
u_int32_t best_lp_missed = 0;
|
|
u_int32_t best_lp_noise = 0;
|
|
u_int32_t best_lp_pulses = 0;
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
u_int32_t pass = 0;
|
|
struct radar_pattern_specification *best_pattern = NULL;
|
|
|
|
/* start the analysis by the last pulse since it might speed up
|
|
* things and then move backward for all non-analyzed pulses.
|
|
* For debugging ONLY - we continue to run this scan after radar is
|
|
* detected, processing all pulses... even when they come in after an
|
|
* iteration of all pulses that were present when this function was
|
|
* invoked. This can happen at some radar waveforms where we will
|
|
* match the first few pulses and then the rest of the burst will come
|
|
* in, but never be analyzed.
|
|
*/
|
|
|
|
while (pulse_tail(sc)->rp_allocated &&
|
|
!pulse_tail(sc)->rp_analyzed &&
|
|
(AH_FALSE == radar ||
|
|
(DFLAG_ISSET(sc, ATH_DEBUG_DOTHFILT) && ++pass <= 3))) {
|
|
|
|
list_for_each_entry_reverse(pulse, &sc->sc_rp_list, list) {
|
|
if (!pulse->rp_allocated)
|
|
break;
|
|
if (pulse->rp_analyzed)
|
|
break;
|
|
|
|
/* Skip pulse analysis after we have confirmed radar
|
|
* presence unless we are debugging and have
|
|
* disabled short-circuit logic. In this case,
|
|
* we'll go through ALL the signatures and find
|
|
* the best match to convince ourselves this code works.
|
|
*/
|
|
if (AH_FALSE == radar ||
|
|
DFLAG_ISSET(sc, ATH_DEBUG_DOTHFILTNOSC)) {
|
|
/* short pulse match status */
|
|
u_int32_t index = 0;
|
|
u_int32_t pri = 0;
|
|
u_int32_t matched = 0;
|
|
u_int32_t missed = 0;
|
|
u_int32_t noise = 0;
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
|
|
/* long pulse match status */
|
|
u_int32_t lp_bc = 0;
|
|
u_int32_t lp_matched = 0;
|
|
u_int32_t lp_missed = 0;
|
|
u_int32_t lp_noise = 0;
|
|
u_int32_t lp_pulses = 0;
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
if (rp_analyze_short_pulse(sc, pulse, &index,
|
|
&pri, &matched, &missed,
|
|
&noise)) {
|
|
int compare_result = (!radar || best_index == -1) ?
|
|
CR_NULL :
|
|
compare_radar_matches(
|
|
matched,
|
|
missed,
|
|
pri,
|
|
noise,
|
|
radar_patterns[index].min_evts,
|
|
radar_patterns[index].max_evts,
|
|
radar_patterns[index].min_rep_int,
|
|
radar_patterns[index].max_rep_int,
|
|
radar_patterns[index].min_pulse,
|
|
radar_patterns[index].max_missing,
|
|
radar_patterns[index].match_midpoint,
|
|
best_matched,
|
|
best_missed,
|
|
best_pri,
|
|
best_noise,
|
|
radar_patterns[best_index].min_evts,
|
|
radar_patterns[best_index].max_evts,
|
|
radar_patterns[best_index].min_rep_int,
|
|
radar_patterns[best_index].max_rep_int,
|
|
radar_patterns[best_index].min_pulse,
|
|
radar_patterns[best_index].max_missing,
|
|
radar_patterns[best_index].match_midpoint
|
|
);
|
|
if (compare_result > CR_FALLTHROUGH) {
|
|
/* Update best match */
|
|
best_matched = matched;
|
|
best_missed = missed;
|
|
best_index = index;
|
|
best_pri = pri;
|
|
best_noise = noise;
|
|
radar = AH_TRUE;
|
|
best_cr = compare_result;
|
|
}
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: %10s: %-17s [match=%2u "
|
|
"{%2u..%2u}, missed=%2u/%2u, "
|
|
"dur=%2d {%2u..%2u}, "
|
|
"noise=%2u/%2u, cr=%2d] "
|
|
"RI=%-9u RF=%-4u\n",
|
|
DEV_NAME(sc->sc_dev),
|
|
(compare_result > CR_FALLTHROUGH) ?
|
|
"BETTER" : "WORSE",
|
|
radar_patterns[index].name,
|
|
matched,
|
|
radar_patterns[index].min_pulse,
|
|
radar_patterns[index].max_evts,
|
|
missed,
|
|
radar_patterns[index].max_missing,
|
|
(matched + missed),
|
|
radar_patterns[index].min_evts,
|
|
radar_patterns[index].max_evts,
|
|
noise,
|
|
(matched + noise),
|
|
compare_result,
|
|
pri,
|
|
interval_to_frequency(pri));
|
|
}
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
if (rp_analyze_long_pulse(sc, pulse,
|
|
&lp_bc,
|
|
&lp_matched,
|
|
&lp_missed,
|
|
&lp_noise,
|
|
&lp_pulses)) {
|
|
/* XXX: Do we care about best match?? */
|
|
radar = AH_TRUE;
|
|
best_lp_bc = lp_bc;
|
|
best_lp_matched = lp_matched;
|
|
best_lp_missed = lp_missed;
|
|
best_lp_noise = lp_noise;
|
|
best_lp_pulses = lp_pulses;
|
|
}
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
}
|
|
pulse->rp_analyzed = 1;
|
|
}
|
|
}
|
|
if (AH_TRUE == radar) {
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
if (!best_lp_bc) {
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
best_pattern =
|
|
&radar_patterns[best_index];
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: %10s: %-17s [match=%2u {%2u..%2u},missed="
|
|
"%2u/%2u,dur=%2d {%2u..%2u},noise=%2u/%2u,cr=%2d] "
|
|
"RI=%-9u RF=%-4u\n",
|
|
DEV_NAME(sc->sc_dev),
|
|
"BEST MATCH",
|
|
best_pattern->name,
|
|
best_matched,
|
|
best_pattern->min_pulse,
|
|
best_pattern->max_evts,
|
|
best_missed,
|
|
best_pattern->max_missing,
|
|
(best_matched + best_missed),
|
|
best_pattern->min_evts,
|
|
best_pattern->max_evts,
|
|
best_noise,
|
|
(best_matched + best_noise),
|
|
best_cr,
|
|
best_pri,
|
|
interval_to_frequency(best_pri));
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
}
|
|
else {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: %10s: %-17s [match=%2u {%2u..%2u},missed="
|
|
"%2u/%2u,noise=%2u/%2u]\n",
|
|
DEV_NAME(sc->sc_dev),
|
|
"BEST MATCH",
|
|
get_longpulse_desc(best_lp_bc),
|
|
best_lp_bc,
|
|
(best_lp_bc-4),
|
|
best_lp_bc,
|
|
best_lp_missed,
|
|
(best_lp_bc-(best_lp_bc-4)),
|
|
best_lp_noise,
|
|
(best_lp_pulses + best_lp_noise)
|
|
);
|
|
}
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
if (DFLAG_ISSET(sc, ATH_DEBUG_DOTHFILT)) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: ========================================\n",
|
|
DEV_NAME(sc->sc_dev));
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: ==BEGIN RADAR SAMPLE====================\n",
|
|
DEV_NAME(sc->sc_dev));
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: ========================================\n",
|
|
DEV_NAME(sc->sc_dev));
|
|
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
if (!best_lp_bc) {
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
best_pattern =
|
|
&radar_patterns[best_index];
|
|
DPRINTF(sc, ATH_DEBUG_DOTHPULSES,
|
|
"%s: Sample contains data matching %s "
|
|
"[match=%2u {%2u..%2u}, "
|
|
"missed=%2u/%2u, dur=%2d {%2u..%2u}, "
|
|
"noise=%2u/%2u,cr=%d] RI=%-9u RF=%-4u\n",
|
|
DEV_NAME(sc->sc_dev),
|
|
best_pattern->name,
|
|
best_matched,
|
|
best_pattern->min_pulse,
|
|
best_pattern->max_evts,
|
|
best_missed,
|
|
best_pattern->max_missing,
|
|
best_matched + best_missed,
|
|
best_pattern->min_evts,
|
|
best_pattern->max_evts,
|
|
best_noise,
|
|
best_noise + best_matched,
|
|
best_cr,
|
|
best_pri,
|
|
interval_to_frequency(best_pri));
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
} else {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHPULSES,
|
|
"%s: Sample contains data matching %s\n",
|
|
DEV_NAME(sc->sc_dev),
|
|
get_longpulse_desc(best_lp_bc));
|
|
}
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
|
|
ath_rp_print(sc, 0 /* analyzed pulses only */ );
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: ========================================\n",
|
|
DEV_NAME(sc->sc_dev));
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: ==END RADAR SAMPLE======================\n",
|
|
DEV_NAME(sc->sc_dev));
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: ========================================\n",
|
|
DEV_NAME(sc->sc_dev));
|
|
}
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
if (!best_lp_bc)
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
ath_radar_detected(sc, radar_patterns[best_index].name);
|
|
#ifdef ATH_RADAR_LONG_PULSE
|
|
else
|
|
ath_radar_detected(sc, get_longpulse_desc(best_lp_bc));
|
|
#endif /* #ifdef ATH_RADAR_LONG_PULSE */
|
|
}
|
|
return radar;
|
|
}
|
|
|
|
/* initialize ath_softc members so sensible values */
|
|
static void ath_rp_clear(struct ath_softc *sc)
|
|
{
|
|
sc->sc_rp = NULL;
|
|
INIT_LIST_HEAD(&sc->sc_rp_list);
|
|
sc->sc_rp_num = 0;
|
|
sc->sc_rp_analyze = NULL;
|
|
}
|
|
|
|
static void ath_rp_tasklet(TQUEUE_ARG data)
|
|
{
|
|
struct net_device *dev = (struct net_device *)data;
|
|
struct ath_softc *sc = netdev_priv(dev);
|
|
|
|
if (sc->sc_rp_analyze != NULL)
|
|
sc->sc_rp_analyze(sc);
|
|
}
|
|
|
|
void ath_rp_init(struct ath_softc *sc)
|
|
{
|
|
struct net_device *dev = sc->sc_dev;
|
|
int i;
|
|
|
|
ath_rp_clear(sc);
|
|
|
|
sc->sc_rp = (struct ath_rp *)kzalloc(
|
|
sizeof(struct ath_rp) *
|
|
ATH_RADAR_PULSE_NR, GFP_KERNEL);
|
|
|
|
if (sc->sc_rp == NULL)
|
|
return;
|
|
|
|
/* initialize the circular list */
|
|
INIT_LIST_HEAD(&sc->sc_rp_list);
|
|
for (i = 0; i < ATH_RADAR_PULSE_NR; i++) {
|
|
sc->sc_rp[i].rp_index = i;
|
|
list_add_tail(&sc->sc_rp[i].list,
|
|
&sc->sc_rp_list);
|
|
}
|
|
|
|
sc->sc_rp_num = 0;
|
|
sc->sc_rp_analyze = rp_analyze;
|
|
|
|
/* compute sc_rp_min */
|
|
sc->sc_rp_min = 2;
|
|
for (i = 0; i < sizetab(radar_patterns); i++)
|
|
sc->sc_rp_min =
|
|
MIN(sc->sc_rp_min,
|
|
radar_patterns[i].min_pulse);
|
|
|
|
/* default values is properly handle pulses and detected radars */
|
|
sc->sc_rp_ignored = 0;
|
|
sc->sc_radar_ignored = 0;
|
|
|
|
ATH_INIT_TQUEUE(&sc->sc_rp_tq, ath_rp_tasklet, dev);
|
|
}
|
|
|
|
void ath_rp_done(struct ath_softc *sc)
|
|
{
|
|
/* free what we allocated in ath_rp_init() */
|
|
kfree(sc->sc_rp);
|
|
|
|
ath_rp_clear(sc);
|
|
}
|
|
|
|
void ath_rp_record(struct ath_softc *sc, u_int64_t tsf, u_int8_t rssi,
|
|
u_int8_t width, HAL_BOOL is_simulated)
|
|
{
|
|
struct ath_rp *pulse;
|
|
|
|
DPRINTF(sc, ATH_DEBUG_DOTHPULSES, "%s: ath_rp_record: "
|
|
"tsf=%10llu rssi=%3u width=%3u%s\n",
|
|
SC_DEV_NAME(sc), (unsigned long long)tsf, rssi, width,
|
|
sc->sc_rp_ignored ? " (ignored)" : "");
|
|
|
|
if (sc->sc_rp_ignored) {
|
|
return;
|
|
}
|
|
|
|
/* pulses width 255 seems to trigger false detection of radar. we
|
|
* ignored it then. */
|
|
|
|
if (width == 255) {
|
|
/* ignored */
|
|
return ;
|
|
}
|
|
|
|
/* check if the new radar pulse is after the last one recorded, or
|
|
* else, we flush the history */
|
|
pulse = pulse_tail(sc);
|
|
if (tsf < pulse->rp_tsf) {
|
|
if (is_simulated == AH_TRUE && 0 == tsf) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %s: ath_rp_flush: simulated tsf "
|
|
"reset. tsf =%10llu, rptsf =%10llu\n",
|
|
SC_DEV_NAME(sc), __func__,
|
|
(unsigned long long)tsf,
|
|
(unsigned long long)pulse->rp_tsf);
|
|
ath_rp_flush(sc);
|
|
} else if ((pulse->rp_tsf - tsf) > (1 << 15)) {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILTVBSE,
|
|
"%s: %s: ath_rp_flush: tsf reset. "
|
|
"(rp_tsf - tsf > 0x8000) tsf=%10llu, rptsf="
|
|
"%10llu\n",
|
|
SC_DEV_NAME(sc), __func__,
|
|
(unsigned long long)tsf,
|
|
(unsigned long long)pulse->rp_tsf);
|
|
ath_rp_flush(sc);
|
|
} else {
|
|
DPRINTF(sc, ATH_DEBUG_DOTHFILT,
|
|
"%s: %s: tsf jitter/bug detected: tsf =%10llu, "
|
|
"rptsf =%10llu, rp_tsf - tsf = %10llu\n",
|
|
SC_DEV_NAME(sc), __func__,
|
|
(unsigned long long)tsf,
|
|
(unsigned long long)pulse->rp_tsf,
|
|
(unsigned long long)(pulse->rp_tsf - tsf));
|
|
}
|
|
}
|
|
|
|
/* remove the head of the list */
|
|
pulse = pulse_head(sc);
|
|
list_del(&pulse->list);
|
|
|
|
pulse->rp_tsf = tsf;
|
|
pulse->rp_rssi = rssi;
|
|
pulse->rp_width = width;
|
|
pulse->rp_allocated = 1;
|
|
pulse->rp_analyzed = 0;
|
|
|
|
/* add at the tail of the list */
|
|
list_add_tail(&pulse->list, &sc->sc_rp_list);
|
|
if (ATH_RADAR_PULSE_NR > sc->sc_rp_num)
|
|
sc->sc_rp_num++;
|
|
}
|
|
|
|
void ath_rp_print_mem(struct ath_softc *sc, int analyzed_pulses_only)
|
|
{
|
|
struct ath_rp *pulse;
|
|
u_int64_t oldest_tsf = ~0;
|
|
int i;
|
|
IPRINTF(sc, "Pulse dump of %spulses using sc_rp containing "
|
|
"%d allocated pulses.\n",
|
|
analyzed_pulses_only ? "analyzed " : "", sc->sc_rp_num);
|
|
|
|
/* Find oldest TSF value so we can print relative times */
|
|
for (i = 0; i < ATH_RADAR_PULSE_NR; i++) {
|
|
pulse = &sc->sc_rp[i];
|
|
if (pulse->rp_allocated && pulse->rp_tsf < oldest_tsf)
|
|
oldest_tsf = pulse->rp_tsf;
|
|
}
|
|
|
|
for (i = 0; i < ATH_RADAR_PULSE_NR; i++) {
|
|
pulse = &sc->sc_rp[i];
|
|
if (!pulse->rp_allocated)
|
|
break;
|
|
if ((!analyzed_pulses_only) || pulse->rp_analyzed)
|
|
IPRINTF(sc, "Pulse [%3d, %p] : relative_tsf=%10llu "
|
|
"tsf=%10llu rssi=%3u width=%3u allocated=%d "
|
|
"analyzed=%d next=%p prev=%p\n",
|
|
pulse->rp_index,
|
|
pulse,
|
|
(unsigned long long)(pulse->rp_tsf - oldest_tsf),
|
|
(unsigned long long)pulse->rp_tsf,
|
|
pulse->rp_rssi,
|
|
pulse->rp_width,
|
|
pulse->rp_allocated,
|
|
pulse->rp_analyzed,
|
|
pulse->list.next,
|
|
pulse->list.prev);
|
|
}
|
|
}
|
|
|
|
void ath_rp_print(struct ath_softc *sc, int analyzed_pulses_only)
|
|
{
|
|
struct ath_rp *pulse;
|
|
u_int64_t oldest_tsf = ~0;
|
|
|
|
IPRINTF(sc, "Pulse dump of %spulses from ring buffer containing %d "
|
|
"pulses.\n",
|
|
analyzed_pulses_only ? "analyzed " : "",
|
|
sc->sc_rp_num);
|
|
|
|
/* Find oldest TSF value so we can print relative times */
|
|
oldest_tsf = ~0;
|
|
list_for_each_entry_reverse(pulse, &sc->sc_rp_list, list)
|
|
if (pulse->rp_allocated && pulse->rp_tsf < oldest_tsf)
|
|
oldest_tsf = pulse->rp_tsf;
|
|
|
|
list_for_each_entry_reverse(pulse, &sc->sc_rp_list, list) {
|
|
if (!pulse->rp_allocated)
|
|
continue;
|
|
if ((!analyzed_pulses_only) || pulse->rp_analyzed)
|
|
IPRINTF(sc, "Pulse [%3d, %p] : relative_tsf=%10llu "
|
|
"tsf=%10llu rssi=%3u width=%3u allocated=%d "
|
|
"analyzed=%d next=%p prev=%p\n",
|
|
pulse->rp_index,
|
|
pulse,
|
|
(unsigned long long)(pulse->rp_tsf - oldest_tsf),
|
|
(unsigned long long)pulse->rp_tsf,
|
|
pulse->rp_rssi,
|
|
pulse->rp_width,
|
|
pulse->rp_allocated,
|
|
pulse->rp_analyzed,
|
|
pulse->list.next,
|
|
pulse->list.prev);
|
|
}
|
|
}
|
|
|
|
void ath_rp_flush(struct ath_softc *sc)
|
|
{
|
|
struct ath_rp *pulse;
|
|
list_for_each_entry_reverse(pulse, &sc->sc_rp_list, list)
|
|
pulse->rp_allocated = 0;
|
|
sc->sc_rp_num = 0;
|
|
}
|