Ver Fonte

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
Yu Wang há 4 anos atrás
pai
commit
98a3fceeb3

+ 13 - 5
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);

+ 671 - 9
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 <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;

+ 50 - 3
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

+ 26 - 0
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

+ 3 - 13
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 <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__ */

+ 23 - 13
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 <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
  *

+ 2 - 2
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);
 }

+ 17 - 1
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,

+ 1 - 1
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