blob: e06d9846a2ebf0d0cfe4446e51301e3b61fe5435 [file] [log] [blame]
/*
* Copyright (c) 2016-2019 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.
*/
/* Suppress -Waddress-of-packed-member for new toolchain update.
* Bug: http://b/33566695
*/
#if __clang_major__ >= 4
#pragma clang diagnostic ignored "-Waddress-of-packed-member"
#endif
/**
* 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_zero_cac.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)
return 0;
if (!dfs->dfs_curchan)
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)
return 0;
if (!dfs->dfs_curchan)
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 (WLAN_IS_CHAN_11N_HT40PLUS(dfs->dfs_curchan))
chan_offset = chan_width;
else if (WLAN_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_chan2freq(
dfs->dfs_curchan) + (chan_offset / 2);
}
/*
* For non-wide channels, the centre frequency is just dfs_ch_freq.
* The centre frequency for pri events is still dfs_ch_freq.
*/
if (is_pri)
return dfs_chan2freq(dfs->dfs_curchan);
if (is_ext)
return dfs_chan2freq(dfs->dfs_curchan) + chan_width;
return dfs_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_zero(e, 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_debug(dfs, WLAN_DEBUG_DFS_PHYERR_SUM,
"rssi=%u dur=%u, freq=%d MHz, freq_lo=%d MHz, freq_hi=%d MHz",
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_debug(dfs, WLAN_DEBUG_DFS,
"short error frame (%d bytes)", 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_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
"EARLY ext channel dur=%u rssi=%u datalen=%d",
dur, rssi, datalen);
}
if (!pulse_bw_info) {
dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
"ERROR channel dur=%u rssi=%u pulse_bw_info=0x%x datalen MOD 4 = %d",
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_zero(e, 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_debug(dfs, WLAN_DEBUG_DFS_PHYERR_SUM,
"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",
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_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
"RAW RSSI: rssi=%u ext_rssi=%u", 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_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
"RAW RSSI: rssi=%u ext_rssi=%u", 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_info(NULL, WLAN_DEBUG_DFS_ALWAYS, "%s", buf);
n = 0;
buf[0] = '\0';
}
}
/* Print the final line if we didn't print it above. */
if (n != 0)
dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS, "%s", buf);
}
/**
* dfs_bump_up_bin5_pulse_dur() - Bump up to a random BIN 5 pulse duration.
* @dfs: Pointer to wlan_dfs structure.
* @e: Pointer to dfs_phy_err structure.
* @slope: Slope value.
*/
static inline void dfs_bump_up_bin5_pulse_dur(
struct wlan_dfs *dfs,
struct dfs_phy_err *e,
int slope)
{
dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR, "old dur %d slope =%d",
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_debug(dfs, WLAN_DEBUG_DFS_PHYERR, "new dur %d", e->dur);
}
/**
* dfs_filter_short_pulses() - Filter short pulses.
* @dfs: Pointer to wlan_dfs structure.
* @e: Pointer to dfs_phy_err structure.
* @retval: Return value
*
* Rssi is not accurate for short pulses, so donot filter based on that for
* short duration pulses.
*/
static inline void dfs_filter_short_pulses(
struct wlan_dfs *dfs,
struct dfs_phy_err *e,
int *retval)
{
if (dfs->dfs_caps.wlan_dfs_ext_chan_ok) {
if ((e->rssi < dfs->dfs_rinfo.rn_minrssithresh &&
(e->dur > MAX_DUR_FOR_LOW_RSSI)) ||
e->dur > (dfs->dfs_rinfo.rn_maxpulsedur)) {
dfs->wlan_dfs_stats.rssi_discards++;
*retval = 1;
}
} else if (e->rssi < dfs->dfs_rinfo.rn_minrssithresh ||
e->dur > dfs->dfs_rinfo.rn_maxpulsedur) {
dfs->wlan_dfs_stats.rssi_discards++;
*retval = 1;
}
if (*retval) {
dfs_debug(dfs, WLAN_DEBUG_DFS1,
"%s pulse is discarded: dur=%d, maxpulsedur=%d, rssi=%d, minrssi=%d",
(dfs->dfs_caps.wlan_dfs_ext_chan_ok) ?
"Extension channel" : "",
e->dur, dfs->dfs_rinfo.rn_maxpulsedur,
e->rssi, dfs->dfs_rinfo.rn_minrssithresh);
}
}
/**
* dfs_set_chan_index() - Set channel index.
* @dfs: Pointer to wlan_dfs structure.
* @e: Pointer to dfs_phy_err structure.
* @event: Pointer to dfs_event structure.
*/
static inline void dfs_set_chan_index(
struct wlan_dfs *dfs,
struct dfs_phy_err *e,
struct dfs_event *event)
{
if (e->is_pri) {
event->re_chanindex = dfs->dfs_curchan_radindex;
} else {
event->re_chanindex = dfs->dfs_extchan_radindex;
dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
"%s New extension channel event is added to queue",
(event->re_chanindex == -1) ?
"- phyerr on ext channel" : "");
}
}
/**
* dfs_is_second_seg_radar_disabled() - Check for second segment radar disabled.
* @dfs: Pointer to wlan_dfs structure.
* @seg_id: Segment id.
*
* Return: true if the second segment RADAR is enabled else false.
*/
static bool dfs_is_second_seg_radar_disabled(
struct wlan_dfs *dfs, int seg_id)
{
if ((seg_id == SEG_ID_SECONDARY) &&
!(dfs->dfs_proc_phyerr & DFS_SECOND_SEGMENT_RADAR_EN)) {
dfs_debug(dfs, WLAN_DEBUG_DFS3,
"Second segment radar detection is disabled");
return true;
}
return false;
}
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) {
dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
return;
}
if (dfs->dfs_ignore_dfs) {
dfs_debug(dfs, WLAN_DEBUG_DFS1, "ignoring dfs");
return;
}
/*
* EV 129487: If radar detection is disabled, do not process PHY error
* data.
*/
if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) {
dfs_debug(dfs, WLAN_DEBUG_DFS1,
"DFS_RADAR_EN not set in dfs->dfs_proc_phyerr");
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 (WLAN_IS_CHAN_RADAR(dfs->dfs_curchan)) {
dfs_debug(dfs, WLAN_DEBUG_DFS1,
"Radar already found in the channel, do not queue radar data");
return;
}
dfs->dfs_phyerr_count++;
dfs->wlan_dfs_stats.total_phy_errors++;
dfs_debug(dfs, WLAN_DEBUG_DFS2, "phyerr %d len %d",
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_zero(&e, 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_bump_up_bin5_pulse_dur(dfs, &e, slope);
} else {
/* Set the duration so that it is rejected. */
e.is_sw_chirp = 0;
e.dur = MAX_BIN5_DUR + 100;
dfs_debug(dfs, WLAN_DEBUG_DFS_PHYERR,
"is_chirping = %d dur=%d",
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->dfs_ch_flags & CHANNEL_108G) == CHANNEL_108G) {
if (!(dfs->dfs_proc_phyerr & DFS_AR_EN)) {
dfs_debug(dfs, WLAN_DEBUG_DFS2,
"DFS_AR_EN not enabled");
return;
}
WLAN_DFSEVENTQ_LOCK(dfs);
event = STAILQ_FIRST(&(dfs->dfs_eventq));
if (!event) {
WLAN_DFSEVENTQ_UNLOCK(dfs);
dfs_debug(dfs, WLAN_DEBUG_DFS,
"no more events space left");
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 ((WLAN_IS_CHAN_DFS(dfs->dfs_curchan) ||
((WLAN_IS_CHAN_11AC_VHT160(dfs->dfs_curchan) ||
WLAN_IS_CHAN_11AC_VHT80_80(dfs->dfs_curchan)) &&
WLAN_IS_CHAN_DFS_CFREQ2(dfs->dfs_curchan))) ||
(dfs_is_precac_timer_running(dfs))) {
int retval = 0;
if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) {
dfs_debug(dfs, WLAN_DEBUG_DFS3,
"DFS_RADAR_EN not enabled");
return;
}
dfs_filter_short_pulses(dfs, &e, &retval);
if (retval)
return;
if (dfs_is_second_seg_radar_disabled(dfs, e.seg_id))
return;
/* Add the event to the list, if there's space. */
WLAN_DFSEVENTQ_LOCK(dfs);
event = STAILQ_FIRST(&(dfs->dfs_eventq));
if (!event) {
WLAN_DFSEVENTQ_UNLOCK(dfs);
dfs_debug(dfs, WLAN_DEBUG_DFS,
"no more events space left");
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;
event->re_delta_diff = e.pulse_delta_diff;
event->re_delta_peak = e.pulse_delta_peak;
event->re_psidx_diff = e.pulse_psidx_diff;
event->re_flags = 0;
event->re_flags |= DFS_EVENT_VALID_PSIDX_DIFF;
/* 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 */
dfs_set_chan_index(dfs, &e, event);
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;
qdf_timer_mod(&dfs->wlan_dfs_task_timer, 0);
}
#undef EXT_CH_RADAR_FOUND
#undef PRI_CH_RADAR_FOUND
#undef EXT_CH_RADAR_EARLY_FOUND
}
#ifdef QCA_MCL_DFS_SUPPORT
void dfs_process_phyerr_filter_offload(struct wlan_dfs *dfs,
struct radar_event_info *wlan_radar_event)
{
struct dfs_event *event;
int empty;
int do_check_chirp = 0;
int is_hw_chirp = 0;
int is_sw_chirp = 0;
int is_pri = 0;
if (!dfs) {
dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL");
return;
}
if (dfs->dfs_ignore_dfs) {
dfs_debug(dfs, WLAN_DEBUG_DFS1, "ignoring dfs");
return;
}
if (!(dfs->dfs_proc_phyerr & DFS_RADAR_EN)) {
dfs_debug(dfs, WLAN_DEBUG_DFS1,
"DFS_RADAR_EN not set in dfs->dfs_proc_phyerr");
return;
}
if (WLAN_IS_CHAN_RADAR(dfs->dfs_curchan)) {
dfs_debug(dfs, WLAN_DEBUG_DFS1,
"Radar already found in the channel, do not queue radar data");
return;
}
dfs->wlan_dfs_stats.total_phy_errors++;
if (dfs->dfs_caps.wlan_chip_is_bb_tlv) {
do_check_chirp = 1;
is_pri = 1;
is_hw_chirp = wlan_radar_event->pulse_is_chirp;
if ((uint32_t) dfs->dfs_phyerr_freq_min >
wlan_radar_event->pulse_center_freq) {
dfs->dfs_phyerr_freq_min =
(int)wlan_radar_event->pulse_center_freq;
}
if (dfs->dfs_phyerr_freq_max <
(int)wlan_radar_event->pulse_center_freq) {
dfs->dfs_phyerr_freq_max =
(int)wlan_radar_event->pulse_center_freq;
}
}
/*
* Now, add the parsed, checked and filtered
* radar phyerror event radar pulse event list.
* This event will then be processed by
* dfs_radar_processevent() to see if the pattern
* of pulses in radar pulse list match any radar
* singnature in the current regulatory domain.
*/
WLAN_DFSEVENTQ_LOCK(dfs);
empty = STAILQ_EMPTY(&(dfs->dfs_eventq));
WLAN_DFSEVENTQ_UNLOCK(dfs);
if (empty)
return;
/*
* Add the event to the list, if there's space.
*/
WLAN_DFSEVENTQ_LOCK(dfs);
event = STAILQ_FIRST(&(dfs->dfs_eventq));
if (!event) {
WLAN_DFSEVENTQ_UNLOCK(dfs);
dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
"%s: No more space left for queuing DFS Phyerror events",
__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 = (uint8_t) wlan_radar_event->pulse_duration;
event->re_rssi = wlan_radar_event->rssi;
event->re_ts = wlan_radar_event->pulse_detect_ts & DFS_TSMASK;
event->re_full_ts = (((uint64_t) wlan_radar_event->upload_fullts_high)
<< 32) | wlan_radar_event->upload_fullts_low;
/*
* Index of peak magnitude
*/
event->re_sidx = wlan_radar_event->peak_sidx;
event->re_delta_diff = wlan_radar_event->delta_diff;
event->re_delta_peak = wlan_radar_event->delta_peak;
event->re_flags = 0;
if (wlan_radar_event->is_psidx_diff_valid) {
event->re_flags |= DFS_EVENT_VALID_PSIDX_DIFF;
event->re_psidx_diff = wlan_radar_event->psidx_diff;
}
/*
* Handle chirp flags.
*/
if (do_check_chirp) {
event->re_flags |= DFS_EVENT_CHECKCHIRP;
if (is_hw_chirp)
event->re_flags |= DFS_EVENT_HW_CHIRP;
if (is_sw_chirp)
event->re_flags |= DFS_EVENT_SW_CHIRP;
}
/*
* Correctly set which channel is being reported on
*/
if (is_pri) {
event->re_chanindex = (uint8_t) dfs->dfs_curchan_radindex;
} else {
if (dfs->dfs_extchan_radindex == -1)
dfs_debug(dfs, WLAN_DEBUG_DFS1,
"%s phyerr on ext channel", __func__);
event->re_chanindex = (uint8_t) dfs->dfs_extchan_radindex;
dfs_debug(dfs, WLAN_DEBUG_DFS1,
"%s:New extension channel event is added to queue",
__func__);
}
WLAN_DFSQ_LOCK(dfs);
STAILQ_INSERT_TAIL(&(dfs->dfs_radarq), event, re_list);
empty = STAILQ_EMPTY(&dfs->dfs_radarq);
WLAN_DFSQ_UNLOCK(dfs);
if (!empty && !dfs->wlan_radar_tasksched) {
dfs->wlan_radar_tasksched = 1;
qdf_timer_mod(&dfs->wlan_dfs_task_timer, 0);
}
}
#endif
void dfs_is_radar_enabled(struct wlan_dfs *dfs, int *ignore_dfs)
{
*ignore_dfs = dfs->dfs_ignore_dfs;
}