Browse Source

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

Feature Description:
FCC/JP domains do not support PreCAC. However, we can always start
beaconing in a channel if we have completed CAC in that channel for more
than or equal to the CAC period of that channel.
If an Agile engine is available and the next channel is known, we can
start CAC in the next channel using agile, while continuing the Tx/Rx
in the current channel. And when we want to start beaconing in the
next channel after a radar detection (or after/for a user request) we can
do so without any new CAC. This allows us to jump to the new channel
without having to disrupt any ongoing data traffic. This type of
continuous CAC in the next channel while operating in the current channel
is known as Rolling CAC.

Following functionalities are implemented:

1. APIs to enable/disable Rolling CAC feature.
2. APIs to set/get user configured Rolling CAC frequency.
3. Rolling CAC timer init, deinit and timeout functions.
4. API to determine if Rolling CAC feature is supported or not.
5. API to prepare a Rolling CAC channel and start RCAC host timer.
6. API to verify if user configured RCAC frequency is valid or not.
7. API to invoke random channel selection algorithm to determine a
   RCAC chan.

CRs-Fixed: 2659363
Change-Id: Idd233268530a743dee5f0d939168573f1ba10ad8
Priyadarshnee S 5 years ago
parent
commit
67c3abe5c0
1 changed files with 635 additions and 69 deletions
  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