qcacmn: Check Agile span overlaps with operating band

Check whether Agile span overlaps with the current
operating span. This check is done at the time of
start scan and Spectral parameter setting.

CRs-Fixed: 2521554
Change-Id: I9f28ca0a02bd5abf6acdff89301a0c9343e34887
diff --git a/target_if/spectral/target_if_spectral.c b/target_if/spectral/target_if_spectral.c
index d819864..f0407fe 100644
--- a/target_if/spectral/target_if_spectral.c
+++ b/target_if/spectral/target_if_spectral.c
@@ -27,10 +27,10 @@
 #include <wlan_osif_priv.h>
 #include <init_deinit_lmac.h>
 #include <reg_services_public_struct.h>
-#include <reg_services_public_struct.h>
 #include <target_if_spectral_sim.h>
 #include <target_if.h>
 #include <qdf_module.h>
+#include <wlan_reg_services_api.h>
 
 /**
  * @spectral_ops - Spectral function table, holds the Spectral functions that
@@ -2152,6 +2152,140 @@
 	return;
 }
 
+/* target_if_spectral_find_agile_width() - Given a channel width enum, find the
+ *                          corresponding translation for Agile channel width.
+ *                          Translation schema of different operating modes:
+ *                          20 -> 20, 40 -> 40, (80 & 160 & 80_80) -> 80.
+ * @chwidth: Channel width enum.
+ *
+ * Return: The translated channel width enum.
+ */
+static enum phy_ch_width
+target_if_spectral_find_agile_width(enum phy_ch_width chwidth)
+{
+	switch (chwidth) {
+	case CH_WIDTH_20MHZ:
+		return CH_WIDTH_20MHZ;
+	case CH_WIDTH_40MHZ:
+		return CH_WIDTH_40MHZ;
+	case CH_WIDTH_80MHZ:
+	case CH_WIDTH_80P80MHZ:
+	case CH_WIDTH_160MHZ:
+		return CH_WIDTH_80MHZ;
+	default:
+		spectral_err("Invalid chwidth enum %d", chwidth);
+		return CH_WIDTH_INVALID;
+	}
+}
+
+/**
+ * target_if_is_agile_span_overlap_with_operating_span() - Helper routine to
+ * check whether agile span overlaps with current operating band.
+ *
+ * @spectral: Pointer to Spectral object
+ * @ss_frequency: Agile span center frequency
+ * @is_overlapping: Indicates whether Agile span overlaps with operating span
+ *
+ * Helper routine to check whether agile span overlaps with current
+ * operating band.
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+target_if_is_agile_span_overlap_with_operating_span
+			(struct target_if_spectral *spectral,
+			 uint32_t ss_frequency,
+			 bool *is_overlapping)
+{
+	uint32_t chan_num;
+	enum phy_ch_width ch_width;
+	enum phy_ch_width agile_ch_width;
+	const struct bonded_channel *bonded_chan_ptr = NULL;
+	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_pdev *pdev;
+	int16_t chan_freq;
+	uint32_t op_start_freq;
+	uint32_t op_end_freq;
+	uint32_t agile_start_freq;
+	uint32_t agile_end_freq;
+	uint32_t cfreq2;
+
+	if (!spectral) {
+		spectral_err("Spectral object is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	pdev  = spectral->pdev_obj;
+	if (!pdev) {
+		spectral_err("pdev object is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!is_overlapping) {
+		spectral_err("Argument(is_overlapping) is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	vdev = target_if_spectral_get_vdev(spectral);
+	if (!vdev) {
+		spectral_err("vdev is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+	ch_width = target_if_vdev_get_ch_width(vdev);
+	chan_freq = target_if_vdev_get_chan_freq(vdev);
+	cfreq2 = target_if_vdev_get_chan_freq_seg2(vdev);
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_SPECTRAL_ID);
+	if (cfreq2 < 0)
+		return QDF_STATUS_E_FAILURE;
+
+	chan_num = wlan_reg_freq_to_chan(pdev, chan_freq);
+
+	if (ch_width == CH_WIDTH_20MHZ) {
+		op_start_freq = chan_freq - 10;
+		op_end_freq = chan_freq + 10;
+	} else {
+		wlan_reg_get_5g_bonded_channel_and_state
+			(pdev, chan_num, ch_width, &bonded_chan_ptr);
+		if (!bonded_chan_ptr) {
+			spectral_err("Bonded channel is not found");
+			return QDF_STATUS_E_FAILURE;
+		}
+		op_start_freq =
+			wlan_reg_chan_to_freq(pdev,
+					      bonded_chan_ptr->start_ch) - 10;
+		op_end_freq =
+			wlan_reg_chan_to_freq(pdev,
+					      bonded_chan_ptr->end_ch) + 10;
+	}
+
+	agile_ch_width = target_if_spectral_find_agile_width(ch_width);
+	if (agile_ch_width == CH_WIDTH_INVALID)
+		return QDF_STATUS_E_FAILURE;
+	agile_start_freq = ss_frequency -
+				(wlan_reg_get_bw_value(agile_ch_width) >> 1);
+	agile_end_freq = ss_frequency +
+				(wlan_reg_get_bw_value(agile_ch_width) >> 1);
+	if (agile_end_freq <= op_start_freq || op_end_freq <= agile_start_freq)
+		*is_overlapping = false;
+	else
+		*is_overlapping = true;
+
+	/* Use non zero cfreq2 to identify 80p80 */
+	if (cfreq2) {
+		uint32_t sec80_start_feq;
+		uint32_t sec80_end_freq;
+
+		sec80_start_feq = cfreq2 - 40;
+		sec80_end_freq = cfreq2 + 40;
+
+		if ((agile_end_freq > sec80_start_feq) &&
+		    (sec80_end_freq > agile_start_freq))
+			*is_overlapping = true;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
 /**
  * _target_if_set_spectral_config() - Set spectral config
  * @spectral:       Pointer to spectral object
@@ -2173,6 +2307,8 @@
 	struct spectral_config params;
 	struct target_if_spectral_ops *p_sops;
 	struct spectral_config *sparams;
+	QDF_STATUS status;
+	bool is_overlapping;
 
 	if (!err) {
 		spectral_err("Error code argument is null");
@@ -2275,6 +2411,16 @@
 		sparams->ss_chn_mask = value;
 		break;
 	case SPECTRAL_PARAM_FREQUENCY:
+		status = target_if_is_agile_span_overlap_with_operating_span
+				(spectral, value, &is_overlapping);
+		if (QDF_IS_STATUS_ERROR(status))
+			return QDF_STATUS_E_FAILURE;
+
+		if (is_overlapping) {
+			spectral_err("Agile span overlapping with current BW");
+			*err = SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE;
+			return QDF_STATUS_E_FAILURE;
+		}
 		sparams->ss_frequency = value;
 		break;
 	}
@@ -2815,6 +2961,26 @@
 		return QDF_STATUS_E_FAILURE;
 	}
 
+	if (smode == SPECTRAL_SCAN_MODE_AGILE) {
+		QDF_STATUS status;
+		bool is_overlapping;
+
+		status = target_if_is_agile_span_overlap_with_operating_span
+				(spectral,
+				 spectral->params[smode].ss_frequency,
+				 &is_overlapping);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			qdf_spin_unlock(&spectral->spectral_lock);
+			return QDF_STATUS_E_FAILURE;
+		}
+
+		if (is_overlapping) {
+			*err = SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE;
+			qdf_spin_unlock(&spectral->spectral_lock);
+			return QDF_STATUS_E_FAILURE;
+		}
+	}
+
 	target_if_spectral_scan_enable_params(spectral,
 					      &spectral->params[smode], smode,
 					      err);