qcacmn: Enhance DCS event handler to support AWGN event

Handle DCS-AWGN event and take actions accordingly:
For STA mode, if the interference locates on primary 20MHz,
disconnect from AP; otherwise, find a max interference free width
and trigger channel change.
For SAP mode, if the interference locates on primary 20MHz,
try to select a random interference free channel and trigger channel
change (stop SAP if no channel is selected); otherwise, find a max
interference free width and trigger channel change.

Change-Id: I33e79f454b459b12ad84f8b0f3259403037464ee
CRs-Fixed: 2958675
This commit is contained in:
Yu Wang 2021-06-01 12:03:35 +08:00 committed by Madan Koyyalamudi
parent 61d75799c8
commit 98a3fceeb3
9 changed files with 806 additions and 47 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, 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
@ -41,7 +41,7 @@ static int target_if_dcs_interference_event_handler(ol_scn_t scn,
uint32_t datalen)
{
QDF_STATUS status;
struct dcs_stats_event ev;
struct wlan_host_dcs_event ev;
struct wlan_objmgr_psoc *psoc;
struct wmi_unified *wmi_handle;
struct wlan_target_if_dcs_rx_ops *rx_ops;
@ -70,17 +70,25 @@ static int target_if_dcs_interference_event_handler(ol_scn_t scn,
if (wmi_extract_dcs_interference_type(wmi_handle, data,
&ev.dcs_param) !=
QDF_STATUS_SUCCESS) {
QDF_STATUS_SUCCESS) {
target_if_err("Unable to extract dcs interference type");
return -EINVAL;
}
if (wmi_extract_dcs_im_tgt_stats(wmi_handle, data, &ev.wlan_stat) !=
QDF_STATUS_SUCCESS) {
if (ev.dcs_param.interference_type == WLAN_HOST_DCS_WLANIM &&
wmi_extract_dcs_im_tgt_stats(wmi_handle, data, &ev.wlan_stat) !=
QDF_STATUS_SUCCESS) {
target_if_err("Unable to extract WLAN IM stats");
return -EINVAL;
}
if (ev.dcs_param.interference_type == WLAN_HOST_DCS_AWGNIM &&
wmi_extract_dcs_awgn_info(wmi_handle, data, &ev.awgn_info) !=
QDF_STATUS_SUCCESS) {
target_if_err("Unable to extract AWGN info");
return -EINVAL;
}
status = rx_ops->process_dcs_event(psoc, &ev);
return qdf_status_to_os_return(status);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, 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
@ -25,6 +25,11 @@
#include <target_if_dcs.h>
#include "wlan_dcs.h"
#include <wlan_objmgr_psoc_obj_i.h>
#include "wlan_utility.h"
#ifdef WLAN_POLICY_MGR_ENABLE
#include "wlan_policy_mgr_api.h"
#endif
struct dcs_pdev_priv_obj *
wlan_dcs_get_pdev_private_obj(struct wlan_objmgr_psoc *psoc, uint32_t pdev_id)
@ -593,7 +598,7 @@ void wlan_dcs_disable_timer_fn(void *dcs_timer_args)
*/
static void wlan_dcs_frequency_control(struct wlan_objmgr_psoc *psoc,
struct dcs_pdev_priv_obj *dcs_pdev_priv,
struct dcs_stats_event *event)
struct wlan_host_dcs_event *event)
{
struct dcs_psoc_priv_obj *dcs_psoc_priv;
struct pdev_dcs_freq_ctrl_params *dcs_freq_ctrl_params;
@ -667,8 +672,659 @@ static void wlan_dcs_frequency_control(struct wlan_objmgr_psoc *psoc,
}
}
/**
* wlan_dcs_switch_chan() - switch channel for vdev
* @vdev: vdev ptr
* @tgt_freq: target frequency
* @tgt_width: target channel width
*
* Return: QDF_STATUS
*/
static QDF_STATUS
wlan_dcs_switch_chan(struct wlan_objmgr_vdev *vdev, qdf_freq_t tgt_freq,
enum phy_ch_width tgt_width)
{
struct wlan_objmgr_psoc *psoc;
struct dcs_psoc_priv_obj *dcs_psoc_priv;
dcs_switch_chan_cb switch_chan_cb;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc)
return QDF_STATUS_E_INVAL;
dcs_psoc_priv = wlan_objmgr_psoc_get_comp_private_obj(psoc,
WLAN_UMAC_COMP_DCS);
if (!dcs_psoc_priv)
return QDF_STATUS_E_INVAL;
switch_chan_cb = dcs_psoc_priv->switch_chan_cb;
if (!switch_chan_cb)
return QDF_STATUS_E_NOSUPPORT;
return switch_chan_cb(vdev, tgt_freq, tgt_width);
}
#ifdef WLAN_POLICY_MGR_ENABLE
/**
* wlan_dcs_get_pcl_for_sap() - get preferred channel list for SAP
* @vdev: vdev ptr
* @freq_list: Pointer to PCL
* @freq_list_sz: Max size of PCL
*
* Return: number of channels in PCL
*/
static uint32_t wlan_dcs_get_pcl_for_sap(struct wlan_objmgr_vdev *vdev,
qdf_freq_t *freq_list,
uint32_t freq_list_sz)
{
struct wlan_objmgr_psoc *psoc;
struct wlan_objmgr_pdev *pdev;
struct policy_mgr_pcl_list *pcl;
qdf_freq_t freq;
enum channel_state state;
QDF_STATUS status;
int i, j;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc)
return 0;
pdev = wlan_vdev_get_pdev(vdev);
if (!pdev)
return 0;
pcl = qdf_mem_malloc(sizeof(*pcl));
if (!pcl)
return 0;
status = policy_mgr_get_pcl_for_vdev_id(psoc,
PM_SAP_MODE,
pcl->pcl_list, &pcl->pcl_len,
pcl->weight_list,
QDF_ARRAY_SIZE(pcl->weight_list),
wlan_vdev_get_id(vdev));
if (QDF_IS_STATUS_ERROR(status) || !pcl->pcl_len) {
qdf_mem_free(pcl);
return 0;
}
for (i = 0, j = 0; i < pcl->pcl_len && i < freq_list_sz; i++) {
freq = (qdf_freq_t)pcl->pcl_list[i];
state = wlan_reg_get_channel_state_for_freq(pdev, freq);
if (state != CHANNEL_STATE_ENABLE)
continue;
freq_list[j++] = freq;
}
qdf_mem_free(pcl);
return j;
}
#else
static uint32_t wlan_dcs_get_pcl_for_sap(struct wlan_objmgr_vdev *vdev,
qdf_freq_t *freq_list,
uint32_t freq_list_sz)
{
struct wlan_objmgr_pdev *pdev;
struct regulatory_channel *cur_chan_list;
qdf_freq_t freq;
enum channel_state state;
int i, j;
pdev = wlan_vdev_get_pdev(vdev);
if (!pdev)
return 0;
cur_chan_list = qdf_mem_malloc(NUM_CHANNELS *
sizeof(struct regulatory_channel));
if (!cur_chan_list)
return 0;
if (wlan_reg_get_current_chan_list(pdev, cur_chan_list) !=
QDF_STATUS_SUCCESS) {
qdf_mem_free(cur_chan_list);
return 0;
}
for (i = 0, j = 0; i < NUM_CHANNELS && i < freq_list_sz; i++) {
freq = cur_chan_list[i].center_freq;
state = wlan_reg_get_channel_state_for_freq(pdev, freq);
if (state != CHANNEL_STATE_ENABLE)
continue;
freq_list[j++] = freq;
}
qdf_mem_free(cur_chan_list);
return j;
}
#endif
/**
* wlan_dcs_awgn_get_intf_for_seg() - get interference for specified segment
* @awgn_info: awgn info pointer
* @segment: segment index in channel band
*
* This function extracts the information from awgn event and check interference
* within the specified segment.
*
* Return: true if interference is found within the segment, false otherwise.
*/
static bool
wlan_dcs_awgn_get_intf_for_seg(struct wlan_host_dcs_awgn_info *awgn_info,
uint32_t segment)
{
uint32_t seg_mask;
switch (segment) {
case WLAN_DCS_SEG_PRI20:
seg_mask = WLAN_DCS_SEG_PRI20_MASK;
break;
case WLAN_DCS_SEG_SEC20:
seg_mask = WLAN_DCS_SEG_SEC20_MASK;
break;
case WLAN_DCS_SEG_SEC40:
seg_mask = WLAN_DCS_SEG_SEC40_MASK;
break;
case WLAN_DCS_SEG_SEC80:
seg_mask = WLAN_DCS_SEG_SEC80_MASK;
break;
case WLAN_DCS_SEG_SEC160:
seg_mask = WLAN_DCS_SEG_SEC160_MASK;
break;
default:
seg_mask = 0xFFFFFFFF;
break;
}
return (awgn_info->chan_bw_intf_bitmap & seg_mask);
}
/**
* wlan_dcs_get_max_seg_idx() - get max segment index for channel width
* @width: channel width
*
* Return: max segment index(enum wlan_dcs_chan_seg) for the channel width.
*/
static enum wlan_dcs_chan_seg wlan_dcs_get_max_seg_idx(enum phy_ch_width width)
{
switch (width) {
case CH_WIDTH_160MHZ: /* fallthrough */
case CH_WIDTH_80P80MHZ:
return WLAN_DCS_SEG_SEC80;
case CH_WIDTH_80MHZ:
return WLAN_DCS_SEG_SEC40;
case CH_WIDTH_40MHZ:
return WLAN_DCS_SEG_SEC20;
case CH_WIDTH_20MHZ:
return WLAN_DCS_SEG_PRI20;
default:
dcs_err("Invalid ch width %d", width);
return WLAN_DCS_SEG_INVALID;
}
}
/**
* wlan_dcs_get_chan_width_for_seg() - get channel width for specified segment
* @seg_idx: segment index
*
* Return: channel width for segment index
*/
static enum phy_ch_width
wlan_dcs_get_chan_width_for_seg(enum wlan_dcs_chan_seg seg_idx)
{
switch (seg_idx) {
case WLAN_DCS_SEG_SEC80:
return CH_WIDTH_160MHZ;
case WLAN_DCS_SEG_SEC40:
return CH_WIDTH_80MHZ;
case WLAN_DCS_SEG_SEC20:
return CH_WIDTH_40MHZ;
case WLAN_DCS_SEG_PRI20:
return CH_WIDTH_20MHZ;
default:
dcs_err("Invalid seg idx %d", seg_idx);
return CH_WIDTH_INVALID;
}
}
/**
* wlan_dcs_get_max_no_intf_bw() - get max no interference band width
* @awgn_info: pointer to awgn info
* @width: pointer to channel width
*
* This function trys to get max no interference band width according to
* awgn event.
*
* Return: true if valid no interference band width is found, false otherwise.
*/
static bool
wlan_dcs_get_max_no_intf_bw(struct wlan_host_dcs_awgn_info *awgn_info,
enum phy_ch_width *width)
{
enum wlan_dcs_chan_seg seg_idx, max_seg_idx;
max_seg_idx = wlan_dcs_get_max_seg_idx(awgn_info->channel_width);
if (max_seg_idx == WLAN_DCS_SEG_INVALID)
return false;
seg_idx = WLAN_DCS_SEG_PRI20;
while (seg_idx <= max_seg_idx) {
if (wlan_dcs_awgn_get_intf_for_seg(awgn_info, seg_idx)) {
dcs_debug("Intf found for seg idx %d", seg_idx);
break;
}
seg_idx++;
}
/* scroll back to the last no-intf idx */
seg_idx--;
if (seg_idx == WLAN_DCS_SEG_INVALID) {
/* If pri20 contains interference, do full channel change */
dcs_debug("Primary 20MHz Channel interference detected");
return false;
}
*width = wlan_dcs_get_chan_width_for_seg(seg_idx);
if (*width == CH_WIDTH_160MHZ &&
awgn_info->channel_width == CH_WIDTH_80P80MHZ)
*width = CH_WIDTH_80P80MHZ;
dcs_debug("Found the max no intf width %d", *width);
return (*width != CH_WIDTH_INVALID);
}
/**
* wlan_dcs_get_available_chan_for_bw() - get available channel for specified
* band width
* @pdev: pdev ptr
* @awgn_info: pointer to awgn info
* @bw: channel width
* @freq_list: List of preferred channels
* @freq_num: Number of channels in the PCL
* @random: request for random channel
*
* Return: the selected channel frequency, 0 if no available chan is found.
*/
static qdf_freq_t
wlan_dcs_get_available_chan_for_bw(struct wlan_objmgr_pdev *pdev,
struct wlan_host_dcs_awgn_info *awgn_info,
enum phy_ch_width bw, qdf_freq_t *freq_list,
uint32_t freq_num, bool random)
{
int i, j = 0;
uint32_t random_chan_idx;
qdf_freq_t freq, selected_freq = 0;
const struct bonded_channel_freq *bonded_chan_ptr = NULL;
enum channel_state state;
uint16_t chan_cfreq;
bool is_safe = true;
if (!freq_list || !freq_num)
return selected_freq;
for (i = 0; i < freq_num; i++) {
if (j && !random) {
selected_freq = freq_list[0];
dcs_debug("get the first available freq %u for bw %u",
selected_freq, bw);
break;
}
freq = freq_list[i];
if (!WLAN_REG_IS_SAME_BAND_FREQS(freq, awgn_info->center_freq))
continue;
/**
* DFS channel may need CAC during restart, which costs time
* and may cause failure.
*/
if (wlan_reg_is_dfs_for_freq(pdev, freq)) {
dcs_debug("skip dfs freq %u", freq);
continue;
}
if (bonded_chan_ptr &&
freq >= bonded_chan_ptr->start_freq &&
freq <= bonded_chan_ptr->end_freq) {
if (is_safe) {
dcs_debug("add freq directly [%d] = %u",
j, freq);
freq_list[j++] = freq;
}
continue;
}
state = wlan_reg_get_5g_bonded_channel_and_state_for_freq(
pdev, freq, bw, &bonded_chan_ptr);
if (state != CHANNEL_STATE_ENABLE)
continue;
/* no bonding channel for 20MHz */
if (bw == CH_WIDTH_20MHZ) {
if (WLAN_DCS_IS_FREQ_IN_WIDTH(awgn_info->center_freq,
awgn_info->center_freq0,
awgn_info->center_freq1,
awgn_info->channel_width,
freq))
continue;
dcs_debug("add freq[%d] = %u", j, freq);
freq_list[j++] = freq;
continue;
}
is_safe = true;
chan_cfreq = bonded_chan_ptr->start_freq;
while (chan_cfreq <= bonded_chan_ptr->end_freq) {
if (WLAN_DCS_IS_FREQ_IN_WIDTH(awgn_info->center_freq,
awgn_info->center_freq0,
awgn_info->center_freq1,
awgn_info->channel_width,
chan_cfreq)) {
is_safe = false;
break;
}
chan_cfreq = chan_cfreq + 20;
}
if (is_safe) {
dcs_debug("add freq[%d] = %u", j, freq);
freq_list[j++] = freq;
}
}
if (j && random) {
qdf_get_random_bytes(&random_chan_idx, sizeof(random_chan_idx));
random_chan_idx = random_chan_idx % j;
selected_freq = freq_list[random_chan_idx];
dcs_debug("get freq[%d] = %u for bw %u",
random_chan_idx, selected_freq, bw);
}
return selected_freq;
}
/**
* wlan_dcs_sap_get_available_chan() - get available channel for sap
* @vdev: vdev ptr
* @awgn_info: pointer to awgn info
* @tgt_freq: frequency of the selected channel
* @tgt_width: band width of the selected channel
* @random: request for random channel
*
* This function trys to get no-interference chan with max possible bandwidth
* from pcl for sap according to awgn info.
*
* Return: true if available channel is found, false otherwise.
*/
static bool
wlan_dcs_sap_select_chan(struct wlan_objmgr_vdev *vdev,
struct wlan_host_dcs_awgn_info *awgn_info,
qdf_freq_t *tgt_freq, enum phy_ch_width *tgt_width,
bool random)
{
int32_t tmp_width;
qdf_freq_t tmp_freq = 0;
struct wlan_objmgr_pdev *pdev;
qdf_freq_t *freq_list;
uint32_t freq_num;
freq_list = qdf_mem_malloc(sizeof(*freq_list) * NUM_CHANNELS);
if (!freq_list)
return false;
freq_num = wlan_dcs_get_pcl_for_sap(vdev, freq_list, NUM_CHANNELS);
if (!freq_num) {
qdf_mem_free(freq_list);
return false;
}
tmp_width = awgn_info->channel_width;
pdev = wlan_vdev_get_pdev(vdev);
if (!pdev) {
qdf_mem_free(freq_list);
return false;
}
while (tmp_width >= CH_WIDTH_20MHZ) {
tmp_freq = wlan_dcs_get_available_chan_for_bw(pdev, awgn_info,
tmp_width,
freq_list,
freq_num,
random);
if (tmp_freq)
break;
tmp_width--;
}
if (tmp_freq) {
*tgt_width = tmp_width;
*tgt_freq = tmp_freq;
dcs_debug("new_width: %d new_freq %u", tmp_width, tmp_freq);
qdf_mem_free(freq_list);
return true;
}
qdf_mem_free(freq_list);
return false;
}
/**
* wlan_dcs_is_awgnim_valid() - validate awgn info
* @awgn_info: pointer to awgn info
*
* Return: true if valid, false otherwise.
*/
static inline bool
wlan_dcs_is_awgnim_valid(struct wlan_host_dcs_awgn_info *awgn_info)
{
return (awgn_info &&
awgn_info->center_freq && awgn_info->chan_bw_intf_bitmap &&
awgn_info->channel_width != CH_WIDTH_INVALID &&
WLAN_REG_IS_6GHZ_CHAN_FREQ(awgn_info->center_freq));
}
/**
* wlan_dcs_vdev_get_op_chan_info() - get operating channel info for vdev
* @vdev: pointer to vdev object
* @cfreq: Center frequency of primary channel
* @cfreq0: Center frequency of segment 1
* @cfreq1: Center frequency of segment 2
* @ch_width: Channel width, enum phy_ch_width
*
* Return: QDF_STATUS
*/
static QDF_STATUS
wlan_dcs_vdev_get_op_chan_info(struct wlan_objmgr_vdev *vdev,
qdf_freq_t *cfreq, qdf_freq_t *cfreq0,
qdf_freq_t *cfreq1, enum phy_ch_width *ch_width)
{
struct wlan_channel *chan;
if (!vdev)
return QDF_STATUS_E_INVAL;
*cfreq = 0;
*cfreq0 = 0;
*cfreq1 = 0;
*ch_width = 0;
if (wlan_vdev_mlme_is_active(vdev) != QDF_STATUS_SUCCESS)
return QDF_STATUS_E_INVAL;
chan = wlan_vdev_get_active_channel(vdev);
if (!chan)
return QDF_STATUS_E_INVAL;
*cfreq = chan->ch_freq;
*cfreq0 = chan->ch_cfreq1;
*cfreq1 = chan->ch_cfreq2;
*ch_width = chan->ch_width;
return QDF_STATUS_SUCCESS;
}
/**
* wlan_dcs_process_awgn_sta() - process AWGN event for STA
* @pdev: pointer to pdev object
* @object: vdev object
* @arg: Arguments to the handler
*
* Return: void
*/
static void wlan_dcs_process_awgn_sta(struct wlan_objmgr_pdev *pdev,
void *object, void *arg)
{
struct wlan_objmgr_vdev *vdev = object;
struct wlan_host_dcs_awgn_info *awgn_info = arg;
enum phy_ch_width ch_width;
enum phy_ch_width tgt_width = CH_WIDTH_INVALID;
qdf_freq_t op_freq, cfreq0, cfreq1;
qdf_freq_t tgt_freq = 0;
QDF_STATUS status;
uint8_t vdev_id;
bool found;
if (!vdev || !pdev)
return;
if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE)
return;
vdev_id = wlan_vdev_get_id(vdev);
status = wlan_dcs_vdev_get_op_chan_info(vdev, &op_freq, &cfreq0,
&cfreq1, &ch_width);
if (QDF_IS_STATUS_ERROR(status))
return;
if (awgn_info->center_freq != op_freq) {
dcs_debug("STA-%d: freq not match", vdev_id);
return;
}
found = wlan_dcs_get_max_no_intf_bw(awgn_info, &tgt_width);
if (found) {
if (ch_width <= tgt_width) {
dcs_debug("STA-%d: freq and bw are unchanged", vdev_id);
return;
}
tgt_freq = op_freq;
}
/* If no width is found, means to disconnect */
dcs_debug("STA-%d: target freq %u width %u",
vdev_id, tgt_freq, tgt_width);
wlan_dcs_switch_chan(vdev, tgt_freq, tgt_width);
}
/**
* wlan_dcs_process_awgn_sap() - process AWGN event for SAP
* @pdev: pointer to pdev object
* @object: vdev object
* @arg: Arguments to the handler
*
* Return: void
*/
static void wlan_dcs_process_awgn_sap(struct wlan_objmgr_pdev *pdev,
void *object, void *arg)
{
struct wlan_objmgr_vdev *vdev = object;
struct wlan_host_dcs_awgn_info *awgn_info = arg;
enum phy_ch_width ch_width;
enum phy_ch_width tgt_width = CH_WIDTH_INVALID;
qdf_freq_t op_freq, cfreq0, cfreq1;
qdf_freq_t tgt_freq = 0;
QDF_STATUS status;
uint8_t vdev_id;
bool found;
if (!vdev || !pdev)
return;
if (wlan_vdev_mlme_get_opmode(vdev) != QDF_SAP_MODE)
return;
vdev_id = wlan_vdev_get_id(vdev);
status = wlan_dcs_vdev_get_op_chan_info(vdev, &op_freq, &cfreq0, &cfreq1, &ch_width);
if (QDF_IS_STATUS_ERROR(status))
return;
if (awgn_info->center_freq != op_freq) {
dcs_debug("SAP-%d: freq not match rpt:%u - op:%u",
vdev_id, awgn_info->center_freq, op_freq);
return;
}
found = wlan_dcs_get_max_no_intf_bw(awgn_info, &tgt_width);
if (found) {
if (ch_width <= tgt_width) {
dcs_debug("SAP-%d: both freq and bw are unchanged",
vdev_id);
return;
}
tgt_freq = op_freq;
} else {
wlan_dcs_sap_select_chan(vdev, awgn_info, &tgt_freq,
&tgt_width, true);
}
/* If no chan is selected, means to stop sap */
dcs_debug("SAP-%d: target freq %u width %u",
vdev_id, tgt_freq, tgt_width);
wlan_dcs_switch_chan(vdev, tgt_freq, tgt_width);
}
/**
* wlan_dcs_awgnim_process() - process awgn IM
* @psoc: psoc ptr
* @pdev_id: pdev id
* @awgn_info: pointer to awgn info
*
* This function triggers channel change for all STAs and SAPs, according
* to AWGN info.
*
* Return: None.
*/
static void
wlan_dcs_awgn_process(struct wlan_objmgr_psoc *psoc, uint8_t pdev_id,
struct wlan_host_dcs_awgn_info *awgn_info)
{
struct wlan_objmgr_pdev *pdev;
if (!wlan_dcs_is_awgnim_valid(awgn_info)) {
dcs_err("Invalid awgnim event");
return;
}
pdev = wlan_objmgr_get_pdev_by_id(psoc, pdev_id, WLAN_DCS_ID);
if (!pdev) {
dcs_err("Invalid pdev id %d", pdev_id);
return;
}
dcs_debug("pdev id %u width %u freq %u freq0 %u fre1 %u bitmap 0x%x",
pdev_id, awgn_info->channel_width, awgn_info->center_freq,
awgn_info->center_freq0, awgn_info->center_freq1,
awgn_info->chan_bw_intf_bitmap);
wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
wlan_dcs_process_awgn_sta,
awgn_info, 0, WLAN_DCS_ID);
wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
wlan_dcs_process_awgn_sap,
awgn_info, 0, WLAN_DCS_ID);
wlan_objmgr_pdev_release_ref(pdev, WLAN_DCS_ID);
}
QDF_STATUS
wlan_dcs_process(struct wlan_objmgr_psoc *psoc, struct dcs_stats_event *event)
wlan_dcs_process(struct wlan_objmgr_psoc *psoc,
struct wlan_host_dcs_event *event)
{
struct dcs_pdev_priv_obj *dcs_pdev_priv;
bool start_dcs_cbk_handler = false;
@ -692,14 +1348,15 @@ wlan_dcs_process(struct wlan_objmgr_psoc *psoc, struct dcs_stats_event *event)
event->dcs_param.interference_type,
event->dcs_param.pdev_id);
if (!dcs_pdev_priv->dcs_host_params.dcs_enable)
return QDF_STATUS_SUCCESS;
switch (event->dcs_param.interference_type) {
case CAP_DCS_CWIM:
case WLAN_HOST_DCS_CWIM:
break;
case CAP_DCS_WLANIM:
if (dcs_pdev_priv->dcs_host_params.dcs_enable & CAP_DCS_WLANIM)
case WLAN_HOST_DCS_WLANIM:
if (!dcs_pdev_priv->dcs_host_params.dcs_enable)
break;
if (dcs_pdev_priv->dcs_host_params.dcs_enable &
WLAN_HOST_DCS_WLANIM)
start_dcs_cbk_handler =
wlan_dcs_wlan_interference_process(
&event->wlan_stat,
@ -716,6 +1373,11 @@ wlan_dcs_process(struct wlan_objmgr_psoc *psoc, struct dcs_stats_event *event)
dcs_pdev_priv,
event);
break;
case WLAN_HOST_DCS_AWGNIM:
/* Skip frequency control for AWGNIM */
wlan_dcs_awgn_process(psoc, event->dcs_param.pdev_id,
&event->awgn_info);
break;
default:
dcs_err("unidentified interference type reported");
break;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, 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
@ -163,14 +163,61 @@ struct dcs_pdev_priv_obj {
int status);
};
/**
* wlan_dcs_chan_seg - Different segments in the channel band.
* @WLAN_DCS_SEG_INVALID: invalid segment
* @WLAN_DCS_SEG_PRI20: primary 20MHz
* @WLAN_DCS_SEG_SEC20: secondary 20MHz
* @WLAN_DCS_SEG_SEC40: secondary 40MHz
* @WLAN_DCS_SEG_SEC80: secondary 80MHz
* @WLAN_DCS_SEG_SEC160: secondary 160MHz
*/
enum wlan_dcs_chan_seg {
WLAN_DCS_SEG_INVALID,
WLAN_DCS_SEG_PRI20,
WLAN_DCS_SEG_SEC20,
WLAN_DCS_SEG_SEC40,
WLAN_DCS_SEG_SEC80,
WLAN_DCS_SEG_SEC160,
};
/* masks for segments */
#define WLAN_DCS_SEG_PRI20_MASK BIT(0)
#define WLAN_DCS_SEG_SEC20_MASK BIT(1)
#define WLAN_DCS_SEG_SEC40_MASK (BIT(2) | BIT(3))
#define WLAN_DCS_SEG_SEC80_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7))
#define WLAN_DCS_SEG_SEC160_MASK (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \
BIT(12) | BIT(13) | BIT(14) | BIT(15))
#define WLAN_DCS_CHAN_FREQ_OFFSET 5
#define WLAN_DCS_IS_FREQ_IN_WIDTH(__cfreq, __cfreq0, __cfreq1, __width, __freq)\
((((__width) == CH_WIDTH_20MHZ) && \
((__cfreq) == (__freq))) || \
(((__width) == CH_WIDTH_40MHZ) && \
(((__freq) >= ((__cfreq0) - (2 * WLAN_DCS_CHAN_FREQ_OFFSET))) && \
((__freq) <= ((__cfreq0) + (2 * WLAN_DCS_CHAN_FREQ_OFFSET))))) || \
(((__width) == CH_WIDTH_80MHZ) && \
(((__freq) >= ((__cfreq0) - (6 * WLAN_DCS_CHAN_FREQ_OFFSET))) && \
((__freq) <= ((__cfreq0) + (6 * WLAN_DCS_CHAN_FREQ_OFFSET))))) || \
(((__width) == CH_WIDTH_160MHZ) && \
(((__freq) >= ((__cfreq1) - (14 * WLAN_DCS_CHAN_FREQ_OFFSET))) && \
((__freq) <= ((__cfreq1) + (14 * WLAN_DCS_CHAN_FREQ_OFFSET))))) || \
(((__width) == CH_WIDTH_80P80MHZ) && \
((((__freq) >= ((__cfreq0) - (6 * WLAN_DCS_CHAN_FREQ_OFFSET))) && \
((__freq) <= ((__cfreq0) + (6 * WLAN_DCS_CHAN_FREQ_OFFSET)))) || \
(((__freq) >= ((__cfreq1) - (6 * WLAN_DCS_CHAN_FREQ_OFFSET))) && \
((__freq) <= ((__cfreq1) + (6 * WLAN_DCS_CHAN_FREQ_OFFSET)))))))
/**
* struct dcs_psoc_priv_obj - define dcs psoc priv
* @dcs_pdev_priv: dcs pdev priv
* @dcs_cbk: dcs callback
* @switch_chan_cb: callback for switching channel
*/
struct dcs_psoc_priv_obj {
struct dcs_pdev_priv_obj dcs_pdev_priv[WLAN_DCS_MAX_PDEVS];
struct psoc_dcs_cbk dcs_cbk;
dcs_switch_chan_cb switch_chan_cb;
};
/**
@ -222,7 +269,7 @@ QDF_STATUS wlan_dcs_cmd_send(struct wlan_objmgr_psoc *psoc,
/**
* wlan_dcs_process() - dcs process main entry
* @psoc: psoc pointer
* @event: dcs stats event pointer
* @event: dcs event pointer
*
* This function is the main entry to do dcs related operation
* such as algorithm handling and dcs frequency control.
@ -230,7 +277,7 @@ QDF_STATUS wlan_dcs_cmd_send(struct wlan_objmgr_psoc *psoc,
* Return: QDF_STATUS
*/
QDF_STATUS wlan_dcs_process(struct wlan_objmgr_psoc *psoc,
struct dcs_stats_event *event);
struct wlan_host_dcs_event *event);
/**
* wlan_dcs_disable_timer_fn() - dcs disable timer callback

View File

@ -21,6 +21,20 @@
#ifndef _WLAN_DCS_PUBLIC_STRUCTS_H_
#define _WLAN_DCS_PUBLIC_STRUCTS_H_
/**
* enum wlan_host_dcs_type - types of DCS interference events
* @WLAN_HOST_DCS_NONE: invalid type
* @WLAN_HOST_DCS_CWIM: continuous wave interference
* @WLAN_HOST_DCS_WLANIM: wlan interference stats
* @WLAN_HOST_DCS_AWGNIM: additive white Gaussian noise (awgn) interference
*/
enum wlan_host_dcs_type {
WLAN_HOST_DCS_NONE = 0, /* 0x0 */
WLAN_HOST_DCS_CWIM = BIT(0), /* 0x1 */
WLAN_HOST_DCS_WLANIM = BIT(1), /* 0x2 */
WLAN_HOST_DCS_AWGNIM = BIT(2), /* 0x4 */
};
/**
* struct wlan_host_dcs_interference_param - dcs interference parameters
* @interference_type: type of DCS interference
@ -119,4 +133,16 @@ struct wlan_host_dcs_awgn_info {
qdf_freq_t center_freq1;
uint32_t chan_bw_intf_bitmap;
};
/**
* struct wlan_host_dcs_event - define dcs event
* @wlan_stat: wlan interference target statistics
* @dcs_param: dcs event param
* @awgn_info: awgn info
*/
struct wlan_host_dcs_event {
struct wlan_host_dcs_im_tgt_stats wlan_stat;
struct wlan_host_dcs_interference_param dcs_param;
struct wlan_host_dcs_awgn_info awgn_info;
};
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, 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
@ -25,26 +25,16 @@
#include <wlan_objmgr_cmn.h>
#include <wlan_dcs_public_structs.h>
/**
* struct dcs_stats_event - define dcs stats event
* @wlan_stat: wlan interference target statistics
* @dcs_param: dcs event param
*/
struct dcs_stats_event {
struct wlan_host_dcs_im_tgt_stats wlan_stat;
struct wlan_host_dcs_interference_param dcs_param;
};
/**
* tgt_dcs_process_event(): dcs FW event process
* @psoc: pointer to psoc object
* @event: pointer to dcs stats event
* @event: pointer to dcs event
*
* This function gets called to process dcs FW event
*
* Return: QDF_STATUS
*/
QDF_STATUS tgt_dcs_process_event(struct wlan_objmgr_psoc *psoc,
struct dcs_stats_event *event);
struct wlan_host_dcs_event *event);
#endif /* __WLAN_DCS_TGT_API_H__ */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, 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
@ -25,16 +25,6 @@
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_dcs_public_structs.h>
/**
* @brief List of DCS capabilities that can be set or unset
* dynamically
* @see UMAC auto channel selection document for details on each feature
*
*/
#define CAP_DCS_CWIM 0x1
#define CAP_DCS_WLANIM 0x2
#define CAP_DCS_MASK (CAP_DCS_CWIM | CAP_DCS_WLANIM)
/**
* typedef dcs_callback() - DCS callback
* @psoc: Pointer to psoc
@ -48,6 +38,16 @@ typedef void (*dcs_callback)(
uint8_t interference_type,
void *arg);
/**
* typedef dcs_switch_chan_cb() - DCS callback for switching channel
* @vdev: Pointer to vdev
* @tgt_freq: target channel frequency
* @tgt_width: target channel width
*/
typedef QDF_STATUS (*dcs_switch_chan_cb)(struct wlan_objmgr_vdev *vdev,
qdf_freq_t tgt_freq,
enum phy_ch_width tgt_width);
#ifdef DCS_INTERFERENCE_DETECTION
/**
* ucfg_dcs_register_cb() - API to register dcs callback
@ -79,6 +79,16 @@ void ucfg_dcs_register_user_cb(struct wlan_objmgr_psoc *psoc,
struct wlan_host_dcs_im_user_stats *stats,
int status));
/**
* ucfg_dcs_register_awgn_cb() - API to register dcs awgn callback
* @psoc: pointer to psoc object
* @cb: dcs switch channel callback to be registered
*
* Return: QDF_STATUS
*/
QDF_STATUS ucfg_dcs_register_awgn_cb(struct wlan_objmgr_psoc *psoc,
dcs_switch_chan_cb cb);
/**
* ucfg_wlan_dcs_cmd(): API to send dcs command
* @psoc: pointer to psoc object
@ -98,7 +108,7 @@ ucfg_wlan_dcs_cmd(struct wlan_objmgr_psoc *psoc,
* ucfg_config_dcs_enable() - API to config dcs enable
* @psoc: pointer to psoc object
* @mac_id: mac id
* @interference_type: CAP_DCS_CWIM, CAP_DCS_WLANIM, CAP_DCS_MASK
* @interference_type: type mask(WLAN_HOST_DCS_CWIM / WLAN_HOST_DCS_WLANIM)
*
* This function gets called to config dcs enable
*
@ -112,7 +122,7 @@ void ucfg_config_dcs_enable(struct wlan_objmgr_psoc *psoc,
* ucfg_config_dcs_disable() - API to config dcs disable
* @psoc: pointer to psoc object
* @mac_id: mac id
* @interference_type: CAP_DCS_CWIM, CAP_DCS_WLANIM, CAP_DCS_MASK
* @interference_type: type mask(WLAN_HOST_DCS_CWIM / WLAN_HOST_DCS_WLANIM)
*
* This function gets called to config dcs disable
*

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, 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
@ -24,7 +24,7 @@
#include "../../core/src/wlan_dcs.h"
QDF_STATUS tgt_dcs_process_event(struct wlan_objmgr_psoc *psoc,
struct dcs_stats_event *event)
struct wlan_host_dcs_event *event)
{
return wlan_dcs_process(psoc, event);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021 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
@ -60,6 +60,22 @@ ucfg_dcs_register_user_cb(struct wlan_objmgr_psoc *psoc,
dcs_pdev_priv->user_cb = cb;
}
QDF_STATUS ucfg_dcs_register_awgn_cb(struct wlan_objmgr_psoc *psoc,
dcs_switch_chan_cb cb)
{
struct dcs_psoc_priv_obj *dcs_psoc_priv;
dcs_psoc_priv =
wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_DCS);
if (!dcs_psoc_priv) {
dcs_err("dcs psoc private object is null");
return QDF_STATUS_E_INVAL;
}
dcs_psoc_priv->switch_chan_cb = cb;
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
ucfg_wlan_dcs_cmd(struct wlan_objmgr_psoc *psoc,
uint32_t mac_id,

View File

@ -210,7 +210,7 @@ struct wlan_target_if_dcs_tx_ops {
*/
struct wlan_target_if_dcs_rx_ops {
QDF_STATUS (*process_dcs_event)(struct wlan_objmgr_psoc *psoc,
struct dcs_stats_event *event);
struct wlan_host_dcs_event *event);
};
#endif