blob: ac18b14a6390eb6be272845d06a4f9007bd34331 [file] [log] [blame]
/*
* Copyright (c) 2002-2014,2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*===========================================================================
dfs_process_phyerr.c
OVERVIEW:
Source code borrowed from QCA_MAIN DFS module
DEPENDENCIES:
Are listed for each API below.
===========================================================================*/
/*===========================================================================
EDIT HISTORY FOR FILE
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
when who what, where, why
---------- --- --------------------------------------------------------
===========================================================================*/
#include "dfs.h"
#include "dfs_phyerr.h" /* For chip-specific phyerr function declarations */
/* TO DO DFS
#include <ieee80211_var.h>
*/
#ifndef UNINET
/* TO DO DFS
#include <ieee80211_channel.h>
*/
#endif
#ifdef ATH_SUPPORT_DFS
/*
* Return the frequency width for the current operating channel.
*
* This isn't the channel width - it's how wide the reported event
* may be. For HT20 this is 20MHz. For HT40 on Howl and later it'll
* still be 20MHz - the hardware returns either pri or ext channel.
*/
static inline int
dfs_get_event_freqwidth(struct ath_dfs *dfs)
{
/* Handle edge cases during startup/transition, shouldn't happen! */
if (dfs == NULL)
return (0);
if (dfs->ic == NULL || dfs->ic->ic_curchan == NULL)
return (0);
/*
* XXX For now, assume 20MHz wide - but this is incorrect when
* operating in half/quarter mode!
*/
return (20);
}
/*
* Return the centre frequency for the current operating channel and
* event.
*
* This is for post-Owl 11n chips which report pri/extension channel
* events.
*/
static inline uint16_t
dfs_get_event_freqcentre(struct ath_dfs *dfs, int is_pri, int is_ext, int is_dc)
{
struct ieee80211com *ic;
int chan_offset = 0, chan_width;
uint16_t freq;
/* Handle edge cases during startup/transition, shouldn't happen! */
if (dfs == NULL)
return (0);
if (dfs->ic == NULL || dfs->ic->ic_curchan == NULL)
return (0);
ic = dfs->ic;
/*
*
* For wide channels, DC and ext frequencies need a bit of hand-holding
* based on whether it's an upper or lower channel.
*/
chan_width = dfs_get_event_freqwidth(dfs);
adf_os_spin_lock_bh(&ic->chan_lock);
if (IEEE80211_IS_CHAN_11N_HT40PLUS(ic->ic_curchan))
chan_offset = chan_width;
else if (IEEE80211_IS_CHAN_11N_HT40MINUS(ic->ic_curchan))
chan_offset = -chan_width;
else
chan_offset = 0;
adf_os_spin_unlock_bh(&ic->chan_lock);
/*
* 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?
*/
adf_os_spin_lock_bh(&ic->chan_lock);
freq = (ieee80211_chan2freq(ic, ic->ic_curchan) +
(chan_offset / 2));
adf_os_spin_unlock_bh(&ic->chan_lock);
return freq;
}
/*
* For non-wide channels, the centre frequency is just ic_freq.
* The centre frequency for pri events is still ic_freq.
*/
if (is_pri) {
adf_os_spin_lock_bh(&ic->chan_lock);
freq = (ieee80211_chan2freq(ic, ic->ic_curchan));
adf_os_spin_unlock_bh(&ic->chan_lock);
return freq;
}
if (is_ext) {
adf_os_spin_lock_bh(&ic->chan_lock);
freq = (ieee80211_chan2freq(ic, ic->ic_curchan) + chan_width);
adf_os_spin_unlock_bh(&ic->chan_lock);
return freq;
}
/* XXX shouldn't get here */
adf_os_spin_lock_bh(&ic->chan_lock);
freq = (ieee80211_chan2freq(ic, ic->ic_curchan));
adf_os_spin_unlock_bh(&ic->chan_lock);
return freq;
}
/*
* Process an Owl-style phy error.
*
* Return 1 on success or 0 on failure.
*/
int
dfs_process_phyerr_owl(struct ath_dfs *dfs, void *buf, u_int16_t datalen,
u_int8_t rssi, u_int8_t ext_rssi, u_int32_t rs_tstamp, u_int64_t fulltsf,
struct dfs_phy_err *e)
{
const char *cbuf = (const char *) buf;
u_int8_t dur;
int event_width;
/* XXX this shouldn't be kept count here */
dfs->ath_dfs_stats.owl_phy_errors++;
/*
* HW cannot detect extension channel radar so it only passes us
* primary channel radar data
*/
if (datalen == 0)
dur = 0;
else
dur = ((u_int8_t *) cbuf)[0];
/*
* This is a spurious event; toss.
*/
if (rssi == 0 && dur == 0) {
dfs->ath_dfs_stats.datalen_discards++;
return (0);
}
/*
* Fill out dfs_phy_err with the information we have
* at hand.
*/
OS_MEMSET(e, 0, sizeof(*e));
e->rssi = rssi;
e->dur = dur;
e->is_pri = 1;
e->is_ext = 0;
e->is_dc = 0;
e->is_early = 1;
e->fulltsf = fulltsf;
e->rs_tstamp = rs_tstamp;
/*
* Owl only ever reports events on the primary channel;
* it doesn't even see events on the secondary channel.
*/
event_width = dfs_get_event_freqwidth(dfs);
e->freq = dfs_get_event_freqcentre(dfs, 1, 0, 0) * 1000;
e->freq_lo = e->freq - (event_width / 2) * 1000;
e->freq_hi = e->freq + (event_width / 2) * 1000;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR_SUM,
"%s: rssi=%u dur=%u, freq=%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);
}
/*
* Process a Sowl/Howl style phy error.
*/
int
dfs_process_phyerr_sowl(struct ath_dfs *dfs, void *buf, u_int16_t datalen,
u_int8_t rssi, u_int8_t ext_rssi, u_int32_t rs_tstamp, u_int64_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;
u_int8_t dur = 0;
u_int8_t pulse_bw_info, pulse_length_ext, pulse_length_pri;
int pri_found = 0, ext_found = 0;
int early_ext = 0;
int event_width;
/*
* If radar can be detected on the extension channel, datalen zero
* pulses are bogus, discard them.
*/
if (!datalen) {
dfs->ath_dfs_stats.datalen_discards++;
return (0);
}
/* Ensure that we have at least three bytes of payload */
if (datalen < 3) {
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
"%s: short error frame (%d bytes)\n",
__func__, datalen);
dfs->ath_dfs_stats.datalen_discards++;
return (0);
}
/*
* Fetch the payload directly - the compiler will happily generate
* byte-read instructions with a const char * cbuf pointer.
*/
pulse_length_pri = cbuf[datalen - 3];
pulse_length_ext = cbuf[datalen - 2];
pulse_bw_info = cbuf[datalen - 1];
/*
* Only the last 3 bits of the BW info are relevant, they indicate
* which channel the radar was detected in.
*/
pulse_bw_info &= 0x07;
/*
* If pulse on DC, both primary and extension flags will be set
*/
if (((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
(pulse_bw_info & PRI_CH_RADAR_FOUND))) {
/*
* Conducted testing, when pulse is on DC, both
* pri and ext durations are reported to be same.
*
* Radiated testing, when pulse is on DC, different
* pri and ext durations are reported, so take the
* larger of the two
*/
if (pulse_length_ext >= pulse_length_pri) {
dur = pulse_length_ext;
ext_found = 1;
} else {
dur = pulse_length_pri;
pri_found = 1;
}
dfs->ath_dfs_stats.dc_phy_errors++;
} else {
if (pulse_bw_info & EXT_CH_RADAR_FOUND) {
dur = pulse_length_ext;
pri_found = 0;
ext_found = 1;
dfs->ath_dfs_stats.ext_phy_errors++;
}
if (pulse_bw_info & PRI_CH_RADAR_FOUND) {
dur = pulse_length_pri;
pri_found = 1;
ext_found = 0;
dfs->ath_dfs_stats.pri_phy_errors++;
}
if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) {
dur = pulse_length_ext;
pri_found = 0;
ext_found = 1;
early_ext = 1;
dfs->ath_dfs_stats.early_ext_phy_errors++;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"EARLY ext channel dur=%u rssi=%u datalen=%d\n",
dur, rssi, datalen);
}
if (! pulse_bw_info) {
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"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->ath_dfs_stats.bwinfo_errors++;
return (0);
}
}
/*
* Always use combined RSSI reported, unless RSSI reported on
* extension is stronger
*/
if ((ext_rssi > rssi) && (ext_rssi < 128)) {
rssi = ext_rssi;
}
/*
* Fill out the rssi/duration fields from above.
*/
OS_MEMSET(e, 0, sizeof(*e));
e->rssi = rssi;
e->dur = dur;
e->is_pri = pri_found;
e->is_ext = ext_found;
e->is_dc = !! (((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
(pulse_bw_info & PRI_CH_RADAR_FOUND)));
e->is_early = early_ext;
e->fulltsf = fulltsf;
e->rs_tstamp = rs_tstamp;
/*
* Sowl and later can report pri/ext events.
*/
event_width = dfs_get_event_freqwidth(dfs);
e->freq =
dfs_get_event_freqcentre(dfs, e->is_pri, e->is_ext, e->is_dc)
* 1000;
e->freq_lo = e->freq - (event_width / 2) * 1000;
e->freq_hi = e->freq + (event_width / 2) * 1000;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR_SUM,
"%s: pulse_bw_info=0x%x pulse_length_ext=%u pulse_length_pri=%u "
"rssi=%u ext_rssi=%u, freq=%d MHz, freq_lo=%d MHz, "
"freq_hi=%d MHz\n",
__func__,
pulse_bw_info,
pulse_length_ext,
pulse_length_pri,
rssi,
ext_rssi,
e->freq / 1000,
e->freq_lo / 1000,
e->freq_hi / 1000);
return (1);
}
/*
* Process a Merlin/Osprey style phy error.
*/
int
dfs_process_phyerr_merlin(struct ath_dfs *dfs, void *buf,
u_int16_t datalen, u_int8_t rssi, u_int8_t ext_rssi, u_int32_t rs_tstamp,
u_int64_t fulltsf, struct dfs_phy_err *e)
{
const char *cbuf = (const char *) buf;
u_int8_t pulse_bw_info = 0;
/*
* Process using the sowl code
*/
if (! dfs_process_phyerr_sowl(dfs, buf, datalen, rssi, ext_rssi,
rs_tstamp, fulltsf, e)) {
return (0);
}
/*
* For osprey (and Merlin) bw_info has implication for selecting
* RSSI value. So re-fetch the bw_info field so the RSSI values
* can be appropriately overridden.
*/
pulse_bw_info = cbuf[datalen - 1];
switch (pulse_bw_info & 0x03) {
case 0x00:
/* No radar in ctrl or ext channel */
rssi = 0;
break;
case 0x01:
/* radar in ctrl channel */
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"RAW RSSI: rssi=%u ext_rssi=%u\n",
rssi, ext_rssi);
if (ext_rssi >= (rssi + 3)) {
/*
* cannot use ctrl channel RSSI if
* extension channel is stronger
*/
rssi = 0;
}
break;
case 0x02:
/* radar in extension channel */
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"RAW RSSI: rssi=%u ext_rssi=%u\n",
rssi, ext_rssi);
if (rssi >= (ext_rssi + 12)) {
/*
* cannot use extension channel RSSI if control channel
* is stronger
*/
rssi = 0;
} else {
rssi = ext_rssi;
}
break;
case 0x03:
/* when both are present use stronger one */
if (rssi < ext_rssi) {
rssi = ext_rssi;
}
break;
}
/*
* Override the rssi decision made by the sowl code.
* The rest of the fields (duration, timestamp, etc)
* are left untouched.
*/
e->rssi = rssi;
return(1);
}
static void
dump_phyerr_contents(const char *d, int len)
{
#ifdef CONFIG_ENABLE_DUMP_PHYERR_CONTENTS
int i, n, bufsize = 64;
/*
* This is statically sized for a 4-digit address + 16 * 2 digit
* data string.
*
* It's done so the printk() passed to the kernel is an entire
* line, so the kernel logging code will atomically print it.
* Otherwise we'll end up with interleaved lines with output
* from other kernel threads.
*/
char buf[64];
/* Initial conditions */
buf[0] = '\n';
n = 0;
for (i = 0; i < len; i++) {
if (i % 16 == 0) {
n += snprintf(buf + n, bufsize - n,
"%04x: ", i);
}
n += snprintf(buf + n, bufsize - n, "%02x ", d[i] & 0xff);
if (i % 16 == 15) {
n = 0;
buf[0] = '\0';
}
}
/*
* Print the final line if we didn't print it above.
*/
if (n != 0)
VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO, "%s: %s\n", __func__, buf);
#endif /* def CONFIG_ENABLE_DUMP_PHYERR_CONTENTS */
}
void
dfs_process_phyerr(struct ieee80211com *ic, void *buf, u_int16_t datalen,
u_int8_t r_rssi, u_int8_t r_ext_rssi, u_int32_t r_rs_tstamp,
u_int64_t r_fulltsf, bool enable_log)
{
struct ath_dfs *dfs = (struct ath_dfs *)ic->ic_dfs;
struct ieee80211_channel *chan=ic->ic_curchan;
struct dfs_event *event;
struct dfs_phy_err e;
int empty;
if (dfs == NULL) {
VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
"%s: sc_dfs is NULL\n", __func__);
return;
}
dfs->dfs_phyerr_count++;
dump_phyerr_contents(buf, datalen);
/*
* XXX The combined_rssi_ok support has been removed.
* This was only clear for Owl.
*
* XXX TODO: re-add this; it requires passing in the ctl/ext
* RSSI set from the RX status descriptor.
*
* XXX TODO TODO: this may be done for us from the legacy
* phy error path in ath_dev; please review that code.
*/
/*
* At this time we have a radar pulse that we need to examine and
* queue. But if dfs_process_radarevent already detected radar and set
* CHANNEL_INTERFERENCE flag then do not queue any more radar data.
* When we are in a new channel this flag will be clear and we will
* start queueing data for new channel. (EV74162)
*/
if (dfs->dfs_debug_mask & ATH_DEBUG_DFS_PHYERR_PKT)
dump_phyerr_contents(buf, datalen);
if (chan == NULL) {
VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
"%s: chan is NULL\n", __func__);
return;
}
adf_os_spin_lock_bh(&ic->chan_lock);
if (IEEE80211_IS_CHAN_RADAR(chan)) {
adf_os_spin_unlock_bh(&ic->chan_lock);
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
"%s: Radar already found in the channel, "
" do not queue radar data\n",
__func__);
return;
}
adf_os_spin_unlock_bh(&ic->chan_lock);
dfs->ath_dfs_stats.total_phy_errors++;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"%s[%d] phyerr %d len %d\n",
__func__, __LINE__,
dfs->ath_dfs_stats.total_phy_errors, datalen);
/*
* hardware stores this as 8 bit signed value.
* we will cap it at 0 if it is a negative number
*/
if (r_rssi & 0x80)
r_rssi = 0;
if (r_ext_rssi & 0x80)
r_ext_rssi = 0;
OS_MEMSET(&e, 0, sizeof(e));
/*
* This is a bit evil - instead of just passing in
* the chip version, the existing code uses a set
* of HAL capability bits to determine what is
* possible.
*
* The way I'm decoding it is thus:
*
* + DFS enhancement? Merlin or later
* + DFS extension channel? Sowl or later. (Howl?)
* + otherwise, Owl (and legacy.)
*/
if (dfs->dfs_caps.ath_chip_is_bb_tlv) {
if (dfs_process_phyerr_bb_tlv(dfs, buf, datalen, r_rssi, r_ext_rssi,
r_rs_tstamp, r_fulltsf,
&e, enable_log) == 0) {
dfs->dfs_phyerr_reject_count++;
return;
} else {
if (dfs->dfs_phyerr_freq_min > e.freq)
dfs->dfs_phyerr_freq_min = e. freq;
if (dfs->dfs_phyerr_freq_max < e.freq)
dfs->dfs_phyerr_freq_max = e. freq;
}
} else if (dfs->dfs_caps.ath_dfs_use_enhancement) {
if (dfs_process_phyerr_merlin(dfs, buf, datalen, r_rssi,
r_ext_rssi, r_rs_tstamp, r_fulltsf, &e) == 0){
return;
}
} else if (dfs->dfs_caps.ath_dfs_ext_chan_ok) {
if (dfs_process_phyerr_sowl(dfs, buf, datalen, r_rssi,
r_ext_rssi, r_rs_tstamp, r_fulltsf, &e) == 0){
return;
}
} else {
if (dfs_process_phyerr_owl(dfs, buf, datalen, r_rssi,
r_ext_rssi, r_rs_tstamp, r_fulltsf, &e) == 0){
return;
}
}
VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO,
"\n %s: Frequency at which the phyerror was injected = %d",
__func__, e.freq);
/*
* If the hardware supports radar reporting on the extension channel
* it will supply FFT data for longer radar pulses.
*
* TLV chips don't go through this software check - the hardware
* check should be enough. If we want to do software checking
* later on then someone will have to craft an FFT parser
* suitable for the TLV FFT data format.
*/
if ((! dfs->dfs_caps.ath_chip_is_bb_tlv) &&
dfs->dfs_caps.ath_dfs_ext_chan_ok) {
/*
* HW has a known issue with chirping pulses injected at or
* around DC in 40MHz mode. Such pulses are reported with
* much lower durations and SW then discards them because
* they do not fit the minimum bin5 pulse duration.
*
* To work around this issue, if a pulse is within a 10us
* range of the bin5 min duration, check if the pulse is
* chirping. If the pulse is chirping, bump up the duration
* to the minimum bin5 duration.
*
* This makes sure that a valid chirping pulse will not be
* discarded because of incorrect low duration.
*
* TBD - Is it possible to calculate the 'real' duration of
* the pulse using the slope of the FFT data?
*
* TBD - Use FFT data to differentiate between radar pulses
* and false PHY errors.
* This will let us reduce the number of false alarms seen.
*
* BIN 5 chirping pulses are only for FCC or Japan MMK4 domain
*/
if (((dfs->dfsdomain == DFS_FCC_DOMAIN) ||
(dfs->dfsdomain == DFS_MKK4_DOMAIN)) &&
(e.dur >= MAYBE_BIN5_DUR) && (e.dur < MAX_BIN5_DUR)) {
int add_dur;
int slope = 0, dc_found = 0;
/*
* Set the event chirping flags; as we're doing
* an actual chirp check.
*/
e.do_check_chirp = 1;
e.is_hw_chirp = 0;
e.is_sw_chirp = 0;
/*
* dfs_check_chirping() expects is_pri and is_ext
* to be '1' for true and '0' for false for now,
* as the function itself uses these values in
* constructing things rather than testing them
* for 'true' or 'false'.
*/
add_dur = dfs_check_chirping(dfs, buf, datalen,
(e.is_pri ? 1 : 0),
(e.is_ext ? 1 : 0),
&slope, &dc_found);
if (add_dur) {
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"old dur %d slope =%d\n", e.dur, slope);
e.is_sw_chirp = 1;
// bump up to a random bin5 pulse duration
if (e.dur < MIN_BIN5_DUR) {
e.dur = dfs_get_random_bin5_dur(dfs,
e.fulltsf);
}
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"new dur %d\n", e.dur);
} else {
/* set the duration so that it is rejected */
e.is_sw_chirp = 0;
e.dur = MAX_BIN5_DUR + 100;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"is_chirping = %d dur=%d \n",
add_dur, e.dur);
}
} else {
/*
* We have a pulse that is either bigger than
* MAX_BIN5_DUR or * less than MAYBE_BIN5_DUR
*/
if ((dfs->dfsdomain == DFS_FCC_DOMAIN) ||
(dfs->dfsdomain == DFS_MKK4_DOMAIN)) {
/*
* XXX Would this result in very large pulses
* wrapping around to become short pulses?
*/
if (e.dur >= MAX_BIN5_DUR) {
/*
* set the duration so that it is
* rejected
*/
e.dur = MAX_BIN5_DUR + 50;
}
}
}
}
/*
* Add the parsed, checked and filtered entry to the radar pulse
* event list. This is then checked by dfs_radar_processevent().
*
* XXX TODO: some filtering is still done below this point - fix
* XXX this!
*/
ATH_DFSEVENTQ_LOCK(dfs);
empty = STAILQ_EMPTY(&(dfs->dfs_eventq));
if (empty) {
ATH_DFSEVENTQ_UNLOCK(dfs);
return;
}
ATH_DFSEVENTQ_UNLOCK(dfs);
/*
* If the channel is a turbo G channel, then the event is
* for the adaptive radio (AR) pattern matching rather than
* radar detection.
*/
adf_os_spin_lock_bh(&ic->chan_lock);
if ((chan->ic_flags & CHANNEL_108G) == CHANNEL_108G) {
adf_os_spin_unlock_bh(&ic->chan_lock);
if (!(dfs->dfs_proc_phyerr & DFS_AR_EN)) {
DFS_DPRINTK(dfs, ATH_DEBUG_DFS2,
"%s: DFS_AR_EN not enabled\n",
__func__);
return;
}
ATH_DFSEVENTQ_LOCK(dfs);
event = STAILQ_FIRST(&(dfs->dfs_eventq));
if (event == NULL) {
ATH_DFSEVENTQ_UNLOCK(dfs);
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
"%s: no more events space left\n",
__func__);
return;
}
STAILQ_REMOVE_HEAD(&(dfs->dfs_eventq), re_list);
ATH_DFSEVENTQ_UNLOCK(dfs);
event->re_rssi = e.rssi;
event->re_dur = e.dur;
event->re_full_ts = e.fulltsf;
event->re_ts = (e.rs_tstamp) & DFS_TSMASK;
event->re_chanindex = dfs->dfs_curchan_radindex;
event->re_flags = 0;
event->sidx = e.sidx;
/*
* Handle chirp flags.
*/
if (e.do_check_chirp) {
event->re_flags |= DFS_EVENT_CHECKCHIRP;
if (e.is_hw_chirp)
event->re_flags |= DFS_EVENT_HW_CHIRP;
if (e.is_sw_chirp)
event->re_flags |= DFS_EVENT_SW_CHIRP;
}
ATH_ARQ_LOCK(dfs);
STAILQ_INSERT_TAIL(&(dfs->dfs_arq), event, re_list);
ATH_ARQ_UNLOCK(dfs);
} else {
if (IEEE80211_IS_CHAN_DFS(chan)) {
adf_os_spin_unlock_bh(&ic->chan_lock);
if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) {
DFS_DPRINTK(dfs, ATH_DEBUG_DFS3,
"%s: DFS_RADAR_EN not enabled\n",
__func__);
return;
}
/*
* rssi is not accurate for short pulses, so do
* not filter based on that for short duration pulses
*
* XXX do this filtering above?
*/
if (dfs->dfs_caps.ath_dfs_ext_chan_ok) {
if ((e.rssi < dfs->dfs_rinfo.rn_minrssithresh &&
(e.dur > 4)) ||
e.dur > (dfs->dfs_rinfo.rn_maxpulsedur) ) {
dfs->ath_dfs_stats.rssi_discards++;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS1,
"Extension channel pulse is "
"discarded: dur=%d, "
"maxpulsedur=%d, rssi=%d, "
"minrssi=%d\n",
e.dur,
dfs->dfs_rinfo.rn_maxpulsedur,
e.rssi,
dfs->dfs_rinfo.rn_minrssithresh);
return;
}
} else {
if (e.rssi < dfs->dfs_rinfo.rn_minrssithresh ||
e.dur > dfs->dfs_rinfo.rn_maxpulsedur) {
/* XXX TODO add a debug statement? */
dfs->ath_dfs_stats.rssi_discards++;
return;
}
}
/*
* Add the event to the list, if there's space.
*/
ATH_DFSEVENTQ_LOCK(dfs);
event = STAILQ_FIRST(&(dfs->dfs_eventq));
if (event == NULL) {
ATH_DFSEVENTQ_UNLOCK(dfs);
DFS_DPRINTK(dfs, ATH_DEBUG_DFS,
"%s: no more events space left\n",
__func__);
return;
}
STAILQ_REMOVE_HEAD(&(dfs->dfs_eventq), re_list);
ATH_DFSEVENTQ_UNLOCK(dfs);
dfs->dfs_phyerr_queued_count++;
dfs->dfs_phyerr_w53_counter++;
event->re_dur = e.dur;
event->re_full_ts = e.fulltsf;
event->re_ts = (e.rs_tstamp) & DFS_TSMASK;
event->re_rssi = e.rssi;
event->sidx = e.sidx;
event->re_delta_diff = e.pulse_delta_diff;
event->re_delta_peak = e.pulse_delta_peak;
/*
* Handle chirp flags.
*/
if (e.do_check_chirp) {
event->re_flags |= DFS_EVENT_CHECKCHIRP;
if (e.is_hw_chirp)
event->re_flags |= DFS_EVENT_HW_CHIRP;
if (e.is_sw_chirp)
event->re_flags |= DFS_EVENT_SW_CHIRP;
}
/*
* Correctly set which channel is being reported on
*/
if (e.is_pri) {
event->re_chanindex = dfs->dfs_curchan_radindex;
} else {
if (dfs->dfs_extchan_radindex == -1) {
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"%s - phyerr on ext channel\n", __func__);
}
event->re_chanindex = dfs->dfs_extchan_radindex;
DFS_DPRINTK(dfs, ATH_DEBUG_DFS_PHYERR,
"%s New extension channel event is added "
"to queue\n",__func__);
}
ATH_DFSQ_LOCK(dfs);
STAILQ_INSERT_TAIL(&(dfs->dfs_radarq), event, re_list);
ATH_DFSQ_UNLOCK(dfs);
} else {
adf_os_spin_unlock_bh(&ic->chan_lock);
}
}
/*
* Schedule the radar/AR task as appropriate.
*
* XXX isn't a lock needed for ath_radar_tasksched?
*/
/*
* Commenting out the dfs_process_ar_event() since the function is never
* called at run time as dfs_arq will be empty and the function
* dfs_process_ar_event is obsolete and function definition is removed
* as part of dfs_ar.c file
*
* if (!STAILQ_EMPTY(&dfs->dfs_arq))
* // XXX shouldn't this be a task/timer too?
* dfs_process_ar_event(dfs, ic->ic_curchan);
*/
if (!STAILQ_EMPTY(&dfs->dfs_radarq) && !dfs->ath_radar_tasksched) {
dfs->ath_radar_tasksched = 1;
OS_SET_TIMER(&dfs->ath_dfs_task_timer, 0);
}
#undef EXT_CH_RADAR_FOUND
#undef PRI_CH_RADAR_FOUND
#undef EXT_CH_RADAR_EARLY_FOUND
}
#else
void
dfs_process_phyerr(struct ieee80211com *ic, void *buf,
u_int16_t datalen, u_int8_t r_rssi,
u_int8_t r_ext_rssi, u_int32_t r_rs_tstamp,
u_int64_t r_fulltsf, bool enable_log)
{
}
#endif /* ATH_SUPPORT_DFS */