blob: 6c02b7036bc7818d17e76c582bc9ad214966ad92 [file] [log] [blame]
/*
* Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: API for processing radar found indication.
*
*/
#include "../dfs.h"
#include "../dfs_zero_cac.h"
#include "../dfs_process_radar_found_ind.h"
#include <wlan_reg_services_api.h>
#include <wlan_objmgr_vdev_obj.h>
#include <wlan_dfs_utils_api.h>
#include <wlan_dfs_tgt_api.h>
#include "wlan_dfs_mlme_api.h"
#include "../dfs_internal.h"
/**
* TODO: The code is not according to the following description needs
* modification and correction. Code always adds left and right channels to
* NOL even if it is not a chirp radar.
*
* A) If chirp radar starts at boundary and ends at boundary then three channels
* will be affected.
* freq_offset.freq[0] = fn (Center frequency)
* freq_offset.freq[1] = fn-1 (Left of center)
* freq_offset.freq[2] = fn+1 (Right of center)
*
* Three channels, ch(n-1), ch(n)and ch(n+1) will be added to NOL.
*
* Chirp start freq Chirp end freq
* | |
* | |
* V V
* _______________________________________________________________________
* | center freq | center freq | center freq |
* | ch(n-1) | ch(n) | ch(n+1) |
* | | | | | | |
* | | | | | | |
* | | | | | | |
* fn-1 fn boundary fn+1
* <-------- 20 Mhz ------>
*
* B) If chirp radar starts at one channel and continues up to another channel
* then two channels will be affected.
* freq_offset.freq[0] = fn
* freq_offset.freq[1] = 0
* freq_offset.freq[2] = fn+1
*
* Three channels, ch(n-1), ch(n)and ch(n+1) will be added to NOL.
*
* Chirp start freq Chirp end freq
* | |
* | |
* V V
* _______________________________________________________________________
* | center freq | center freq | center freq |
* | ch(n-1) | ch(n) | ch(n+1) |
* | | | | | | |
* | | | | | | |
* | | | | | | |
* fn-1 fn boundary fn+1
* <-------- 20 Mhz ------>
*
* C) Radar found at boundary, two channels will be affected.
* freq_offset.freq[0] = fn
* freq_offset.freq[1] = 0
* freq_offset.freq[2] = fn+1
*
* Two channels, ch(n) and ch(n+1) will be added to NOL.
*
* dfs_freq_offset (radar found freq)
* |
* |
* V
* _______________________________________________________________________
* | center freq | center freq | center freq |
* | ch(n-1) | ch(n) | ch(n+1) |
* | | | | | | |
* | | | | | | |
* | | | | | | |
* fn-1 fn boundary fn+1
* <-------- 20 Mhz ------>
*
*
* D) Else only one channel will be affected.
* freq_offset.freq[0] = fn
* freq_offset.freq[1] = 0
* freq_offset.freq[2] = 0
*
* One channel ch(n) will be added to NOL.
*
*
* dfs_freq_offset (radar found freq)
* |
* |
* V
* _______________________________________________________________________
* | center freq | center freq | center freq |
* | ch(n-1) | ch(n) | ch(n+1) |
* | | | | | | |
* | | | | | | |
* | | | | | | |
* fn-1 fn boundary fn+1
* <-------- 20 Mhz ------>
*/
int dfs_set_nol_subchannel_marking(struct wlan_dfs *dfs,
bool nol_subchannel_marking)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!dfs)
return -EIO;
dfs->dfs_use_nol_subchannel_marking = nol_subchannel_marking;
dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "NOL subchannel marking is %s ",
(nol_subchannel_marking) ? "set" : "disabled");
if (dfs->dfs_is_offload_enabled)
status = tgt_dfs_send_subchan_marking(dfs->dfs_pdev_obj,
nol_subchannel_marking);
return qdf_status_to_os_return(status);
}
int dfs_get_nol_subchannel_marking(struct wlan_dfs *dfs,
bool *nol_subchannel_marking)
{
if (!dfs)
return -EIO;
(*nol_subchannel_marking) = dfs->dfs_use_nol_subchannel_marking;
return 0;
}
/**
* dfs_radar_add_channel_list_to_nol_for_freq()- Add given channels to nol
* @dfs: Pointer to wlan_dfs structure.
* @freq_list: Pointer to list of frequency.
* @num_channels: Number of channels in the list.
*
* Add list of channels to nol, only if the channel is dfs.
*
* Return: QDF_STATUS
*/
#ifdef CONFIG_CHAN_FREQ_API
static QDF_STATUS
dfs_radar_add_channel_list_to_nol_for_freq(struct wlan_dfs *dfs,
uint16_t *freq_list,
uint8_t num_channels)
{
int i;
uint16_t last_chan_freq = 0;
uint16_t nol_freq_list[NUM_CHANNELS_160MHZ];
uint8_t num_ch = 0;
if (num_channels > NUM_CHANNELS_160MHZ) {
dfs_err(dfs, WLAN_DEBUG_DFS,
"Invalid num channels: %d", num_channels);
return QDF_STATUS_E_FAILURE;
}
for (i = 0; i < num_channels; i++) {
if (freq_list[i] == 0 ||
freq_list[i] == last_chan_freq)
continue;
if (!utils_is_dfs_chan_for_freq(dfs->dfs_pdev_obj,
freq_list[i])) {
dfs_info(dfs, WLAN_DEBUG_DFS, "ch=%d is not dfs, skip",
freq_list[i]);
continue;
}
last_chan_freq = freq_list[i];
DFS_NOL_ADD_CHAN_LOCKED(dfs,
freq_list[i],
dfs->wlan_dfs_nol_timeout);
nol_freq_list[num_ch++] = last_chan_freq;
utils_dfs_deliver_event(dfs->dfs_pdev_obj,
freq_list[i],
WLAN_EV_NOL_STARTED);
dfs_info(dfs, WLAN_DEBUG_DFS_NOL, "ch=%d Added to NOL",
last_chan_freq);
}
if (!num_ch) {
dfs_err(dfs, WLAN_DEBUG_DFS,
"dfs channels not found in channel list");
return QDF_STATUS_E_FAILURE;
}
utils_dfs_reg_update_nol_chan_for_freq(dfs->dfs_pdev_obj,
nol_freq_list, num_ch,
DFS_NOL_SET);
if (dfs->dfs_is_stadfs_enabled)
if (dfs_mlme_is_opmode_sta(dfs->dfs_pdev_obj))
utils_dfs_reg_update_nol_history_chan_for_freq(
dfs->dfs_pdev_obj, nol_freq_list,
num_ch, DFS_NOL_HISTORY_SET);
dfs_nol_update(dfs);
utils_dfs_save_nol(dfs->dfs_pdev_obj);
return QDF_STATUS_SUCCESS;
}
#endif
/**
* dfs_radar_chan_for_80()- Find frequency offsets for 80MHz
* @freq_offset: freq offset
* @center_freq: center frequency
*
* Find frequency offsets for 80MHz
*
* Return: None
*/
static void dfs_radar_chan_for_80(struct freqs_offsets *freq_offset,
uint32_t center_freq)
{
int i;
for (i = 0; i < DFS_NUM_FREQ_OFFSET; i++) {
if (freq_offset->offset[i] < DFS_OFFSET_SECOND_LOWER)
freq_offset->freq[i] =
DFS_THIRD_LOWER_CHANNEL(center_freq);
else if ((freq_offset->offset[i] > DFS_OFFSET_SECOND_LOWER) &&
(freq_offset->offset[i] < DFS_OFFSET_FIRST_LOWER))
freq_offset->freq[i] =
DFS_SECOND_LOWER_CHANNEL(center_freq);
else if ((freq_offset->offset[i] > DFS_OFFSET_FIRST_LOWER) &&
(freq_offset->offset[i] < 0))
freq_offset->freq[i] =
DFS_FIRST_LOWER_CHANNEL(center_freq);
else if ((freq_offset->offset[i] > 0) &&
(freq_offset->offset[i] < DFS_OFFSET_FIRST_UPPER))
freq_offset->freq[i] =
DFS_FIRST_UPPER_CHANNEL(center_freq);
else if ((freq_offset->offset[i] > DFS_OFFSET_FIRST_UPPER) &&
(freq_offset->offset[i] < DFS_OFFSET_SECOND_UPPER))
freq_offset->freq[i] =
DFS_SECOND_UPPER_CHANNEL(center_freq);
else if (freq_offset->offset[i] > DFS_OFFSET_SECOND_UPPER)
freq_offset->freq[i] =
DFS_THIRD_UPPER_CHANNEL(center_freq);
}
}
/**
* dfs_radar_chan_for_40()- Find frequency offsets for 40MHz
* @freq_offset: freq offset
* @center_freq: center frequency
*
* Find frequency offsets for 40MHz
*
* Return: None
*/
static void dfs_radar_chan_for_40(struct freqs_offsets *freq_offset,
uint32_t center_freq)
{
int i;
for (i = 0; i < DFS_NUM_FREQ_OFFSET; i++) {
if (freq_offset->offset[i] < DFS_OFFSET_FIRST_LOWER)
freq_offset->freq[i] =
DFS_SECOND_LOWER_CHANNEL(center_freq);
else if ((freq_offset->offset[i] > DFS_OFFSET_FIRST_LOWER) &&
(freq_offset->offset[i] < 0))
freq_offset->freq[i] =
DFS_FIRST_LOWER_CHANNEL(center_freq);
else if ((freq_offset->offset[i] > 0) &&
(freq_offset->offset[i] < DFS_OFFSET_FIRST_UPPER))
freq_offset->freq[i] =
DFS_FIRST_UPPER_CHANNEL(center_freq);
else if (freq_offset->offset[i] > DFS_OFFSET_FIRST_UPPER)
freq_offset->freq[i] =
DFS_SECOND_UPPER_CHANNEL(center_freq);
}
}
/**
* dfs_radar_chan_for_20()- Find frequency offsets for 20MHz
* @freq_offset: freq offset
* @center_freq: center frequency
*
* Find frequency offsets for 20MHz
*
* Return: None
*/
static void dfs_radar_chan_for_20(struct freqs_offsets *freq_offset,
uint32_t center_freq)
{
int i;
for (i = 0; i < DFS_NUM_FREQ_OFFSET; i++) {
if (freq_offset->offset[i] <= DFS_20MZ_OFFSET_LOWER)
freq_offset->freq[i] =
DFS_20MHZ_LOWER_CHANNEL(center_freq);
else if ((freq_offset->offset[i] > DFS_20MZ_OFFSET_LOWER) &&
(freq_offset->offset[i] < DFS_20MZ_OFFSET_UPPER))
freq_offset->freq[i] = center_freq;
else if (freq_offset->offset[i] >= DFS_20MZ_OFFSET_UPPER)
freq_offset->freq[i] =
DFS_20MHZ_UPPER_CHANNEL(center_freq);
}
}
/* dfs_compute_radar_found_cfreq(): Computes the centre frequency of the
* radar hit channel.
* @dfs: Pointer to wlan_dfs structure.
* @radar_found: Pointer to radar_found_info.
* @freq_center: Pointer to retrieve the value of radar found cfreq.
*/
#ifdef CONFIG_CHAN_FREQ_API
static void
dfs_compute_radar_found_cfreq(struct wlan_dfs *dfs,
struct radar_found_info *radar_found,
uint32_t *freq_center)
{
struct dfs_channel *curchan = dfs->dfs_curchan;
/* Radar found on agile detector ID.
* Applicable to chips that have a separate agile radar detector
* engine.
*/
if (radar_found->detector_id == AGILE_DETECTOR_ID) {
*freq_center = dfs->dfs_agile_precac_freq_mhz;
} else if (!radar_found->segment_id) {
*freq_center = curchan->dfs_ch_mhz_freq_seg1;
} else {
/* Radar found on secondary segment by the HW when
* preCAC was running. It (dfs_precac_enable) is specific to
* legacy chips.
*/
if (dfs_is_precac_timer_running(dfs) &&
dfs_is_legacy_precac_enabled(dfs)) {
*freq_center = dfs->dfs_precac_secondary_freq_mhz;
} else {
/* Radar found on secondary segment by the HW, when preCAC
* was not running in legacy chips or preCAC was running
* in Lithium chips.
*/
*freq_center = curchan->dfs_ch_mhz_freq_seg2;
if (WLAN_IS_CHAN_MODE_160(curchan)) {
/* If center frequency of entire 160 band
* is less than center frequency of primary
* segment, then the center frequency of
* secondary segment is -40 of center
* frequency of entire 160 segment.
*/
if (curchan->dfs_ch_mhz_freq_seg2 <
curchan->dfs_ch_mhz_freq_seg1)
*freq_center -=
DFS_160MHZ_SECOND_SEG_OFFSET;
else
*freq_center +=
DFS_160MHZ_SECOND_SEG_OFFSET;
}
}
}
}
#else
#ifdef CONFIG_CHAN_NUM_API
static void
dfs_compute_radar_found_cfreq(struct wlan_dfs *dfs,
struct radar_found_info
*radar_found,
uint32_t *freq_center)
{
struct dfs_channel *curchan = dfs->dfs_curchan;
/* Radar found on agile detector ID.
* Applicable to chips that have a separate agile radar detector
* engine.
*/
if (radar_found->detector_id == AGILE_DETECTOR_ID) {
*freq_center = utils_dfs_chan_to_freq(
dfs->dfs_agile_precac_freq);
/* Radar found on primary segment by the HW. */
} else if (radar_found->segment_id == PRIMARY_SEG) {
*freq_center = utils_dfs_chan_to_freq(
curchan->dfs_ch_vhtop_ch_freq_seg1);
} else {
/* Radar found on secondary segment by the HW when
* preCAC was running. It (dfs_precac_enable) is specific to
* legacy chips.
*/
if (dfs_is_precac_timer_running(dfs) &&
dfs_is_legacy_precac_enabled(dfs)) {
*freq_center = utils_dfs_chan_to_freq(
dfs->dfs_precac_secondary_freq);
} else {
/* Radar found on secondary segment by the HW, when preCAC
* was not running in legacy chips or preCAC was running
* in Lithium chips.
*/
*freq_center = utils_dfs_chan_to_freq(
curchan->dfs_ch_vhtop_ch_freq_seg2);
if (WLAN_IS_CHAN_MODE_160(curchan)) {
/* If center frequency of entire 160 band
* is less than center frequency of primary
* segment, then the center frequency of
* secondary segment is -40 of center
* frequency of entire 160 segment.
*/
if (curchan->dfs_ch_vhtop_ch_freq_seg2 <
curchan->dfs_ch_vhtop_ch_freq_seg1)
*freq_center -=
DFS_160MHZ_SECOND_SEG_OFFSET;
else
*freq_center +=
DFS_160MHZ_SECOND_SEG_OFFSET;
}
}
}
}
#endif
#endif
/**
* dfs_find_radar_affected_subchans_for_freq() - Find radar affected sub chans.
* @dfs: Pointer to wlan_dfs structure.
* @radar_found: Pointer to radar_found structure.
* @freq_list: Pointer to save radar affected channels.
* @freq_center: Freq_center of the radar affected chan.
*
* Return: Number of channels.
*/
#ifdef CONFIG_CHAN_FREQ_API
static uint8_t
dfs_find_radar_affected_subchans_for_freq(struct wlan_dfs *dfs,
struct radar_found_info *radar_found,
uint16_t *freq_list,
uint32_t freq_center)
{
int i, j;
uint8_t num_radar_subchans;
uint32_t flag;
int32_t sidx;
uint16_t candidate_subchan_freq;
uint16_t cur_subchans[NUM_CHANNELS_160MHZ];
uint8_t n_cur_subchans;
struct dfs_channel *curchan = dfs->dfs_curchan;
struct freqs_offsets freq_offset;
qdf_mem_zero(&freq_offset, sizeof(freq_offset));
flag = curchan->dfs_ch_flags;
for (i = 0; i < DFS_NUM_FREQ_OFFSET; i++)
freq_offset.offset[i] = radar_found->freq_offset;
sidx = DFS_FREQ_OFFSET_TO_SIDX(radar_found->freq_offset);
dfs_info(dfs, WLAN_DEBUG_DFS,
"seg=%d, det=%d, sidx=%d, offset=%d, chirp=%d, flag=%d, f=%d",
radar_found->segment_id, radar_found->detector_id, sidx,
radar_found->freq_offset, radar_found->is_chirp,
flag, freq_center);
if ((WLAN_IS_CHAN_A(curchan)) ||
WLAN_IS_CHAN_MODE_20(curchan)) {
if (radar_found->is_chirp ||
(sidx && !(abs(sidx) % DFS_BOUNDARY_SIDX))) {
freq_offset.offset[LEFT_CH] -= DFS_CHIRP_OFFSET;
freq_offset.offset[RIGHT_CH] += DFS_CHIRP_OFFSET;
}
dfs_radar_chan_for_20(&freq_offset, freq_center);
} else if (WLAN_IS_CHAN_MODE_40(curchan)) {
if (radar_found->is_chirp || !(abs(sidx) % DFS_BOUNDARY_SIDX)) {
freq_offset.offset[LEFT_CH] -= DFS_CHIRP_OFFSET;
freq_offset.offset[RIGHT_CH] += DFS_CHIRP_OFFSET;
}
dfs_radar_chan_for_40(&freq_offset, freq_center);
} else if (WLAN_IS_CHAN_MODE_80(curchan) ||
WLAN_IS_CHAN_MODE_160(curchan) ||
WLAN_IS_CHAN_MODE_80_80(curchan)) {
if (radar_found->is_chirp || !(abs(sidx) % DFS_BOUNDARY_SIDX)) {
freq_offset.offset[LEFT_CH] -= DFS_CHIRP_OFFSET;
freq_offset.offset[RIGHT_CH] += DFS_CHIRP_OFFSET;
}
dfs_radar_chan_for_80(&freq_offset, freq_center);
} else {
dfs_err(dfs, WLAN_DEBUG_DFS,
"channel flag=%d is invalid", flag);
return 0;
}
n_cur_subchans =
dfs_get_bonding_channels_for_freq(dfs, curchan,
radar_found->segment_id,
radar_found->detector_id,
cur_subchans);
for (i = 0, num_radar_subchans = 0; i < DFS_NUM_FREQ_OFFSET; i++) {
candidate_subchan_freq = freq_offset.freq[i];
for (j = 0; j < n_cur_subchans; j++) {
if (cur_subchans[j] == candidate_subchan_freq) {
freq_list[num_radar_subchans++] =
candidate_subchan_freq;
dfs_info(dfs, WLAN_DEBUG_DFS,
"offset=%d, channel=%d",
num_radar_subchans,
freq_list[num_radar_subchans - 1]);
break;
}
}
}
return num_radar_subchans;
}
#endif
#ifdef CONFIG_CHAN_NUM_API
uint8_t dfs_get_bonding_channels_without_seg_info(struct dfs_channel *chan,
uint8_t *channels)
{
uint8_t center_chan;
uint8_t nchannels = 0;
center_chan = chan->dfs_ch_vhtop_ch_freq_seg1;
if (WLAN_IS_CHAN_MODE_20(chan)) {
nchannels = 1;
channels[0] = center_chan;
} else if (WLAN_IS_CHAN_MODE_40(chan)) {
nchannels = 2;
channels[0] = center_chan - DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[1] = center_chan + DFS_5GHZ_NEXT_CHAN_OFFSET;
} else if (WLAN_IS_CHAN_MODE_80(chan)) {
nchannels = 4;
channels[0] = center_chan - DFS_5GHZ_2ND_CHAN_OFFSET;
channels[1] = center_chan - DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[2] = center_chan + DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[3] = center_chan + DFS_5GHZ_2ND_CHAN_OFFSET;
} else if (WLAN_IS_CHAN_MODE_80_80(chan)) {
nchannels = 8;
channels[0] = center_chan - DFS_5GHZ_2ND_CHAN_OFFSET;
channels[1] = center_chan - DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[2] = center_chan + DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[3] = center_chan + DFS_5GHZ_2ND_CHAN_OFFSET;
center_chan = chan->dfs_ch_vhtop_ch_freq_seg2;
channels[4] = center_chan - DFS_5GHZ_2ND_CHAN_OFFSET;
channels[5] = center_chan - DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[6] = center_chan + DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[7] = center_chan + DFS_5GHZ_2ND_CHAN_OFFSET;
} else if (WLAN_IS_CHAN_MODE_160(chan)) {
nchannels = 8;
center_chan = chan->dfs_ch_vhtop_ch_freq_seg2;
channels[0] = center_chan - DFS_5GHZ_4TH_CHAN_OFFSET;
channels[1] = center_chan - DFS_5GHZ_3RD_CHAN_OFFSET;
channels[2] = center_chan - DFS_5GHZ_2ND_CHAN_OFFSET;
channels[3] = center_chan - DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[4] = center_chan + DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[5] = center_chan + DFS_5GHZ_2ND_CHAN_OFFSET;
channels[6] = center_chan + DFS_5GHZ_3RD_CHAN_OFFSET;
channels[7] = center_chan + DFS_5GHZ_4TH_CHAN_OFFSET;
}
return nchannels;
}
#endif
/*
* dfs_get_bonding_channel_without_seg_info_for_freq() - Get bonding frequency
* list.
* @chan: Pointer to dfs_channel.
* @freq_list: Pointer to frequency list.
*/
#ifdef CONFIG_CHAN_FREQ_API
uint8_t
dfs_get_bonding_channel_without_seg_info_for_freq(struct dfs_channel *chan,
uint16_t *freq_list)
{
uint16_t center_freq;
uint8_t nchannels = 0;
center_freq = chan->dfs_ch_mhz_freq_seg1;
if (WLAN_IS_CHAN_MODE_20(chan)) {
nchannels = 1;
freq_list[0] = center_freq;
} else if (WLAN_IS_CHAN_MODE_40(chan)) {
nchannels = 2;
freq_list[0] = center_freq - DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[1] = center_freq + DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
} else if (WLAN_IS_CHAN_MODE_80(chan)) {
nchannels = 4;
freq_list[0] = center_freq - DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
freq_list[1] = center_freq - DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[2] = center_freq + DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[3] = center_freq + DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
} else if (WLAN_IS_CHAN_MODE_80_80(chan)) {
nchannels = 8;
freq_list[0] = center_freq - DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
freq_list[1] = center_freq - DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[2] = center_freq + DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[3] = center_freq + DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
center_freq = chan->dfs_ch_mhz_freq_seg2;
freq_list[4] = center_freq - DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
freq_list[5] = center_freq - DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[6] = center_freq + DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[7] = center_freq + DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
} else if (WLAN_IS_CHAN_MODE_160(chan)) {
nchannels = 8;
center_freq = chan->dfs_ch_mhz_freq_seg2;
freq_list[0] = center_freq - DFS_5GHZ_4TH_CHAN_FREQ_OFFSET;
freq_list[1] = center_freq - DFS_5GHZ_3RD_CHAN_FREQ_OFFSET;
freq_list[2] = center_freq - DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
freq_list[3] = center_freq - DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[4] = center_freq + DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[5] = center_freq + DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
freq_list[6] = center_freq + DFS_5GHZ_3RD_CHAN_FREQ_OFFSET;
freq_list[7] = center_freq + DFS_5GHZ_4TH_CHAN_FREQ_OFFSET;
}
return nchannels;
}
#endif
/*
* dfs_get_bonding_channels_for_freq() - Get bonding channel frequency.
* @dfs: Pointer to wlan_dfs.
* @curchan: Pointer to dfs_channel.
* @segment_id: Segment ID.
* @detector_id: Detector ID.
* @freq_list: Pointer to frequency list.
*/
#ifdef CONFIG_CHAN_FREQ_API
uint8_t dfs_get_bonding_channels_for_freq(struct wlan_dfs *dfs,
struct dfs_channel *curchan,
uint32_t segment_id,
uint8_t detector_id,
uint16_t *freq_list)
{
uint16_t center_freq;
uint8_t nchannels = 0;
if (detector_id == AGILE_DETECTOR_ID)
center_freq = dfs->dfs_agile_precac_freq_mhz;
else if (!segment_id)
center_freq = curchan->dfs_ch_mhz_freq_seg1;
else {
/* When precac is running "dfs_ch_vhtop_ch_freq_seg2" is
* zero and "dfs_precac_secondary_freq" holds the secondary
* frequency.
*/
if (dfs_is_precac_timer_running(dfs))
center_freq = dfs->dfs_precac_secondary_freq_mhz;
else
center_freq = curchan->dfs_ch_mhz_freq_seg2;
}
if (WLAN_IS_CHAN_MODE_20(curchan)) {
nchannels = 1;
freq_list[0] = center_freq;
} else if (WLAN_IS_CHAN_MODE_40(curchan)) {
nchannels = 2;
freq_list[0] = center_freq - DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[1] = center_freq + DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
} else if (WLAN_IS_CHAN_MODE_80(curchan) ||
WLAN_IS_CHAN_MODE_80_80(curchan) ||
detector_id == AGILE_DETECTOR_ID) {
/* If the current channel's bandwidth is 80/80+80/160Mhz,
* the corresponding agile Detector's bandwidth will be 80Mhz.
* Therefore, if radar is found on the agile detector find
* subchannels for 80Mhz bandwidth.
*/
nchannels = 4;
freq_list[0] = center_freq - DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
freq_list[1] = center_freq - DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[2] = center_freq + DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[3] = center_freq + DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
} else if (WLAN_IS_CHAN_MODE_160(curchan)) {
nchannels = 8;
center_freq = curchan->dfs_ch_mhz_freq_seg2;
freq_list[0] = center_freq - DFS_5GHZ_4TH_CHAN_FREQ_OFFSET;
freq_list[1] = center_freq - DFS_5GHZ_3RD_CHAN_FREQ_OFFSET;
freq_list[2] = center_freq - DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
freq_list[3] = center_freq - DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[4] = center_freq + DFS_5GHZ_NEXT_CHAN_FREQ_OFFSET;
freq_list[5] = center_freq + DFS_5GHZ_2ND_CHAN_FREQ_OFFSET;
freq_list[6] = center_freq + DFS_5GHZ_3RD_CHAN_FREQ_OFFSET;
freq_list[7] = center_freq + DFS_5GHZ_4TH_CHAN_FREQ_OFFSET;
}
return nchannels;
}
#endif
#ifdef CONFIG_CHAN_NUM_API
uint8_t dfs_get_bonding_channels(struct wlan_dfs *dfs,
struct dfs_channel *curchan,
uint32_t segment_id,
uint8_t detector_id,
uint8_t *channels)
{
uint8_t center_chan;
uint8_t nchannels = 0;
if (detector_id == AGILE_DETECTOR_ID)
center_chan = dfs->dfs_agile_precac_freq;
else if (!segment_id)
center_chan = curchan->dfs_ch_vhtop_ch_freq_seg1;
else {
/* When precac is running "dfs_ch_vhtop_ch_freq_seg2" is
* zero and "dfs_precac_secondary_freq" holds the secondary
* frequency in case of legacy chips.
* For chips that support a separate agile detector engine,
* "dfs_agile_precac_freq" holds the frequency that agile
* engine operates on.
*
* In case of radar detected by the HW in the secondary 80
* channel,"dfs_ch_vhtop_ch_freq_seg2" holds the secondary
* segment center frequency in the below cases:
* 1. preCAC timer is running in chips that support separate
* agile engines.
* 2. preCAC timer is not running.
*/
if (dfs_is_precac_timer_running(dfs) &&
dfs_is_legacy_precac_enabled(dfs))
center_chan = dfs->dfs_precac_secondary_freq;
else
center_chan = curchan->dfs_ch_vhtop_ch_freq_seg2;
}
if (WLAN_IS_CHAN_MODE_20(curchan)) {
nchannels = 1;
channels[0] = center_chan;
} else if (WLAN_IS_CHAN_MODE_40(curchan)) {
nchannels = 2;
channels[0] = center_chan - DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[1] = center_chan + DFS_5GHZ_NEXT_CHAN_OFFSET;
} else if (WLAN_IS_CHAN_MODE_80(curchan) ||
WLAN_IS_CHAN_MODE_80_80(curchan) ||
detector_id == AGILE_DETECTOR_ID) {
/* If the current channel's bandwidth is 80/80+80/160Mhz,
* the corresponding agile Detector's bandwidth will be 80Mhz.
* Therefore, if radar is found on the agile detector find
* subchannels for 80Mhz bandwidth.
*/
nchannels = 4;
channels[0] = center_chan - DFS_5GHZ_2ND_CHAN_OFFSET;
channels[1] = center_chan - DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[2] = center_chan + DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[3] = center_chan + DFS_5GHZ_2ND_CHAN_OFFSET;
} else if (WLAN_IS_CHAN_MODE_160(curchan)) {
nchannels = 8;
center_chan = curchan->dfs_ch_vhtop_ch_freq_seg2;
channels[0] = center_chan - DFS_5GHZ_4TH_CHAN_OFFSET;
channels[1] = center_chan - DFS_5GHZ_3RD_CHAN_OFFSET;
channels[2] = center_chan - DFS_5GHZ_2ND_CHAN_OFFSET;
channels[3] = center_chan - DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[4] = center_chan + DFS_5GHZ_NEXT_CHAN_OFFSET;
channels[5] = center_chan + DFS_5GHZ_2ND_CHAN_OFFSET;
channels[6] = center_chan + DFS_5GHZ_3RD_CHAN_OFFSET;
channels[7] = center_chan + DFS_5GHZ_4TH_CHAN_OFFSET;
}
return nchannels;
}
#endif
static inline void dfs_reset_bangradar(struct wlan_dfs *dfs)
{
dfs->dfs_bangradar_type = DFS_NO_BANGRADAR;
}
int dfs_radarevent_basic_sanity(struct wlan_dfs *dfs,
struct dfs_channel *chan)
{
if (!(dfs->dfs_seg_id == SEG_ID_SECONDARY &&
dfs_is_precac_timer_running(dfs)))
if (!(WLAN_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(chan))) {
dfs_debug(dfs, WLAN_DEBUG_DFS2,
"radar event on non-DFS chan");
if (!(dfs->dfs_is_offload_enabled)) {
dfs_reset_radarq(dfs);
dfs_reset_alldelaylines(dfs);
dfs_reset_bangradar(dfs);
}
return 0;
}
return 1;
}
/**
* dfs_send_csa_to_current_chan() - Send CSA to current channel
* @dfs: Pointer to wlan_dfs structure.
*
* For the test mode(usenol = 0), don't do a CSA; but setup the test timer so
* we get a CSA _back_ to the current operating channel.
*/
static inline void dfs_send_csa_to_current_chan(struct wlan_dfs *dfs)
{
qdf_timer_stop(&dfs->wlan_dfstesttimer);
dfs->wlan_dfstest = 1;
dfs->wlan_dfstest_ieeechan = dfs->dfs_curchan->dfs_ch_ieee;
dfs->wlan_dfstesttime = 1; /* 1ms */
qdf_timer_mod(&dfs->wlan_dfstesttimer, dfs->wlan_dfstesttime);
}
int dfs_second_segment_radar_disable(struct wlan_dfs *dfs)
{
dfs->dfs_proc_phyerr &= ~DFS_SECOND_SEGMENT_RADAR_EN;
return 0;
}
/* dfs_prepare_nol_ie_bitmap: Create a Bitmap from the radar found subchannels
* to be sent along with RCSA.
* @dfs: Pointer to wlan_dfs.
* @radar_found: Pointer to radar_found_info.
* @in_sub_channels: Pointer to Sub-channels.
* @n_in_sub_channels: Number of sub-channels.
*/
#ifdef CONFIG_CHAN_FREQ_API
static void
dfs_prepare_nol_ie_bitmap_for_freq(struct wlan_dfs *dfs,
struct radar_found_info *radar_found,
uint16_t *in_sub_channels,
uint8_t n_in_sub_channels)
{
uint16_t cur_subchans[NUM_CHANNELS_160MHZ];
uint8_t n_cur_subchans;
uint8_t i;
uint8_t j;
uint8_t bits = 0x01;
n_cur_subchans =
dfs_get_bonding_channels_for_freq(dfs, dfs->dfs_curchan,
radar_found->segment_id,
radar_found->detector_id,
cur_subchans);
dfs->dfs_nol_ie_bandwidth = MIN_DFS_SUBCHAN_BW;
dfs->dfs_nol_ie_startfreq = cur_subchans[0];
/* Search through the array list of radar affected subchannels
* to find if the subchannel in our current channel has radar hit.
* Break if found to reduce loop count.
*/
for (i = 0; i < n_cur_subchans; i++) {
for (j = 0; j < n_in_sub_channels; j++) {
if (cur_subchans[i] == in_sub_channels[j]) {
dfs->dfs_nol_ie_bitmap |= bits;
break;
}
}
bits <<= 1;
}
}
#endif
void dfs_fetch_nol_ie_info(struct wlan_dfs *dfs,
uint8_t *nol_ie_bandwidth,
uint16_t *nol_ie_startfreq,
uint8_t *nol_ie_bitmap)
{
if (nol_ie_bandwidth)
*nol_ie_bandwidth = dfs->dfs_nol_ie_bandwidth;
if (nol_ie_startfreq)
*nol_ie_startfreq = dfs->dfs_nol_ie_startfreq;
if (nol_ie_bitmap)
*nol_ie_bitmap = dfs->dfs_nol_ie_bitmap;
}
void dfs_get_rcsa_flags(struct wlan_dfs *dfs, bool *is_rcsa_ie_sent,
bool *is_nol_ie_sent)
{
if (is_rcsa_ie_sent)
*is_rcsa_ie_sent = dfs->dfs_is_rcsa_ie_sent;
if (is_nol_ie_sent)
*is_nol_ie_sent = dfs->dfs_is_nol_ie_sent;
}
void dfs_set_rcsa_flags(struct wlan_dfs *dfs, bool is_rcsa_ie_sent,
bool is_nol_ie_sent)
{
dfs->dfs_is_rcsa_ie_sent = is_rcsa_ie_sent;
dfs->dfs_is_nol_ie_sent = is_nol_ie_sent;
}
static void dfs_reset_nol_ie_bitmap(struct wlan_dfs *dfs)
{
dfs->dfs_nol_ie_bitmap = 0;
}
#ifdef CONFIG_CHAN_FREQ_API
bool dfs_process_nol_ie_bitmap(struct wlan_dfs *dfs, uint8_t nol_ie_bandwidth,
uint16_t nol_ie_startfreq, uint8_t nol_ie_bitmap)
{
uint8_t num_subchans;
uint8_t bits = 0x01;
uint16_t radar_subchans[NUM_CHANNELS_160MHZ];
bool should_nol_ie_be_sent = true;
qdf_mem_zero(radar_subchans, sizeof(radar_subchans));
if (!dfs->dfs_use_nol_subchannel_marking) {
/* Since subchannel marking is disabled, disregard
* NOL IE and set NOL IE flag as false, so it
* can't be sent to uplink.
*/
num_subchans =
dfs_get_bonding_channels_for_freq(dfs,
dfs->dfs_curchan,
SEG_ID_PRIMARY,
DETECTOR_ID_0,
radar_subchans);
should_nol_ie_be_sent = false;
} else {
/* Add the NOL IE information in DFS structure so that RCSA
* and NOL IE can be sent to uplink if uplink exists.
*/
uint32_t frequency = (uint32_t)nol_ie_startfreq;
dfs->dfs_nol_ie_bandwidth = nol_ie_bandwidth;
dfs->dfs_nol_ie_startfreq = nol_ie_startfreq;
dfs->dfs_nol_ie_bitmap = nol_ie_bitmap;
for (num_subchans = 0; num_subchans < NUM_CHANNELS_160MHZ;
num_subchans++) {
if (nol_ie_bitmap & bits) {
radar_subchans[num_subchans] = frequency;
}
bits <<= 1;
frequency += nol_ie_bandwidth;
}
}
dfs_radar_add_channel_list_to_nol_for_freq(dfs, radar_subchans,
num_subchans);
return should_nol_ie_be_sent;
}
#endif
#ifdef CONFIG_CHAN_FREQ_API
QDF_STATUS dfs_process_radar_ind(struct wlan_dfs *dfs,
struct radar_found_info *radar_found)
{
bool wait_for_csa = false;
uint16_t freq_list[NUM_CHANNELS_160MHZ];
uint8_t num_channels;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
uint32_t freq_center;
uint32_t radarfound_freq;
struct dfs_channel *dfs_curchan;
/* Acquire a lock to avoid initiating mode switch till radar
* processing is completed.
*/
DFS_RADAR_MODE_SWITCH_LOCK(dfs);
/* Before processing radar, check if HW mode switch is in progress.
* If in progress, defer the processing of radar event received till
* the mode switch is completed.
*/
if (dfs_is_hw_mode_switch_in_progress(dfs)) {
struct radar_found_info *radar_params = NULL;
radar_params = qdf_mem_malloc(sizeof(*radar_params));
if (!radar_params)
goto exit;
/* If CAC timer is running, cancel it here rather than
* after processing to avoid handling unnecessary CAC timeouts.
*/
if (dfs->dfs_cac_timer_running)
dfs_cac_stop(dfs);
/* If CAC timer is to be handled after mode switch and then
* we receive radar, no point in handling CAC completion.
*/
if (dfs->dfs_defer_params.is_cac_completed)
dfs->dfs_defer_params.is_cac_completed = false;
qdf_mem_copy(radar_params, radar_found, sizeof(*radar_params));
dfs->dfs_defer_params.radar_params = radar_params;
dfs->dfs_defer_params.is_radar_detected = true;
status = QDF_STATUS_SUCCESS;
goto exit;
}
dfs_curchan = dfs->dfs_curchan;
if (!dfs_curchan) {
dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs->dfs_curchan is NULL");
goto exit;
}
/* Check if the current channel is a non DFS channel
* If the current channel is non-DFS and the radar is from Agile
* Detector we need to process it since Agile Detector has a
* different channel.
*/
if (!dfs_radarevent_basic_sanity(dfs, dfs_curchan) &&
!(radar_found->detector_id == AGILE_DETECTOR_ID)) {
dfs_err(dfs, WLAN_DEBUG_DFS,
"radar event on a non-DFS channel");
goto exit;
}
/* Sanity checks for radar on Agile detector */
if (radar_found->detector_id == AGILE_DETECTOR_ID &&
(!dfs_is_agile_precac_enabled(dfs) || !dfs->dfs_agile_precac_freq_mhz))
{
dfs_err(dfs, WLAN_DEBUG_DFS,
"radar on Agile detector when ADFS is not running");
goto exit;
}
/* For Full Offload, FW sends segment id,freq_offset and chirp
* information and gets assigned when there is radar detect. In
* case of radartool bangradar enhanced command and real radar
* for DA and PO, we assign these information here.
*/
if (!(dfs->dfs_is_offload_enabled && dfs->dfs_radar_found_for_fo)) {
radar_found->segment_id = dfs->dfs_seg_id;
radar_found->freq_offset = dfs->dfs_freq_offset;
radar_found->is_chirp = dfs->dfs_is_chirp;
}
dfs_compute_radar_found_cfreq(dfs, radar_found, &freq_center);
radarfound_freq = freq_center + radar_found->freq_offset;
if (radar_found->detector_id == AGILE_DETECTOR_ID)
dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
"Radar found on Agile detector freq=%d radar freq=%d",
freq_center, radarfound_freq);
else if (radar_found->segment_id == SEG_ID_SECONDARY)
dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
"Radar found on second segment.Radarfound Freq=%d MHz.Secondary Chan cfreq=%d MHz.",
radarfound_freq, freq_center);
else
dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS,
"Radar found on channel=%d, freq=%d MHz. Primary beaconning chan:%d, freq=%d MHz.",
utils_dfs_freq_to_chan(radarfound_freq),
radarfound_freq, dfs_curchan->dfs_ch_ieee,
dfs_curchan->dfs_ch_freq);
utils_dfs_deliver_event(dfs->dfs_pdev_obj, radarfound_freq,
WLAN_EV_RADAR_DETECTED);
if (!dfs->dfs_use_nol) {
dfs_reset_bangradar(dfs);
dfs_send_csa_to_current_chan(dfs);
status = QDF_STATUS_SUCCESS;
goto exit;
}
if (dfs->dfs_bangradar_type == DFS_BANGRADAR_FOR_ALL_SUBCHANS)
num_channels =
dfs_get_bonding_channel_without_seg_info_for_freq
(dfs_curchan, freq_list);
/* BW reduction is dependent on subchannel marking */
else if ((dfs->dfs_use_nol_subchannel_marking) &&
(!(dfs->dfs_bangradar_type) ||
(dfs->dfs_bangradar_type ==
DFS_BANGRADAR_FOR_SPECIFIC_SUBCHANS)))
num_channels =
dfs_find_radar_affected_subchans_for_freq(dfs,
radar_found,
freq_list,
freq_center);
else
num_channels = dfs_get_bonding_channels_for_freq
(dfs, dfs_curchan, radar_found->segment_id,
radar_found->detector_id, freq_list);
dfs_reset_bangradar(dfs);
status = dfs_radar_add_channel_list_to_nol_for_freq(dfs,
freq_list,
num_channels);
if (QDF_IS_STATUS_ERROR(status)) {
dfs_err(dfs, WLAN_DEBUG_DFS,
"radar event received on invalid channel");
goto exit;
}
dfs->dfs_is_nol_ie_sent = false;
(dfs->is_radar_during_precac ||
radar_found->detector_id == AGILE_DETECTOR_ID) ?
(dfs->dfs_is_rcsa_ie_sent = false) :
(dfs->dfs_is_rcsa_ie_sent = true);
if (dfs->dfs_use_nol_subchannel_marking) {
dfs_reset_nol_ie_bitmap(dfs);
dfs_prepare_nol_ie_bitmap_for_freq(dfs, radar_found, freq_list,
num_channels);
dfs->dfs_is_nol_ie_sent = true;
}
/*
* If precac is running and the radar found in secondary
* VHT80 mark the channel as radar and add to NOL list.
* Otherwise random channel selection can choose this
* channel.
*/
dfs_debug(dfs, WLAN_DEBUG_DFS,
"found_on_second=%d is_pre=%d",
dfs->is_radar_found_on_secondary_seg,
dfs_is_precac_timer_running(dfs));
/*
* Even if radar found on primary, we need to mark the channel as NOL
* in preCAC list. The preCAC list also maintains the current CAC
* channels as part of pre-cleared DFS. Hence call the API
* to mark channels as NOL irrespective of preCAC being enabled or not.
*/
dfs_debug(dfs, WLAN_DEBUG_DFS,
"%s: %d Radar found on dfs detector:%d",
__func__, __LINE__, radar_found->detector_id);
dfs_mark_precac_nol_for_freq(dfs,
dfs->is_radar_found_on_secondary_seg,
radar_found->detector_id,
freq_list,
num_channels);
/*
* This calls into the umac DFS code, which sets the umac
* related radar flags and begins the channel change
* machinery.
* Even during precac, this API is called, but with a flag
* saying not to send RCSA, but only the radar affected subchannel
* information.
*/
dfs_mlme_start_rcsa(dfs->dfs_pdev_obj, &wait_for_csa);
/* If radar is found on preCAC or Agile CAC, return here since
* channel change is not required.
*/
if (radar_found->detector_id == AGILE_DETECTOR_ID)
goto exit;
if (!dfs->dfs_is_offload_enabled &&
dfs->is_radar_found_on_secondary_seg) {
dfs_second_segment_radar_disable(dfs);
dfs->is_radar_found_on_secondary_seg = 0;
if (dfs->is_radar_during_precac) {
dfs->is_radar_during_precac = 0;
goto exit;
}
}
/*
* XXX TODO: the umac NOL code isn't used, but
* WLAN_CHAN_DFS_RADAR still gets set. Since the umac
* NOL code isn't used, that flag is never cleared. This
* needs to be fixed. See EV 105776.
*/
if (wait_for_csa)
goto exit;
/*
* EV 129487 : We have detected radar in the channel,
* stop processing PHY error data as this can cause
* false detect in the new channel while channel
* change is in progress.
*/
if (!dfs->dfs_is_offload_enabled) {
dfs_radar_disable(dfs);
dfs_second_segment_radar_disable(dfs);
}
dfs_mlme_mark_dfs_for_freq(dfs->dfs_pdev_obj,
dfs->dfs_curchan->dfs_ch_ieee,
dfs->dfs_curchan->dfs_ch_freq,
dfs->dfs_curchan->dfs_ch_mhz_freq_seg2,
dfs->dfs_curchan->dfs_ch_flags);
exit:
DFS_RADAR_MODE_SWITCH_UNLOCK(dfs);
return status;
}
#endif