diff --git a/target_if/dcs/src/target_if_dcs.c b/target_if/dcs/src/target_if_dcs.c index 8009388c17..69fd921b30 100644 --- a/target_if/dcs/src/target_if_dcs.c +++ b/target_if/dcs/src/target_if_dcs.c @@ -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); diff --git a/umac/dcs/core/src/wlan_dcs.c b/umac/dcs/core/src/wlan_dcs.c index ecd7d3f661..0e2327adc7 100644 --- a/umac/dcs/core/src/wlan_dcs.c +++ b/umac/dcs/core/src/wlan_dcs.c @@ -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 #include "wlan_dcs.h" +#include +#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; diff --git a/umac/dcs/core/src/wlan_dcs.h b/umac/dcs/core/src/wlan_dcs.h index fed5b5fe6a..6a53a3932b 100644 --- a/umac/dcs/core/src/wlan_dcs.h +++ b/umac/dcs/core/src/wlan_dcs.h @@ -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 diff --git a/umac/dcs/dispatcher/inc/wlan_dcs_public_structs.h b/umac/dcs/dispatcher/inc/wlan_dcs_public_structs.h index 44817a1be0..8b223c5c8c 100644 --- a/umac/dcs/dispatcher/inc/wlan_dcs_public_structs.h +++ b/umac/dcs/dispatcher/inc/wlan_dcs_public_structs.h @@ -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 diff --git a/umac/dcs/dispatcher/inc/wlan_dcs_tgt_api.h b/umac/dcs/dispatcher/inc/wlan_dcs_tgt_api.h index f8cf5976f9..07fcc57000 100644 --- a/umac/dcs/dispatcher/inc/wlan_dcs_tgt_api.h +++ b/umac/dcs/dispatcher/inc/wlan_dcs_tgt_api.h @@ -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 #include -/** - * 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__ */ diff --git a/umac/dcs/dispatcher/inc/wlan_dcs_ucfg_api.h b/umac/dcs/dispatcher/inc/wlan_dcs_ucfg_api.h index a9da6882bc..fe8b84e469 100644 --- a/umac/dcs/dispatcher/inc/wlan_dcs_ucfg_api.h +++ b/umac/dcs/dispatcher/inc/wlan_dcs_ucfg_api.h @@ -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 #include -/** - * @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 * diff --git a/umac/dcs/dispatcher/src/wlan_dcs_tgt_api.c b/umac/dcs/dispatcher/src/wlan_dcs_tgt_api.c index 2b4bd1ec86..9db3d5dd94 100644 --- a/umac/dcs/dispatcher/src/wlan_dcs_tgt_api.c +++ b/umac/dcs/dispatcher/src/wlan_dcs_tgt_api.c @@ -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); } diff --git a/umac/dcs/dispatcher/src/wlan_dcs_ucfg_api.c b/umac/dcs/dispatcher/src/wlan_dcs_ucfg_api.c index 9550102f66..98a1a1ddf9 100644 --- a/umac/dcs/dispatcher/src/wlan_dcs_ucfg_api.c +++ b/umac/dcs/dispatcher/src/wlan_dcs_ucfg_api.c @@ -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, diff --git a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h index 5ffaefcc18..a0474cf355 100644 --- a/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h +++ b/umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h @@ -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