Przeglądaj źródła

Merge "qca-wifi: Add DFS APIs to support Rolling CAC feature"

Linux Build Service Account 5 lat temu
rodzic
commit
ea06d17bec
1 zmienionych plików z 635 dodań i 69 usunięć
  1. 635 69
      umac/dfs/core/src/misc/dfs_zero_cac.c

+ 635 - 69
umac/dfs/core/src/misc/dfs_zero_cac.c

@@ -415,18 +415,19 @@ void dfs_find_chwidth_and_center_chan(struct wlan_dfs *dfs,
 }
 #endif
 
-/* dfs_find_chwidth_and_center_chan_for_freq() - Find channel width and center
- * channel frequency.
+/* dfs_find_curchwidth_and_center_chan_for_freq() - Find channel width and
+ *                                                  center channelfrequency.
  * @dfs: Pointer to wlan_dfs.
  * @chwidth: Pointer to phy_ch_width.
  * @primary_chan_freq: Pointer to primary channel.
  * @secondary_chan_freq: Pointer to secondary channel.
  */
 #ifdef CONFIG_CHAN_FREQ_API
-void dfs_find_chwidth_and_center_chan_for_freq(struct wlan_dfs *dfs,
-					       enum phy_ch_width *chwidth,
-					       uint16_t *primary_chan_freq,
-					       uint16_t *secondary_chan_freq)
+void
+dfs_find_curchwidth_and_center_chan_for_freq(struct wlan_dfs *dfs,
+					     enum phy_ch_width *chwidth,
+					     qdf_freq_t *primary_chan_freq,
+					     qdf_freq_t *secondary_chan_freq)
 {
 	struct dfs_channel *curchan = dfs->dfs_curchan;
 
@@ -781,6 +782,27 @@ void dfs_find_pdev_for_agile_precac(struct wlan_objmgr_pdev *pdev,
 	   (dfs_soc_obj->cur_precac_dfs_index + 1) % dfs_soc_obj->num_dfs_privs;
 }
 
+/*
+ * dfs_fill_adfs_chan_params() - Fill the ADFS FW params.
+ * @dfs: Pointer to wlan_dfs.
+ * @adfs_param: Pointer to struct dfs_agile_cac_params.
+ * @ch_freq: Frequency in MHZ to be programmed to the agile detector.
+ */
+static void dfs_fill_adfs_chan_params(struct wlan_dfs *dfs,
+				      struct dfs_agile_cac_params *adfs_param)
+{
+	qdf_freq_t ch_freq = dfs->dfs_agile_precac_freq_mhz;
+
+	adfs_param->precac_center_freq_1 =
+		(ch_freq == RESTRICTED_80P80_CHAN_CENTER_FREQ) ?
+		(RESTRICTED_80P80_LEFT_80_CENTER_FREQ) : ch_freq;
+	adfs_param->precac_center_freq_2 =
+		(ch_freq == RESTRICTED_80P80_CHAN_CENTER_FREQ) ?
+		(RESTRICTED_80P80_RIGHT_80_CENTER_FREQ) : 0;
+	adfs_param->precac_chan = utils_dfs_freq_to_chan(ch_freq);
+	adfs_param->precac_chwidth = dfs->dfs_precac_chwidth;
+}
+
 /*
  * dfs_prepare_agile_precac_chan() - Prepare an agile channel for preCAC.
  * @dfs: Pointer to wlan_dfs.
@@ -826,10 +848,10 @@ void dfs_prepare_agile_precac_chan(struct wlan_dfs *dfs)
 				vhtop_ch_freq_seg2 +=
 					DFS_160MHZ_SECSEG_CHAN_OFFSET;
 		}
-		dfs_get_ieeechan_for_agilecac_for_freq(temp_dfs,
-						       &ch_freq,
-						       vhtop_ch_freq_seg1,
-						       vhtop_ch_freq_seg2);
+		dfs_set_agilecac_chan_for_freq(temp_dfs,
+					       &ch_freq,
+					       vhtop_ch_freq_seg1,
+					       vhtop_ch_freq_seg2);
 
 		if (!ch_freq) {
 			qdf_info(" %s : %d No preCAC required channels left in current pdev: %pK",
@@ -841,14 +863,7 @@ void dfs_prepare_agile_precac_chan(struct wlan_dfs *dfs)
 	}
 
 	if (ch_freq) {
-		adfs_param.precac_center_freq_1 =
-			(ch_freq == RESTRICTED_80P80_CHAN_CENTER_FREQ) ?
-			(RESTRICTED_80P80_LEFT_80_CENTER_FREQ) : ch_freq;
-		adfs_param.precac_center_freq_2 =
-			(ch_freq == RESTRICTED_80P80_CHAN_CENTER_FREQ) ?
-			(RESTRICTED_80P80_RIGHT_80_CENTER_FREQ) : 0;
-		adfs_param.precac_chan = utils_dfs_freq_to_chan(ch_freq);
-		adfs_param.precac_chwidth = temp_dfs->dfs_precac_chwidth;
+		dfs_fill_adfs_chan_params(temp_dfs, &adfs_param);
 		dfs_start_agile_precac_timer(temp_dfs,
 					     dfs->dfs_soc_obj->ocac_status,
 					     &adfs_param);
@@ -2129,6 +2144,29 @@ static inline bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs)
 }
 #endif
 
+/* dfs_mark_adfs_chan_as_cac_done()- Mark the ADFS CAC completed channel as
+ *                                   CAC done in the precac tree.
+ * @dfs: Pointer to struct wlan_dfs.
+ */
+static void dfs_mark_adfs_chan_as_cac_done(struct wlan_dfs *dfs)
+{
+	qdf_freq_t pri_chan_freq, sec_chan_freq;
+	enum phy_ch_width chan_width;
+
+	if (dfs->dfs_agile_precac_freq_mhz ==
+		RESTRICTED_80P80_CHAN_CENTER_FREQ) {
+		pri_chan_freq = RESTRICTED_80P80_LEFT_80_CENTER_FREQ;
+		sec_chan_freq = RESTRICTED_80P80_RIGHT_80_CENTER_FREQ;
+		chan_width = CH_WIDTH_80P80MHZ;
+	} else {
+		pri_chan_freq = dfs->dfs_agile_precac_freq_mhz;
+		sec_chan_freq = 0;
+		chan_width =  dfs->dfs_precac_chwidth;
+	}
+	dfs_mark_precac_done_for_freq(dfs, pri_chan_freq, sec_chan_freq,
+				      chan_width);
+}
+
 /**
  * dfs_precac_timeout() - Precac timeout.
  *
@@ -2189,20 +2227,7 @@ static os_timer_func(dfs_precac_timeout)
 		     current_time / 1000);
 	    if (dfs_soc_obj->ocac_status == OCAC_SUCCESS) {
 		dfs_soc_obj->ocac_status = OCAC_RESET;
-		if (dfs->dfs_agile_precac_freq_mhz ==
-		    RESTRICTED_80P80_CHAN_CENTER_FREQ) {
-			dfs_mark_precac_done_for_freq(
-				dfs,
-				RESTRICTED_80P80_LEFT_80_CENTER_FREQ,
-				RESTRICTED_80P80_RIGHT_80_CENTER_FREQ,
-				CH_WIDTH_80P80MHZ);
-		} else {
-			dfs_mark_precac_done_for_freq(
-				dfs,
-				dfs->dfs_agile_precac_freq_mhz,
-				0,
-				dfs->dfs_precac_chwidth);
-		}
+		dfs_mark_adfs_chan_as_cac_done(dfs);
 	    }
 	    /* check if CAC done on home channel */
 	    is_cac_done_on_des_chan = dfs_precac_check_home_chan_change(dfs);
@@ -3791,6 +3816,10 @@ void dfs_start_agile_precac_timer(struct wlan_dfs *dfs,
 	/* Add the preCAC timeout in the params to be sent to FW. */
 	adfs_param->min_precac_timeout = min_precac_timeout;
 	adfs_param->max_precac_timeout = max_precac_timeout;
+	/* For preCAC, in which the FW has to run a timer of a finite amount of
+	 * time, set the mode to QUICK_OCAC_MODE.
+	 */
+	adfs_param->ocac_mode = QUICK_OCAC_MODE;
 	/* Increase the preCAC timeout in HOST by 2 seconds to avoid
 	 * FW OCAC completion event and HOST timer firing at same time. */
 	if (min_precac_timeout)
@@ -4599,17 +4628,17 @@ dfs_translate_chwidth_enum2val(struct wlan_dfs *dfs,
 	}
 }
 
-/* dfs_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.
+/* dfs_map_to_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.
  * @dfs:     Pointer to WLAN DFS structure.
  * @chwidth: Channel width enum.
  *
  * Return: The translated channel width enum.
  */
 static enum phy_ch_width
-dfs_find_agile_width(struct wlan_dfs *dfs, enum phy_ch_width chwidth)
+dfs_map_to_agile_width(struct wlan_dfs *dfs, enum phy_ch_width chwidth)
 {
 	switch (chwidth) {
 	case CH_WIDTH_20MHZ:
@@ -4659,7 +4688,7 @@ void dfs_get_ieeechan_for_agilecac(struct wlan_dfs *dfs,
 			"aDFS during 160MHz operation not supported by target");
 		return;
 	}
-	dfs->dfs_precac_chwidth = dfs_find_agile_width(dfs, chwidth);
+	dfs->dfs_precac_chwidth = dfs_map_to_agile_width(dfs, chwidth);
 	if (dfs->dfs_precac_chwidth == CH_WIDTH_INVALID) {
 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "cannot start agile CAC!");
 		return;
@@ -4682,23 +4711,178 @@ void dfs_get_ieeechan_for_agilecac(struct wlan_dfs *dfs,
 }
 #endif
 
-/*
- * dfs_get_ieeechan_for_agilecac_for_freq() - Get agile CAC frequency.
- * @dfs: Pointer to wlan_dfs.
- * @ch_freq: Channel frequency in MHZ.
- * @pri_ch_freq: Primary channel frequency.
- * @sec_ch_freq: Secondary channel frequency.
+#ifdef QCA_SUPPORT_ADFS_RCAC
+
+/* dfs_fill_des_rcac_chan_params() - Fill ch_params from dfs current channel.
+ *                                   This ch_params is used to determine a
+ *                                   Rolling CAC frequency invoking DFS Random
+ *                                   selection algorithm.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @ch_params: Pointer to ch_params structure.
+ * @des_chwidth: Desired channel width.
  */
-#ifdef CONFIG_CHAN_FREQ_API
-void dfs_get_ieeechan_for_agilecac_for_freq(struct wlan_dfs *dfs,
-					    uint16_t *ch_freq,
-					    uint16_t pri_ch_freq,
-					    uint16_t sec_ch_freq)
+static void dfs_fill_des_rcac_chan_params(struct wlan_dfs *dfs,
+					  struct ch_params *ch_params,
+					  enum phy_ch_width des_chwidth)
 {
-	uint8_t chwidth_val;
-	uint16_t ieee_chan_freq;
-	enum phy_ch_width chwidth = CH_WIDTH_INVALID;
+	struct dfs_channel *chan = dfs->dfs_curchan;
 
+	ch_params->ch_width = des_chwidth;
+	ch_params->center_freq_seg0 = chan->dfs_ch_vhtop_ch_freq_seg1;
+	ch_params->center_freq_seg1 = chan->dfs_ch_vhtop_ch_freq_seg2;
+	ch_params->mhz_freq_seg0 = chan->dfs_ch_mhz_freq_seg1;
+	ch_params->mhz_freq_seg1 = chan->dfs_ch_mhz_freq_seg2;
+}
+
+/* dfs_get_random_rcac_chan() - Get a random rolling CAC frequency (MHZ).
+ * @dfs: Pointer to wlan_dfs structure.
+ * @des_chwidth: Desired channel width.
+ * @flags: Flags provided to the DFS Random channel algorithm.
+ *
+ * Return: Frequency in MHZ of type qdf_freq_t.
+ */
+static qdf_freq_t
+dfs_get_random_rcac_chan(struct wlan_dfs *dfs, enum phy_ch_width des_chwidth,
+			 uint16_t flags)
+{
+	struct ch_params ch_params;
+	qdf_freq_t target_chan_freq = 0;
+
+	qdf_mem_zero(&ch_params, sizeof(struct ch_params));
+	dfs_fill_des_rcac_chan_params(dfs, &ch_params, des_chwidth);
+
+	/* The current dfs channel width may not be supported by the agile
+	 * engine. For example, some chips may support 160/80+80Mhz mode
+	 * for its operating channel (Tx/Rx), however, the agile engine
+	 * may support up to a maximum of 80Mhz bandwidth.
+	 * Therefore, we need to compute the agile channel width.
+	 * The function dfs_compute_agile_chan_width calculated the agile
+	 * channel width elsewhere and the agile channel width is
+	 * passed to the utils_dfs_get_random_channel_for_freq through
+	 * ch_params->ch_width.
+	 */
+	utils_dfs_get_random_channel_for_freq(dfs->dfs_pdev_obj, flags,
+					      &ch_params, NULL,
+					      &target_chan_freq, NULL);
+	return target_chan_freq;
+}
+#endif
+
+/* dfs_is_agile_rcac_enabled() - Determine if Rolling CAC is supported
+ * or not. Following are the conditions needed to assertain that rolling CAC
+ * is enabled:
+ * 1. DFS domain of the PDEV must be FCC or MKK.
+ * 2. User has enabled Rolling CAC configuration.
+ * 3. FW capability to support ADFS. Only non-160 capability is checked here.
+ * If we happen to choose the next RCAC channel as 160/80-80,
+ * 'dfs_fw_adfs_support_160' is also verified.
+ *
+ * @dfs: Pointer to struct wlan_dfs.
+ *
+ * Return: True if RCAC support is enabled, false otherwise.
+ */
+#ifdef QCA_SUPPORT_ADFS_RCAC
+bool dfs_is_agile_rcac_enabled(struct wlan_dfs *dfs)
+{
+	enum dfs_reg dfsdomain;
+	bool rcac_enabled = false;
+
+	dfsdomain = utils_get_dfsdomain(dfs->dfs_pdev_obj);
+	if ((dfsdomain == DFS_FCC_REGION || dfsdomain == DFS_MKK_REGION) &&
+	    dfs->dfs_agile_rcac_ucfg && dfs->dfs_fw_adfs_support_non_160)
+	    rcac_enabled = true;
+
+	return rcac_enabled;
+}
+#else
+bool dfs_is_agile_rcac_enabled(struct wlan_dfs *dfs)
+{
+	return false;
+}
+#endif
+
+/* dfs_convert_chwidth_to_wlan_phymode() - Given a channel width, find out the
+ *                                         11AXA channel mode.
+ * @chwidth: Channel width of type enum phy_ch_width.
+ *
+ * Return: Converted phymode of type wlan_phymode.
+ */
+static enum wlan_phymode
+dfs_convert_chwidth_to_wlan_phymode(enum phy_ch_width chwidth)
+{
+	switch (chwidth) {
+	case CH_WIDTH_20MHZ:
+		return WLAN_PHYMODE_11AXA_HE20;
+	case CH_WIDTH_40MHZ:
+		return WLAN_PHYMODE_11AXA_HE40;
+	case CH_WIDTH_80MHZ:
+		return WLAN_PHYMODE_11AXA_HE80;
+	case CH_WIDTH_160MHZ:
+		return WLAN_PHYMODE_11AXA_HE160;
+	case CH_WIDTH_80P80MHZ:
+		return WLAN_PHYMODE_11AXA_HE80_80;
+	default:
+		return WLAN_PHYMODE_MAX;
+	}
+}
+
+/* dfs_find_dfschan_for_freq() - Given frequency and channel width, find
+ *                               compute a dfs channel structure.
+ * @dfs: Pointer to struct wlan_dfs.
+ * @freq: Frequency in MHZ.
+ * @chwidth: Channel width.
+ * @chan: Pointer to struct dfs_channel to be filled.
+ *
+ * Return: QDF_STATUS_SUCCESS if a valid channel pointer exists, else
+ *         return status as QDF_STATUS_E_FAILURE.
+ */
+static QDF_STATUS
+dfs_find_dfschan_for_freq(struct wlan_dfs *dfs, qdf_freq_t freq,
+			  enum phy_ch_width chwidth,
+			  struct dfs_channel *chan)
+{
+	enum wlan_phymode mode;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!freq) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "Input freq is 0!!");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	mode = dfs_convert_chwidth_to_wlan_phymode(chwidth);
+
+	if (mode == WLAN_PHYMODE_MAX) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "Invalid RCAC mode, user "
+				"rcac channel invalid!");
+		return QDF_STATUS_E_FAILURE;
+	}
+	status =
+	    dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj,
+					      freq, 0, mode,
+					      &chan->dfs_ch_freq,
+					      &chan->dfs_ch_flags,
+					      &chan->dfs_ch_flagext,
+					      &chan->dfs_ch_ieee,
+					      &chan->dfs_ch_vhtop_ch_freq_seg1,
+					      &chan->dfs_ch_vhtop_ch_freq_seg2,
+					      &chan->dfs_ch_mhz_freq_seg1,
+					      &chan->dfs_ch_mhz_freq_seg2);
+
+	return status;
+}
+
+/* dfs_compute_agile_chan_width() - Compute agile detector's channel width
+ *                                  and current channel's width.
+ *
+ * @dfs: Pointer to wlan_dfs structure.
+ * @agile_ch_width: Agile channel width to be filled.
+ * @cur_ch_width: Current channel width to be filled.
+ */
+static void
+dfs_compute_agile_and_curchan_width(struct wlan_dfs *dfs,
+				    enum phy_ch_width *agile_ch_width,
+				    enum phy_ch_width *cur_ch_width)
+{
 	/*
 	 * Agile detector's band of operation depends on current pdev.
 	 * Find the current channel's width and apply the translate rules
@@ -4707,36 +4891,257 @@ void dfs_get_ieeechan_for_agilecac_for_freq(struct wlan_dfs *dfs,
 	 * to Agile detector's width:
 	 * 20 - 20, 40 - 40, 80 - 80, 160 - 80, 160 (non contiguous) - 80.
 	 */
-	dfs_find_chwidth_and_center_chan_for_freq(dfs, &chwidth, NULL, NULL);
+	dfs_find_curchwidth_and_center_chan_for_freq(dfs, cur_ch_width,
+						     NULL, NULL);
 
 	/* Check if the FW supports agile DFS when the pdev is operating on
 	 * 160 or 80P80MHz bandwidth. This information is stored in the flag
 	 * "dfs_fw_adfs_support_160" when the current chainmask is configured.
 	 */
-	if ((chwidth == CH_WIDTH_80P80MHZ || chwidth == CH_WIDTH_160MHZ) &&
+	if ((*cur_ch_width == CH_WIDTH_80P80MHZ ||
+	     *cur_ch_width == CH_WIDTH_160MHZ) &&
 	    (!dfs->dfs_fw_adfs_support_160)) {
+	    dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+		    "aDFS during 160MHz operation not supported by target");
+	    return;
+	}
+	*agile_ch_width = dfs_map_to_agile_width(dfs, *cur_ch_width);
+}
+
+#ifdef QCA_SUPPORT_ADFS_RCAC
+
+/* dfs_is_subchans_of_rcac_chan_in_nol() - Find out the HT20 subchannels of the
+ *                                         given dfs_channel and determine if
+ *                                         sub channels are in NOL.
+ * @dfs: Pointer to struct wlan_dfs.
+ * @rcac_chan: Pointer to struct dfs_channel.
+ *
+ * Return: true if the channel is in NOL else return false.
+ */
+static bool
+dfs_is_subchans_of_rcac_chan_in_nol(struct wlan_dfs *dfs,
+				    struct dfs_channel *rcac_chan)
+{
+	qdf_freq_t rcac_subchans[NUM_CHANNELS_160MHZ];
+	uint8_t n_rcac_sub_chans = 0;
+	int i;
+	bool is_nol = false;
+
+	n_rcac_sub_chans = dfs_find_dfs_sub_channels_for_freq(dfs, rcac_chan,
+							      rcac_subchans);
+
+	for (i = 0; i < n_rcac_sub_chans; i++) {
+		if (dfs_is_freq_in_nol(dfs, rcac_subchans[i])) {
+			is_nol = true;
+			break;
+		}
+	}
+	return is_nol;
+}
+
+/* dfs_is_rcac_chan_valid() - Find out if the given frequency is
+ *                            a valid RCAC channel.
+ * @dfs: Pointer to struct wlan_dfs.
+ * @chwidth: Agile channel width
+ * @rcac_freq: Rolling CAC frequency.
+ *
+ * Return: true if the channel is valid else return false.
+ */
+static bool
+dfs_is_rcac_chan_valid(struct wlan_dfs *dfs, enum phy_ch_width chwidth,
+		       qdf_freq_t rcac_freq)
+{
+	struct dfs_channel rcac_chan;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	qdf_mem_zero(&rcac_chan, sizeof(struct dfs_channel));
+
+	/* 1. Find a valid channel pointer with rcac freq and
+	 * agile channel width. If a valid channel pointer does not exists,
+	 * return failure.
+	 */
+	status = dfs_find_dfschan_for_freq(dfs, rcac_freq, chwidth,
+					   &rcac_chan);
+	if (status != QDF_STATUS_SUCCESS) {
 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
-			"aDFS during 160MHz operation not supported by target");
-		return;
+			"RCAC Channel %d not found for agile width %d",
+			dfs->dfs_agile_rcac_freq_ucfg,
+			chwidth);
+		return false;
 	}
-	dfs->dfs_precac_chwidth = dfs_find_agile_width(dfs, chwidth);
-	if (dfs->dfs_precac_chwidth == CH_WIDTH_INVALID) {
-		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "cannot start agile CAC!");
-		return;
+
+	/* 2. Reject the RCAC channel if it is a subset of the current operating
+	 * channel or if the RCAC channel is non-DFS.
+	 */
+	if (dfs_is_new_chan_subset_of_old_chan(dfs, &rcac_chan,
+					       dfs->dfs_curchan))
+		return false;
+
+	/* 3. Reject the RCAC channel if it has NOL channel as its subset. */
+	if (dfs_is_subchans_of_rcac_chan_in_nol(dfs, &rcac_chan))
+		return false;
+
+	return true;
+}
+
+/* dfs_save_rcac_ch_params() - Save the RCAC channel's params in DFS.
+ *                             It is stored in dfs->dfs_rcac_ch_params.
+ *                             This ch_params is used in 80211_dfs_action
+ *                             as the next channel after radar detect.
+ * @dfs: Pointer to struct wlan_dfs.
+ * @chwidth: Agile channel width.
+ * @rcac_freq: Rolling CAC frequency.
+ */
+static void
+dfs_save_rcac_ch_params(struct wlan_dfs *dfs, struct ch_params rcac_ch_params,
+			enum phy_ch_width rcac_width)
+{
+    dfs->dfs_rcac_ch_params.ch_width = rcac_width;
+    dfs->dfs_rcac_ch_params.sec_ch_offset = rcac_ch_params.sec_ch_offset;
+    dfs->dfs_rcac_ch_params.center_freq_seg0 = rcac_ch_params.center_freq_seg0;
+    dfs->dfs_rcac_ch_params.center_freq_seg1 = rcac_ch_params.center_freq_seg1;
+    dfs->dfs_rcac_ch_params.mhz_freq_seg0 = rcac_ch_params.mhz_freq_seg0;
+    dfs->dfs_rcac_ch_params.mhz_freq_seg1 = rcac_ch_params.mhz_freq_seg1;
+}
+
+/* dfs_find_rcac_chan() - Find out a rolling CAC channel.
+ *
+ * @dfs: Pointer to struct wlan_dfs.
+ * @curchan_chwidth: Current channel width.
+ *
+ * Return: Rolling CAC frequency in MHZ.
+ */
+static qdf_freq_t dfs_find_rcac_chan(struct wlan_dfs *dfs,
+				     enum phy_ch_width curchan_chwidth)
+{
+	bool is_user_rcac_chan_valid = false;
+	qdf_freq_t rcac_freq, rcac_center_freq = 0;
+	struct dfs_channel dfs_chan;
+	struct ch_params nxt_chan_params;
+	enum phy_ch_width nextchan_width = CH_WIDTH_INVALID;
+
+	qdf_mem_zero(&dfs_chan, sizeof(struct dfs_channel));
+	qdf_mem_zero(&nxt_chan_params, sizeof(struct ch_params));
+
+	/* If Rolling CAC is configured, RCAC frequency is the user configured
+	 * RCAC frequency or it is found using DFS Random Channel Algorithm.
+	 */
+
+	/* Check if user configured RCAC frequency is valid */
+	if (dfs->dfs_agile_rcac_freq_ucfg)
+		is_user_rcac_chan_valid =
+		    dfs_is_rcac_chan_valid(dfs, curchan_chwidth,
+					   dfs->dfs_agile_rcac_freq_ucfg);
+
+	if (is_user_rcac_chan_valid) {
+		rcac_freq = dfs->dfs_agile_rcac_freq_ucfg;
+	} else {
+		/* Invoke Random channel selection and select only
+		 * DFS channels.
+		 */
+		uint16_t flags = DFS_RANDOM_CH_FLAG_NO_CURR_OPE_CH;
+
+		rcac_freq =
+			dfs_get_random_rcac_chan(dfs, curchan_chwidth, flags);
+	}
+	if (dfs_find_dfschan_for_freq(dfs, rcac_freq, curchan_chwidth,
+				      &dfs_chan) != QDF_STATUS_SUCCESS)
+		return 0;
+
+	if (!WLAN_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(&dfs_chan))
+	    return 0;
+
+	/* Store the random channel ch params for future use on
+	 * radar detection.
+	 */
+
+	nxt_chan_params.ch_width = curchan_chwidth;
+	/* Get the ch_params from regulatory. ch_width and rcac_freq are the
+	 * input given to fetch other params of struct ch_params.
+	 */
+	wlan_reg_set_channel_params_for_freq(dfs->dfs_pdev_obj, rcac_freq, 0,
+					     &nxt_chan_params);
+	nextchan_width = dfs_map_to_agile_width(dfs, nxt_chan_params.ch_width);
+	if (nextchan_width != dfs->dfs_precac_chwidth) {
+	    return 0;
 	}
+	/* Store the rcac chan params in dfs */
+	dfs_save_rcac_ch_params(dfs, nxt_chan_params, nextchan_width);
+	rcac_center_freq = dfs_chan.dfs_ch_mhz_freq_seg1;
+
+	return rcac_center_freq;
+}
+#else
+static inline qdf_freq_t dfs_find_rcac_chan(struct wlan_dfs *dfs,
+					    enum phy_ch_width curchan_chwidth)
+{
+	return 0;
+}
+#endif
+
+#endif
+
+/* dfs_find_precac_chan() - Find out a channel to perform preCAC.
+ *
+ * @dfs: Pointer to struct wlan_dfs.
+ * @pri_ch_freq: Primary channel frequency in MHZ.
+ * @sec_ch_freq: Secondary channel frequency in MHZ.
+ *
+ * Return: PreCAC frequency in MHZ.
+ */
+static qdf_freq_t dfs_find_precac_chan(struct wlan_dfs *dfs,
+				       qdf_freq_t pri_ch_freq,
+				       qdf_freq_t sec_ch_freq)
+{
+	/* Convert precac_chwidth to DFS width and find a valid Agile
+	 * PreCAC frequency from the preCAC tree.
+	 */
+	uint8_t chwidth_val;
+
 	/* Find chwidth value for the given enum */
 	chwidth_val = dfs_translate_chwidth_enum2val(dfs,
 						     dfs->dfs_precac_chwidth);
 
 	dfs->dfs_soc_obj->ocac_status = OCAC_RESET;
-	ieee_chan_freq = dfs_get_ieeechan_for_precac_for_freq(dfs,
-							      pri_ch_freq,
-							      sec_ch_freq,
-							      chwidth_val);
-	if (ieee_chan_freq)
-		dfs->dfs_agile_precac_freq_mhz = ieee_chan_freq;
+	return dfs_get_ieeechan_for_precac_for_freq(dfs,
+						    pri_ch_freq,
+						    sec_ch_freq,
+						    chwidth_val);
+}
+
+/*
+ * dfs_set_agilecac_chan_for_freq() - Set agile CAC frequency.
+ * @dfs: Pointer to wlan_dfs.
+ * @ch_freq: Channel frequency in MHZ.
+ * @pri_ch_freq: Primary channel frequency.
+ * @sec_ch_freq: Secondary channel frequency.
+ */
+#ifdef CONFIG_CHAN_FREQ_API
+void dfs_set_agilecac_chan_for_freq(struct wlan_dfs *dfs,
+				    qdf_freq_t *ch_freq,
+				    qdf_freq_t pri_ch_freq,
+				    qdf_freq_t sec_ch_freq)
+{
+	qdf_freq_t ieee_chan_freq;
+	enum phy_ch_width agile_chwidth = CH_WIDTH_INVALID;
+	enum phy_ch_width curchan_chwidth = CH_WIDTH_INVALID;
+
+	dfs_compute_agile_and_curchan_width(dfs, &agile_chwidth,
+					    &curchan_chwidth);
+	if (agile_chwidth == CH_WIDTH_INVALID) {
+		qdf_info("Cannot start Agile CAC as a valid agile channel width "
+			 "could not be found\n");
+		return;
+	}
+	dfs->dfs_precac_chwidth = agile_chwidth;
+
+	if (dfs_is_agile_rcac_enabled(dfs))
+		ieee_chan_freq = dfs_find_rcac_chan(dfs, curchan_chwidth);
 	else
-		dfs->dfs_agile_precac_freq_mhz = 0;
+		ieee_chan_freq = dfs_find_precac_chan(dfs, pri_ch_freq,
+						      sec_ch_freq);
+
+	dfs->dfs_agile_precac_freq_mhz = ieee_chan_freq;
 
 	/* It was assumed that the bandwidth of the restricted 80p80 channel is
 	 * 160MHz to build the precac tree. But when configuring Agile the
@@ -4746,9 +5151,11 @@ void dfs_get_ieeechan_for_agilecac_for_freq(struct wlan_dfs *dfs,
 		dfs->dfs_precac_chwidth = CH_WIDTH_80P80MHZ;
 
 	*ch_freq = dfs->dfs_agile_precac_freq_mhz;
+
+	if (!*ch_freq)
+		qdf_info("%s: No valid Agile channels available in the current pdev", __func__);
 }
 #endif
-#endif
 
 #ifdef CONFIG_CHAN_NUM_API
 void dfs_find_vht80_chan_for_precac(struct wlan_dfs *dfs,
@@ -5848,4 +6255,163 @@ QDF_STATUS dfs_rcac_sm_destroy(struct dfs_soc_priv_obj *dfs_soc_obj)
 
 	return QDF_STATUS_SUCCESS;
 }
-#endif /* QCA_SUPPORT_ADFS_RCAC */
+
+QDF_STATUS dfs_set_rcac_enable(struct wlan_dfs *dfs, bool rcac_en)
+{
+	if (rcac_en == dfs->dfs_agile_rcac_ucfg) {
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "Rolling CAC: %d is already configured", rcac_en);
+		return QDF_STATUS_SUCCESS;
+	}
+	dfs->dfs_agile_rcac_ucfg = rcac_en;
+
+	/* RCAC config is changed. Reset the preCAC tree. */
+	dfs_reset_precac_lists(dfs);
+
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "rolling cac is %d", rcac_en);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS dfs_get_rcac_enable(struct wlan_dfs *dfs, uint8_t *rcacen)
+{
+	*rcacen = dfs->dfs_agile_rcac_ucfg;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS dfs_set_rcac_freq(struct wlan_dfs *dfs, qdf_freq_t rcac_freq)
+{
+	if (wlan_reg_is_5ghz_ch_freq(rcac_freq))
+		dfs->dfs_agile_rcac_freq_ucfg = rcac_freq;
+	else
+		dfs->dfs_agile_rcac_freq_ucfg = 0;
+
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,  "rolling cac freq %d",
+		 dfs->dfs_agile_rcac_freq_ucfg);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS dfs_get_rcac_freq(struct wlan_dfs *dfs, qdf_freq_t *rcac_freq)
+{
+	*rcac_freq = dfs->dfs_agile_rcac_freq_ucfg;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/*
+ * Rolling CAC Timer timeout function. Following actions are done
+ * on timer expiry:
+ * Timer running flag is cleared.
+ * If the rolling CAC state is completed, the RCAC freq and its sub-channels
+ * are marked as 'CAC Done' in the preCAC tree.
+ */
+static os_timer_func(dfs_rcac_timeout)
+{
+	struct wlan_dfs *dfs;
+	struct dfs_soc_priv_obj *dfs_soc_obj;
+
+	OS_GET_TIMER_ARG(dfs_soc_obj, struct dfs_soc_priv_obj *);
+
+	dfs = dfs_soc_obj->dfs_priv[dfs_soc_obj->cur_precac_dfs_index].dfs;
+
+	dfs_mark_adfs_chan_as_cac_done(dfs);
+}
+
+void dfs_rcac_timer_init(struct dfs_soc_priv_obj *dfs_soc_obj)
+{
+	qdf_timer_init(NULL, &dfs_soc_obj->dfs_rcac_timer,
+		       dfs_rcac_timeout,
+		       (void *)dfs_soc_obj,
+		       QDF_TIMER_TYPE_WAKE_APPS);
+}
+
+void dfs_rcac_timer_deinit(struct dfs_soc_priv_obj *dfs_soc_obj)
+{
+	qdf_timer_free(&dfs_soc_obj->dfs_rcac_timer);
+}
+
+/* dfs_start_agile_engine() - Prepare ADFS params and program the agile
+ *                            engine sending agile config cmd to FW.
+ * @dfs: Pointer to struct wlan_dfs.
+ */
+void dfs_start_agile_engine(struct wlan_dfs *dfs)
+{
+	struct dfs_agile_cac_params adfs_param;
+	struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops;
+	struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj;
+
+	/* Fill the RCAC ADFS params and send it to FW.
+	 * FW does not use RCAC timeout values for RCAC feature.
+	 * FW runs an infinite timer.
+	 */
+	dfs_fill_adfs_chan_params(dfs, &adfs_param);
+	adfs_param.min_precac_timeout = MIN_RCAC_DURATION;
+	adfs_param.max_precac_timeout = MAX_RCAC_DURATION;
+	adfs_param.ocac_mode = QUICK_RCAC_MODE;
+
+	qdf_info("%s : %d RCAC channel request sent for pdev: %pK "
+			 "ch_freq: %d", __func__, __LINE__, dfs->dfs_pdev_obj,
+			 dfs->dfs_agile_precac_freq_mhz);
+
+	dfs_tx_ops = wlan_psoc_get_dfs_txops(dfs_soc_obj->psoc);
+
+	if (dfs_tx_ops && dfs_tx_ops->dfs_agile_ch_cfg_cmd)
+		dfs_tx_ops->dfs_agile_ch_cfg_cmd(dfs->dfs_pdev_obj,
+						 &adfs_param);
+	else
+		dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS,
+			"dfs_tx_ops=%pK", dfs_tx_ops);
+}
+
+/* dfs_start_agile_rcac_timer() - Start host agile RCAC timer.
+ *
+ * @dfs: Pointer to struct wlan_dfs.
+ */
+void dfs_start_agile_rcac_timer(struct wlan_dfs *dfs)
+{
+	struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj;
+	uint32_t rcac_timeout = MIN_RCAC_DURATION;
+
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+		 "Host RCAC timeout = %d ms", rcac_timeout);
+
+	qdf_timer_mod(&dfs_soc_obj->dfs_rcac_timer,
+		      rcac_timeout);
+}
+
+/* dfs_prepare_agile_rcac_channel() - Find a valid Rolling CAC channel if
+ *                                    available.
+ *
+ * @dfs: Pointer to struct wlan_dfs.
+ * @is_rcac_chan_available: Flag to indicate if a valid RCAC channel is
+ *                          available.
+ */
+void dfs_prepare_agile_rcac_channel(struct wlan_dfs *dfs,
+				    bool *is_rcac_chan_available)
+{
+	qdf_freq_t rcac_ch_freq = 0;
+
+	/* Find out a valid rcac_ch_freq */
+	dfs_set_agilecac_chan_for_freq(dfs, &rcac_ch_freq, 0, 0);
+
+	/* If RCAC channel is available, the caller will start the timer and
+	 * send RCAC config to FW. If channel not available, the caller takes
+	 * care of sending RCAC abort and moving SM to INIT, resetting the RCAC
+	 * variables.
+	 */
+	*is_rcac_chan_available = rcac_ch_freq ? true : false;
+}
+
+/* dfs_stop_agile_rcac_timer() - Cancel the RCAC timer.
+ *
+ * @dfs: Pointer to struct wlan_dfs.
+ */
+void dfs_stop_agile_rcac_timer(struct wlan_dfs *dfs)
+{
+	struct dfs_soc_priv_obj *dfs_soc_obj;
+
+	dfs_soc_obj = dfs->dfs_soc_obj;
+	qdf_timer_sync_cancel(&dfs_soc_obj->dfs_rcac_timer);
+}
+#endif