瀏覽代碼

qca-wifi: Split dfs_zero_cac.c into multiple files

To enhance code-readability, split dfs_zero_cac.c file into multiple
smaller and logically coherent files. The new files are:
dfs_agile_sm.c, dfs_precac_list.c, dfs_precac_forest.h

CRs-Fixed: 2822588
Change-Id: If3e599cc3d895b3fbc6b114de6b69dd3afff3b6f
Vijay Krishnan 4 年之前
父節點
當前提交
6ee85bd7d4

+ 257 - 0
umac/dfs/core/src/dfs_precac_forest.h

@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * DOC: This file has the headers of Precac Tree structure feature.
+ *
+ */
+
+#ifndef _DFS_PRECAC_FOREST_H_
+#define _DFS_PRECAC_FOREST_H_
+#include <dfs_zero_cac.h>
+#include <dfs.h>
+#include <wlan_lmac_if_def.h>
+#define N_OFFSETS 2
+
+/**
+ * struct dfs_channel_bw - Structure to store the information about precac
+ * root's primary channel frequency, maximum bandwidth and the center frequency.
+ *
+ * @dfs_pri_ch_freq:     Primary channel frequency of the root channel.
+ * @dfs_center_ch_freq:  Center frequency of the 20/40/80/160 channel.In case of
+ *                       the 165MHz channel, it is 5730MHz.
+ * @dfs_max_bw:          Maximum bandwidth of the channel available in the
+ *                       current channel list.
+ */
+struct dfs_channel_bw {
+	uint16_t dfs_pri_ch_freq;
+	uint16_t dfs_center_ch_freq;
+	uint16_t dfs_max_bw;
+};
+
+/**
+ * struct precac_tree_offset_for_different_bw - Bandwidth, tree depth and
+ * channel offsets information to build the precac tree.
+ * @bandwidth:                Bandwidth of the the root node.
+ * @tree_depth:               Tree depth of the precac tree.
+ * @initial_and_next_offsets: Offset to root node to find the initial and the
+ *                            next channels of the node.
+ */
+struct precac_tree_offset_for_different_bw {
+	int bandwidth;
+	int tree_depth;
+	int initial_and_next_offsets[TREE_DEPTH_MAX][N_OFFSETS];
+};
+
+#define START_INDEX 0
+#define STEP_INDEX 1
+
+
+static const
+struct precac_tree_offset_for_different_bw offset20 = {DFS_CHWIDTH_20_VAL,
+	TREE_DEPTH_20,
+	{
+		{0, NEXT_20_CHAN_FREQ_OFFSET}
+	}
+};
+
+static const
+struct precac_tree_offset_for_different_bw offset40 = {DFS_CHWIDTH_40_VAL,
+	TREE_DEPTH_40,
+	{
+		{0, NEXT_40_CHAN_FREQ_OFFSET},
+		{-10, NEXT_20_CHAN_FREQ_OFFSET}
+	}
+};
+
+static const
+struct precac_tree_offset_for_different_bw offset80 = {DFS_CHWIDTH_80_VAL,
+	TREE_DEPTH_80,
+	{
+		{0, NEXT_80_CHAN_FREQ_OFFSET},
+		{-20, NEXT_40_CHAN_FREQ_OFFSET},
+		{-30, NEXT_20_CHAN_FREQ_OFFSET}
+	}
+};
+
+static const
+struct precac_tree_offset_for_different_bw offset160 = {DFS_CHWIDTH_160_VAL,
+	TREE_DEPTH_160,
+	{
+		{INITIAL_160_CHAN_FREQ_OFFSET, NEXT_160_CHAN_FREQ_OFFSET},
+		{INITIAL_80_CHAN_FREQ_OFFSET, NEXT_80_CHAN_FREQ_OFFSET},
+		{INITIAL_40_CHAN_FREQ_OFFSET, NEXT_40_CHAN_FREQ_OFFSET},
+		{INITIAL_20_CHAN_FREQ_OFFSET, NEXT_20_CHAN_FREQ_OFFSET}
+	}
+};
+
+static const
+struct precac_tree_offset_for_different_bw default_offset = {0, 0};
+
+/* Given a bandwidth, find the number of subchannels in that bandwidth */
+#define N_SUBCHS_FOR_BANDWIDTH(_bw) ((_bw) / MIN_DFS_SUBCHAN_BW)
+
+#define DFS_160MHZ_SECSEG_CHAN_OFFSET 40
+
+#define VHT80_FREQ_OFFSET 30
+/* For any 160MHz channel, a frequency offset of 70MHz would have been enough
+ * to include the right edge and left edge channels. But, the restricted 80P80
+ * or the 165MHz channel is also assumed to have a 160MHz root ie channel 146,
+ * so an offset of 75MHz is chosen.
+ */
+#define VHT160_FREQ_OFFSET 75
+
+#define IS_WITHIN_RANGE(_A, _B, _C)  \
+	(((_A) >= ((_B)-(_C))) && ((_A) <= ((_B)+(_C))))
+
+#define IS_WITHIN_RANGE_STRICT(_A, _B, _C)  \
+	(((_A) > ((_B)-(_C))) && ((_A) < ((_B)+(_C))))
+
+#define MAX_PREFIX_CHAR 28
+
+/**
+ * dfs_configure_deschan_for_precac() - API to prioritize user configured
+ * channel for preCAC.
+ *
+ * @dfs: Pointer to DFS of wlan_dfs structure.
+ * Return: frequency of type qdf_freq_t if configured, else 0.
+ */
+qdf_freq_t dfs_configure_deschan_for_precac(struct wlan_dfs *dfs);
+
+/**
+ * dfs_is_pcac_required_for_freq() - Find if given frequency is preCAC required.
+ * @node: Pointer to the preCAC tree Node in which the frequency is present.
+ * @freq: Frequency to be checked.
+ *
+ * Return: False if the frequency is not fully CAC done or in NOL, else true.
+ */
+bool dfs_is_pcac_required_for_freq(struct precac_tree_node *node,
+				   uint16_t freq);
+
+/**
+ * dfs_find_subchannels_for_center_freq() - API to find the subchannels given
+ * the center frequencies and ch_width.
+ * @pri_center_freq: It is the center of 20/40/80/160Mhz band and for 80+80Mhz
+ *                   it is the center of the first 80Mhz band.
+ * @sec_center_freq: It is used only for 80+80Mhz and denotes the center
+ *                   of the second 80Mhz band.
+ * @ch_width: Channel width.
+ * @channels: List of subchannels.
+ *
+ * Return: Number of subchannels.
+ */
+uint8_t dfs_find_subchannels_for_center_freq(qdf_freq_t pri_center_freq,
+					     qdf_freq_t sec_center_freq,
+					     enum phy_ch_width ch_width,
+					     qdf_freq_t *channels);
+
+/**
+ * dfs_find_precac_state_of_node() - Find the preCAC state of the given channel.
+ * @channel: Channel whose preCAC state is to be found.
+ * @precac_entry: PreCAC entry where the channel exists.
+ *
+ * Return, enum value of type precac_chan_state.
+ */
+enum precac_chan_state
+dfs_find_precac_state_of_node(qdf_freq_t channel,
+			      struct dfs_precac_entry *precac_entry);
+
+/* 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.
+ */
+void dfs_mark_adfs_chan_as_cac_done(struct wlan_dfs *dfs);
+
+/* dfs_descend_precac_tree_for_freq() - Descend into the precac BSTree based on
+ *                             the channel provided. If the channel is less than
+ *                             given node's channel, descend left, else right.
+ * @node:    Precac BSTree node.
+ * @chan_freq: Channel freq whose node is to be found.
+ *
+ * Return: the next precac_tree_node (left child or right child of
+ * current node).
+ */
+
+struct precac_tree_node *
+dfs_descend_precac_tree_for_freq(struct precac_tree_node *node,
+				 uint16_t chan_freq);
+
+/**
+ * dfs_unmark_rcac_done() - Unmark the CAC done channels from the RCAC list.
+ * @dfs: Pointer to wlan_dfs object.
+ */
+#ifdef QCA_SUPPORT_ADFS_RCAC
+void dfs_unmark_rcac_done(struct wlan_dfs *dfs);
+#else
+static inline
+void dfs_unmark_rcac_done(struct wlan_dfs *dfs)
+{
+}
+#endif
+/**
+ * dfs_is_precac_completed_count_non_zero() - API to find if the preCAC
+ * completed channels count is zero/non_zero.
+ * @dfs: Pointer to DFS object.
+ *
+ * Return true, if there exists atleast one node/subchannel in the preCAC list
+ * that is CAC done, else return false.
+ */
+bool dfs_is_precac_completed_count_non_zero(struct wlan_dfs *dfs);
+
+/*
+ * 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.
+ */
+void dfs_fill_adfs_chan_params(struct wlan_dfs *dfs,
+			       struct dfs_agile_cac_params *adfs_param);
+
+/* dfs_agile_precac_cleanup() - Reset parameters of wlan_dfs.
+ *
+ * @dfs: Pointer to struct wlan_dfs.
+ */
+void dfs_agile_precac_cleanup(struct wlan_dfs *dfs);
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * dfs_precac_check_home_chan_change() - Change the home channel
+ *                                       after precac is done.
+ *
+ * @dfs: Pointer to dfs handler.
+ *
+ * If precac is done on the home channel, then return true, else false.
+ *
+ * Return: true if precac done on home channel, else false.
+ */
+bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs);
+#else
+static inline bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs)
+{
+	return false;
+}
+#endif
+#endif /* _DFS_PRECAC_FOREST_H_ */

+ 952 - 0
umac/dfs/core/src/misc/dfs_agile_sm.c

@@ -0,0 +1,952 @@
+/*
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * DOC: This file has Agile State machine functions.
+ *
+ */
+
+#include "../dfs_precac_forest.h"
+#include "wlan_reg_services_api.h"
+
+#ifdef QCA_SUPPORT_AGILE_DFS
+/* 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);
+}
+
+/**
+ * --------------------- ROLLING CAC STATE MACHINE ----------------------
+ *
+ * Rolling CAC is a feature where in, a separate hardware (Agile detector)
+ * will be brought up in a channel that is not the current operating channel
+ * and will continue to monitor the channel non-stop, until the next
+ * channel change or radar in this RCAC channel.
+ *
+ * Now if the Rolling CAC channel was radar free for a minimum duration
+ * (1 min.) and the device is now switching to this channel, no CAC is required.
+ *
+ * I.e. let's say the current operating channel is 64 HT80 and we are starting
+ * the agile detector in 100 HT80. After a minute of being up in 100 HT80, we
+ * switch the radio to 100 HT80. This operating channel change will not
+ * require CAC now since the channel was radar free for the last 1 minute,
+ * as determined by the agile detector.
+ *
+ * Introduction of a rolling CAC state machine:
+ *
+ * To acheive the rolling CAC feature using the agile detector, a trivial
+ * state machine is implemented, as represented below:
+ *
+ *                           _________________
+ *                          |                 |
+ *            |------------>|       INIT      |<-----------|
+ *            |             |_________________|            |
+ *            |                      |                     |
+ *            |                      |                     |
+ *            | [EV_RCAC_STOP]       | [EV_RCAC_START]     | [EV_RCAC_STOP]
+ *            | [EV_ADFS_RADAR]      |                     | [EV_ADFS_RADAR]
+ *            |                      |                     |
+ *            |                      |                     |
+ *    ________|________              |             ________|________
+ *   |                 |             |----------->|                 |
+ *   |    COMPLETE     |                          |     RUNNING     |
+ *   |_________________|<-------------------------|_________________|
+ *                             [EV_RCAC_DONE]
+ *
+ *
+ *
+ * Legend:
+ *     _________________
+ *    |                 |
+ * 1. |   RCAC STATES   |
+ *    |_________________|
+ *
+ * 2. [RCAC_EVENTS]
+ *
+ *
+ * Event triggers and handlers description:
+ *
+ * EV_RCAC_START:
+ *   Posted from vdev response and is handled by all three states.
+ *   1. INIT handler:
+ *        a. Check if RCAC is already running,
+ *           - If yes, do not transition.
+ *           - If no, go to step b.
+ *        b. Check if a new RCAC channel can be found,
+ *           - If no, do not transition.
+ *           - If yes, transition to RUNNING.
+ *
+ * EV_RCAC_STOP:
+ *   Posted from last vap down or config disable, handled by RUNNING
+ *   and COMPLETE.
+ *   1. RUNNING handler:
+ *        a. Stop the HOST RCAC timer.
+ *        b. Send wmi_adfs_abort_cmd to FW and transition to INIT.
+ *   2. COMPLETE handler:
+ *        a. Send wmi_adfs_abort_cmd to FW and transition to INIT.
+ *
+ * EV_ADFS_RADAR:
+ *   Posted from radar detection and is handled in RUNNING and COMPLETE.
+ *   1. RUNNING handler (same as EV_RCAC_START):
+ *        a. Check if RCAC was running for this pdev,
+ *           - If yes, transition to INIT and post EV_RCAC_START event.
+ *           - If no, ignore.
+ *   2. COMPLETE handler (same as EV_RCAC_START):
+ *        a. Check if RCAC was running for this pdev,
+ *           - If yes, transition to INIT and post EV_RCAC_START event.
+ *           - If no, ignore.
+ *
+ *   Note: EV_ADFS_RADAR works same as EV_RCAC_START event right now, but
+ *         will change in future, where, based on user preference, either
+ *         a new RCAC channel will be picked (requiring the transition to
+ *         INIT like present), or RCAC will be restarted on the same channel.
+ *
+ * EV_RCAC_DONE:
+ *   Posted from host RCAC timer completion and is handled in RUNNING.
+ *   1. RUNNING handler:
+ *      a. mark RCAC done and transition to COMPLETE.
+ *
+ * Epilogue:
+ *   Rolling CAC state machine is for the entire psoc and since the
+ *   agile detector can run for one pdev at a time, sharing of resource is
+ *   required.
+ *   In case of ETSI preCAC, sharing was done in a round robin fashion where
+ *   each pdev runs ADFS for it's channels alternatively. However, in RCAC, the
+ *   CAC period is not defined is continuous till the next channel change.
+ *
+ *   Hence ADFS detector is shared as follows:
+ *   1. First come first serve: the pdev that is brought up first, i.e, for
+ *      the first vdev response, an RCAC_START is posted and this pdev will
+ *      hold the agile detector and run RCAC till it is stopped.
+ *   2. Stopping the RCAC can be either by disabling user config "rcac_en 0"
+ *      or by bringing down all vaps, or if no channel is available.
+ *   3. Once RCAC is stopped for a pdev, it can be started in the other pdev
+ *      by restarting it's vap (i.e. a vdev response).
+ *
+ *   A working sequence of RCAC is as follows:
+ *     - Consider that the channel configured during bring up is 52HT80.
+ *       1. The First VAP's vdev_start_resp posts an event EV_RCAC_START to the
+ *          RCAC state machine.
+ *       2. The RCAC state machine which is in INIT state (default) receives the
+ *          event, picks a channel to do rolling CAC on, e.g. channel 100HT80.
+ *          The SM is then transitioned to RUNNING state.
+ *       3. In the entry of RUNNING state, a host timer is started and agile
+ *          cfg cmd to FW is sent.
+ *       4. When the HOST timer expires, it posts the EV_RCAC_DONE event to
+ *          the state machine.
+ *       5. EV_RCAC_DONE event received in RUNNING state, transitions the SM
+ *          to COMPLETE.
+ *       6. In the entry of COMPLETE, the RCAC channel is marked as CAC done
+ *          in the precac tree.
+ *       7. If radar is detected on primary channel, the new channel is the
+ *          RCAC channel (100HT80) which does not require CAC if the preCAC
+ *          tree is marked as CAC done.
+ *          Before sending vdev_start, an EV_RCAC_STOP is posted
+ *          which moves the SM to INIT state clearing all the params and
+ *          bringing down the agile detector.
+ *          (CAC decisions are taken before).
+ *       8. After vdev_resp, another EV_RCAC_START is sent to restart the
+ *          RCAC SM with a new RCAC channel if available.
+ *
+ *   A future enhancement will be triggering RCAC_START at user level.
+ */
+
+/**
+ * dfs_agile_set_curr_state() - API to set the current state of Agile SM.
+ * @dfs_soc_obj: Pointer to DFS soc private object.
+ * @state: value of current state.
+ *
+ * Return: void.
+ */
+static void dfs_agile_set_curr_state(struct dfs_soc_priv_obj *dfs_soc_obj,
+				     enum dfs_agile_sm_state state)
+{
+	if (state < DFS_AGILE_S_MAX) {
+		dfs_soc_obj->dfs_agile_sm_cur_state = state;
+	} else {
+		dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS,
+			"DFS RCAC state (%d) is invalid", state);
+		QDF_BUG(0);
+	}
+}
+
+/**
+ * dfs_agile_get_curr_state() - API to get current state of Agile SM.
+ * @dfs_soc_obj: Pointer to DFS soc private object.
+ *
+ * Return: current state enum of type, dfs_rcac_sm_state.
+ */
+static enum dfs_agile_sm_state
+dfs_agile_get_curr_state(struct dfs_soc_priv_obj *dfs_soc_obj)
+{
+	return dfs_soc_obj->dfs_agile_sm_cur_state;
+}
+
+/**
+ * dfs_rcac_sm_transition_to() - Wrapper API to transition the Agile SM state.
+ * @dfs_soc_obj: Pointer to dfs soc private object that hold the SM handle.
+ * @state: State to which the SM is transitioning to.
+ *
+ * Return: void.
+ */
+static void dfs_agile_sm_transition_to(struct dfs_soc_priv_obj *dfs_soc_obj,
+				       enum dfs_agile_sm_state state)
+{
+	wlan_sm_transition_to(dfs_soc_obj->dfs_agile_sm_hdl, state);
+}
+
+/**
+ * dfs_agile_sm_deliver_event() - API to post events to Agile SM.
+ * @dfs_soc_obj: Pointer to dfs soc private object.
+ * @event: Event to be posted to the RCAC SM.
+ * @event_data_len: Length of event data.
+ * @event_data: Pointer to event data.
+ *
+ * Return: QDF_STATUS_SUCCESS on handling the event, else failure.
+ *
+ * Note: This version of event posting API is not under lock and hence
+ * should only be called for posting events within the SM and not be
+ * under a dispatcher API without a lock.
+ */
+static
+QDF_STATUS dfs_agile_sm_deliver_event(struct dfs_soc_priv_obj *dfs_soc_obj,
+				      enum dfs_agile_sm_evt event,
+				      uint16_t event_data_len,
+				      void *event_data)
+{
+	return wlan_sm_dispatch(dfs_soc_obj->dfs_agile_sm_hdl,
+				event,
+				event_data_len,
+				event_data);
+}
+
+#ifdef QCA_SUPPORT_ADFS_RCAC
+/* 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_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);
+}
+
+
+/**
+ * dfs_abort_agile_rcac() - Send abort Agile RCAC to F/W.
+ * @dfs: Pointer to struct wlan_dfs.
+ */
+static void dfs_abort_agile_rcac(struct wlan_dfs *dfs)
+{
+
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops;
+
+	dfs_stop_agile_rcac_timer(dfs);
+	psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj);
+	dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc);
+	if (dfs_tx_ops && dfs_tx_ops->dfs_ocac_abort_cmd)
+		dfs_tx_ops->dfs_ocac_abort_cmd(dfs->dfs_pdev_obj);
+
+	qdf_mem_zero(&dfs->dfs_rcac_param, sizeof(struct dfs_rcac_params));
+	dfs->dfs_agile_precac_freq_mhz = 0;
+	dfs->dfs_precac_chwidth = CH_WIDTH_INVALID;
+	dfs->dfs_soc_obj->cur_agile_dfs_index = DFS_PSOC_NO_IDX;
+}
+#else
+static inline void dfs_abort_agile_rcac(struct wlan_dfs *dfs)
+{
+}
+#endif
+
+/* dfs_abort_agile_precac() - Reset parameters of wlan_dfs and send abort
+ * to F/W.
+ * @dfs: Pointer to struct wlan_dfs.
+ */
+static void dfs_abort_agile_precac(struct wlan_dfs *dfs)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops;
+
+	psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj);
+	dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc);
+
+	dfs_agile_precac_cleanup(dfs);
+	/*Send the abort to F/W as well */
+	if (dfs_tx_ops && dfs_tx_ops->dfs_ocac_abort_cmd)
+		dfs_tx_ops->dfs_ocac_abort_cmd(dfs->dfs_pdev_obj);
+}
+
+/**
+ * dfs_agile_state_init_entry() - Entry API for INIT state
+ * @ctx: DFS SoC private object
+ *
+ * API to perform operations on moving to INIT state
+ *
+ * Return: void
+ */
+static void dfs_agile_state_init_entry(void *ctx)
+{
+	struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx;
+
+	dfs_agile_set_curr_state(dfs_soc, DFS_AGILE_S_INIT);
+}
+
+/**
+ * dfs_agile_state_init_exit() - Exit API for INIT state
+ * @ctx: DFS SoC private object
+ *
+ * API to perform operations on moving out of INIT state
+ *
+ * Return: void
+ */
+static void dfs_agile_state_init_exit(void *ctx)
+{
+	/* NO OPS */
+}
+
+/**
+ * dfs_init_agile_start_evt_handler() - Init state start event handler.
+ * @dfs: Instance of wlan_dfs structure.
+ * @dfs_soc: DFS SoC private object
+ *
+ * Return : True if PreCAC/RCAC chan is found.
+ */
+static bool  dfs_init_agile_start_evt_handler(struct wlan_dfs *dfs,
+					      struct dfs_soc_priv_obj *dfs_soc)
+{
+	bool is_chan_found = false;
+
+	/*For RCAC */
+	if (dfs_is_agile_rcac_enabled(dfs)) {
+		/* Check if feature is enabled for this DFS and if RCAC channel
+		 * is valid, if those are true, send appropriate WMIs to FW
+		 * and only then transition to the state as follows.
+		 */
+		dfs_prepare_agile_rcac_channel(dfs, &is_chan_found);
+	}
+	/*For PreCAC */
+	else if (dfs_is_agile_precac_enabled(dfs)) {
+		dfs_soc->dfs_priv[dfs->dfs_psoc_idx].agile_precac_active
+			= true;
+		if (!dfs_soc->precac_state_started &&
+		    !dfs_soc->dfs_precac_timer_running) {
+			dfs_soc->precac_state_started = true;
+			dfs_prepare_agile_precac_chan(dfs, &is_chan_found);
+		}
+	}
+
+	return is_chan_found;
+}
+
+/**
+ * dfs_agile_state_init_event() - INIT State event handler
+ * @ctx: DFS SoC private object
+ * @event: Event posted to the SM.
+ * @event_data_len: Length of event data.
+ * @event_data: Pointer to event data.
+ *
+ * API to handle events in INIT state
+ *
+ * Return: TRUE:  on handling event
+ *         FALSE: on ignoring the event
+ */
+static bool dfs_agile_state_init_event(void *ctx,
+				      uint16_t event,
+				      uint16_t event_data_len,
+				      void *event_data)
+{
+	struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx;
+	bool status;
+	struct wlan_dfs *dfs;
+	bool is_chan_found;
+
+	if (!event_data)
+		return false;
+
+	dfs = (struct wlan_dfs *)event_data;
+
+	switch (event) {
+	case DFS_AGILE_SM_EV_AGILE_START:
+
+		if (dfs_soc->cur_agile_dfs_index != DFS_PSOC_NO_IDX)
+			return true;
+
+		is_chan_found = dfs_init_agile_start_evt_handler(dfs,
+								 dfs_soc);
+		if (is_chan_found) {
+			dfs_soc->cur_agile_dfs_index = dfs->dfs_psoc_idx;
+			dfs_agile_sm_transition_to(dfs_soc,
+						   DFS_AGILE_S_RUNNING);
+		} else {
+			/*
+			 * This happens when there is no preCAC chan
+			 * in any of the radios
+			 */
+			dfs_agile_precac_cleanup(dfs);
+			/* Cleanup and wait */
+		}
+
+		status = true;
+		break;
+	default:
+		status = false;
+		break;
+	}
+
+	return status;
+}
+
+/**
+ * dfs_agile_state_running_entry() - Entry API for running state
+ * @ctx: DFS SoC private object
+ *
+ * API to perform operations on moving to running state
+ *
+ * Return: void
+ */
+static void dfs_agile_state_running_entry(void *ctx)
+{
+	struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx;
+	struct wlan_dfs *dfs =
+		dfs_soc->dfs_priv[dfs_soc->cur_agile_dfs_index].dfs;
+
+	dfs_agile_set_curr_state(dfs_soc, DFS_AGILE_S_RUNNING);
+
+	/* RCAC */
+	if (dfs_is_agile_rcac_enabled(dfs)) {
+		dfs_start_agile_rcac_timer(dfs);
+		dfs_start_agile_engine(dfs);
+	}
+}
+
+/**
+ * dfs_agile_state_running_exit() - Exit API for RUNNING state
+ * @ctx: DFS SoC private object
+ *
+ * API to perform operations on moving out of RUNNING state
+ *
+ * Return: void
+ */
+static void dfs_agile_state_running_exit(void *ctx)
+{
+	/* NO OPS */
+}
+
+/**
+ * dfs_agile_state_running_event() - RUNNING State event handler
+ * @ctx: DFS SoC private object
+ * @event: Event posted to the SM.
+ * @event_data_len: Length of event data.
+ * @event_data: Pointer to event data.
+ *
+ * API to handle events in RUNNING state
+ *
+ * Return: TRUE:  on handling event
+ *         FALSE: on ignoring the event
+ */
+static bool dfs_agile_state_running_event(void *ctx,
+					 uint16_t event,
+					 uint16_t event_data_len,
+					 void *event_data)
+{
+	struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx;
+	bool status;
+	struct wlan_dfs *dfs;
+	bool is_cac_done_on_des_chan;
+
+	if (!event_data)
+		return false;
+
+	dfs = (struct wlan_dfs *)event_data;
+
+	if (dfs->dfs_psoc_idx != dfs_soc->cur_agile_dfs_index)
+		return false;
+
+	switch (event) {
+	case DFS_AGILE_SM_EV_ADFS_RADAR:
+		/* After radar is found on the Agile channel we need to find
+		 * a new channel and then start Agile CAC on that.
+		 * On receiving the "DFS_AGILE_SM_EV_ADFS_RADAR_FOUND" if
+		 * we change the state from [RUNNING] -> [RUNNING] then
+		 * [RUNNING] should handle case in which a channel is not found
+		 * and bring the state machine back to INIT.
+		 * Instead we move the state to INIT and post the event
+		 * "DFS_AGILE_SM_EV_AGILE_START" so INIT handles the case of
+		 * channel not found and stay in that state.
+		 * Abort the existing RCAC and restart from INIT state.
+		 */
+		if (dfs_is_agile_rcac_enabled(dfs))
+			dfs_abort_agile_rcac(dfs);
+		else if (dfs_is_agile_precac_enabled(dfs))
+			dfs_abort_agile_precac(dfs);
+
+		dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT);
+		dfs_agile_sm_deliver_event(dfs_soc,
+					   DFS_AGILE_SM_EV_AGILE_START,
+					   event_data_len,
+					   event_data);
+
+		status = true;
+		break;
+	case DFS_AGILE_SM_EV_AGILE_STOP:
+		if (dfs_is_agile_rcac_enabled(dfs))
+			dfs_abort_agile_rcac(dfs);
+		else if (dfs_is_agile_precac_enabled(dfs))
+			dfs_abort_agile_precac(dfs);
+
+		dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT);
+		status = true;
+		break;
+	case DFS_AGILE_SM_EV_AGILE_DONE:
+		if (dfs_is_agile_precac_enabled(dfs)) {
+			if (dfs_soc->ocac_status == OCAC_SUCCESS) {
+				dfs_soc->ocac_status = OCAC_RESET;
+				dfs_mark_adfs_chan_as_cac_done(dfs);
+			}
+			dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT);
+			dfs_agile_precac_cleanup(dfs);
+			is_cac_done_on_des_chan =
+				dfs_precac_check_home_chan_change(dfs);
+			if (!is_cac_done_on_des_chan) {
+				dfs_agile_sm_deliver_event(dfs_soc,
+					DFS_AGILE_SM_EV_AGILE_START,
+					event_data_len,
+					event_data);
+			}
+		} else if (dfs_is_agile_rcac_enabled(dfs)) {
+			dfs_agile_sm_transition_to(dfs_soc,
+						   DFS_AGILE_S_COMPLETE);
+		}
+		status = true;
+		break;
+	default:
+		status = false;
+		break;
+	}
+
+	return status;
+}
+
+/**
+ * dfs_agile_state_complete_entry() - Entry API for complete state
+ * @ctx: DFS SoC private object
+ *
+ * API to perform operations on moving to complete state
+ *
+ * Return: void
+ */
+static void dfs_agile_state_complete_entry(void *ctx)
+{
+	struct dfs_soc_priv_obj *dfs_soc_obj = (struct dfs_soc_priv_obj *)ctx;
+	struct wlan_dfs *dfs;
+
+	dfs_agile_set_curr_state(dfs_soc_obj, DFS_AGILE_S_COMPLETE);
+
+	if (!(dfs_soc_obj->cur_agile_dfs_index < WLAN_UMAC_MAX_PDEVS))
+		return;
+
+	dfs = dfs_soc_obj->dfs_priv[dfs_soc_obj->cur_agile_dfs_index].dfs;
+
+	/* Mark the RCAC channel as CAC done. */
+	dfs_mark_adfs_chan_as_cac_done(dfs);
+}
+
+/**
+ * dfs_agile_state_complete_exit() - Exit API for complete state
+ * @ctx: DFS SoC private object
+ *
+ * API to perform operations on moving out of complete state
+ *
+ * Return: void
+ */
+static void dfs_agile_state_complete_exit(void *ctx)
+{
+	/* NO OPs. */
+}
+
+/**
+ * dfs_agile_state_complete_event() - COMPLETE State event handler
+ * @ctx: DFS SoC private object
+ * @event: Event posted to the SM.
+ * @event_data_len: Length of event data.
+ * @event_data: Pointer to event data.
+ *
+ * API to handle events in COMPLETE state
+ *
+ * Return: TRUE:  on handling event
+ *         FALSE: on ignoring the event
+ */
+static bool dfs_agile_state_complete_event(void *ctx,
+					  uint16_t event,
+					  uint16_t event_data_len,
+					  void *event_data)
+{
+	struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx;
+	bool status;
+	struct wlan_dfs *dfs;
+
+	if (!event_data)
+		return false;
+
+	dfs = (struct wlan_dfs *)event_data;
+
+	if (dfs->dfs_psoc_idx != dfs_soc->cur_agile_dfs_index)
+		return false;
+
+	switch (event) {
+	case DFS_AGILE_SM_EV_ADFS_RADAR:
+		/* Reset the RCAC done state for this RCAC chan of this dfs.
+		 * Unmark the channels for RCAC done before calling abort API as
+		 * the abort API invalidates the cur_agile_dfs_index.
+		 */
+		dfs_unmark_rcac_done(dfs);
+		/* Abort the existing RCAC and restart from INIT state. */
+		dfs_abort_agile_rcac(dfs);
+		dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT);
+		dfs_agile_sm_deliver_event(dfs_soc,
+					  DFS_AGILE_SM_EV_AGILE_START,
+					  event_data_len,
+					  event_data);
+		status = true;
+		break;
+	case DFS_AGILE_SM_EV_AGILE_STOP:
+		/* Reset the RCAC done state for this RCAC chan of this dfs.
+		 * Unmark the channels for RCAC done before calling abort API as
+		 * the abort API invalidates the cur_agile_dfs_index.
+		 */
+		dfs_unmark_rcac_done(dfs);
+		dfs_abort_agile_rcac(dfs);
+		dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT);
+		status = true;
+		break;
+	default:
+		status = false;
+		break;
+	}
+
+	return status;
+}
+
+static struct wlan_sm_state_info dfs_agile_sm_info[] = {
+	{
+		(uint8_t)DFS_AGILE_S_INIT,
+		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
+		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
+		false,
+		"INIT",
+		dfs_agile_state_init_entry,
+		dfs_agile_state_init_exit,
+		dfs_agile_state_init_event
+	},
+	{
+		(uint8_t)DFS_AGILE_S_RUNNING,
+		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
+		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
+		false,
+		"RUNNING",
+		dfs_agile_state_running_entry,
+		dfs_agile_state_running_exit,
+		dfs_agile_state_running_event
+	},
+	{
+		(uint8_t)DFS_AGILE_S_COMPLETE,
+		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
+		(uint8_t)WLAN_SM_ENGINE_STATE_NONE,
+		false,
+		"COMPLETE",
+		dfs_agile_state_complete_entry,
+		dfs_agile_state_complete_exit,
+		dfs_agile_state_complete_event
+	},
+};
+
+static const char *dfs_agile_sm_event_names[] = {
+	"EV_AGILE_START",
+	"EV_AGILE_STOP",
+	"EV_AGILE_DONE",
+	"EV_ADFS_RADAR_FOUND",
+};
+
+/**
+ * dfs_agile_sm_print_state() - API to log the current state.
+ * @dfs_soc_obj: Pointer to dfs soc private object.
+ *
+ * Return: void.
+ */
+static void dfs_agile_sm_print_state(struct dfs_soc_priv_obj *dfs_soc_obj)
+{
+	enum dfs_agile_sm_state state;
+
+	state = dfs_agile_get_curr_state(dfs_soc_obj);
+	if (!(state < DFS_AGILE_S_MAX))
+		return;
+
+	dfs_debug(NULL, WLAN_DEBUG_DFS_AGILE, "->[%s] %s",
+		  dfs_soc_obj->dfs_agile_sm_hdl->name,
+		  dfs_agile_sm_info[state].name);
+}
+
+/**
+ * dfs_agile_sm_print_state_event() - API to log the current state and event
+ *                                   received.
+ * @dfs_soc_obj: Pointer to dfs soc private object.
+ * @event: Event posted to RCAC SM.
+ *
+ * Return: void.
+ */
+static void dfs_agile_sm_print_state_event(struct dfs_soc_priv_obj *dfs_soc_obj,
+					  enum dfs_agile_sm_evt event)
+{
+	enum dfs_agile_sm_state state;
+
+	state = dfs_agile_get_curr_state(dfs_soc_obj);
+	if (!(state < DFS_AGILE_S_MAX))
+		return;
+
+	dfs_debug(NULL, WLAN_DEBUG_DFS_AGILE, "[%s]%s, %s",
+		  dfs_soc_obj->dfs_agile_sm_hdl->name,
+		  dfs_agile_sm_info[state].name,
+		  dfs_agile_sm_event_names[event]);
+}
+
+QDF_STATUS dfs_agile_sm_deliver_evt(struct dfs_soc_priv_obj *dfs_soc_obj,
+				    enum dfs_agile_sm_evt event,
+				    uint16_t event_data_len,
+				    void *event_data)
+{
+	enum dfs_agile_sm_state old_state, new_state;
+	QDF_STATUS status;
+
+	DFS_AGILE_SM_SPIN_LOCK(dfs_soc_obj);
+	old_state = dfs_agile_get_curr_state(dfs_soc_obj);
+
+	/* Print current state and event received */
+	dfs_agile_sm_print_state_event(dfs_soc_obj, event);
+
+	status = dfs_agile_sm_deliver_event(dfs_soc_obj, event,
+					   event_data_len, event_data);
+
+	new_state = dfs_agile_get_curr_state(dfs_soc_obj);
+
+	/* Print new state after event if transition happens */
+	if (old_state != new_state)
+		dfs_agile_sm_print_state(dfs_soc_obj);
+	DFS_AGILE_SM_SPIN_UNLOCK(dfs_soc_obj);
+
+	return status;
+}
+
+QDF_STATUS dfs_agile_sm_create(struct dfs_soc_priv_obj *dfs_soc_obj)
+{
+	struct wlan_sm *sm;
+
+	sm = wlan_sm_create("DFS_AGILE", dfs_soc_obj,
+			    DFS_AGILE_S_INIT,
+			    dfs_agile_sm_info,
+			    QDF_ARRAY_SIZE(dfs_agile_sm_info),
+			    dfs_agile_sm_event_names,
+			    QDF_ARRAY_SIZE(dfs_agile_sm_event_names));
+	if (!sm) {
+		qdf_err("DFS AGILE SM allocation failed");
+		return QDF_STATUS_E_FAILURE;
+	}
+	dfs_soc_obj->dfs_agile_sm_hdl = sm;
+
+	qdf_spinlock_create(&dfs_soc_obj->dfs_agile_sm_lock);
+
+	/* Initialize the RCAC DFS index to default (no index). */
+	dfs_soc_obj->cur_agile_dfs_index = DFS_PSOC_NO_IDX;
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS dfs_agile_sm_destroy(struct dfs_soc_priv_obj *dfs_soc_obj)
+{
+	wlan_sm_delete(dfs_soc_obj->dfs_agile_sm_hdl);
+	qdf_spinlock_destroy(&dfs_soc_obj->dfs_agile_sm_lock);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+#ifdef 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);
+
+	if (!rcac_en) {
+		dfs_agile_sm_deliver_evt(dfs->dfs_soc_obj,
+					DFS_AGILE_SM_EV_AGILE_STOP,
+					0,
+					(void *)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, bool *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_agile_dfs_index].dfs;
+
+	dfs_agile_sm_deliver_evt(dfs_soc_obj,
+				DFS_AGILE_SM_EV_AGILE_DONE,
+				0,
+				(void *)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_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_debug(dfs, WLAN_DEBUG_DFS_AGILE, "Chosen rcac channel: %d",
+		  rcac_ch_freq);
+}
+#endif
+#endif

+ 1871 - 0
umac/dfs/core/src/misc/dfs_precac_list.c

@@ -0,0 +1,1871 @@
+/*
+ * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * DOC: This file has Precac Tree structure functions.
+ *
+ */
+
+#include "../dfs_precac_forest.h"
+#include <wlan_dfs_mlme_api.h>
+#include <wlan_dfs_utils_api.h>
+#include <dfs_process_radar_found_ind.h>
+#include <dfs_internal.h>
+
+/* dfs_calculate_bw_for_same_pri_ch() - When the primary channel is a new
+ * channel initialize the center channel frequency and bandwidth and when the
+ * primary is same as previous channel update the center frequency and the
+ * bandwidth.
+ * @dfs:               WLAN DFS structure
+ * @dfs_max_bw_info:   Structure to store precac tree root channel's
+ * information.
+ * @index:             The index that is manipulated.
+ * @ichan:             The DFS channel structure that holds the primary channel
+ *                     number, center frquency and channel bandwidth.
+ * @delimiter:         Band gap in MHz from the current primary channel to next
+ *                     primary channel.
+ */
+static void
+dfs_calculate_bw_for_same_pri_ch(struct wlan_dfs *dfs,
+				 struct dfs_channel_bw *dfs_max_bw_info,
+				 int index,
+				 struct dfs_channel *ichan,
+				 int *delimiter)
+{
+	uint8_t temp_bw = 0;
+	uint16_t tmp_center_freq;
+
+	dfs_max_bw_info[index].dfs_pri_ch_freq = ichan->dfs_ch_freq;
+	tmp_center_freq = ichan->dfs_ch_mhz_freq_seg1;
+
+	if (WLAN_IS_CHAN_MODE_20(ichan)) {
+		temp_bw = DFS_CHWIDTH_20_VAL;
+	} else if (WLAN_IS_CHAN_MODE_40(ichan)) {
+		temp_bw = DFS_CHWIDTH_40_VAL;
+	} else if (WLAN_IS_CHAN_MODE_80(ichan) ||
+		   WLAN_IS_CHAN_MODE_80_80(ichan)) {
+		temp_bw = DFS_CHWIDTH_80_VAL;
+	if (dfs_is_restricted_80p80mhz_supported(dfs) &&
+	    WLAN_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(ichan) &&
+	    (ichan->dfs_ch_vhtop_ch_freq_seg1 ==
+	     RESTRICTED_80P80_LEFT_80_CENTER_CHAN) &&
+	    (ichan->dfs_ch_vhtop_ch_freq_seg2 ==
+	     RESTRICTED_80P80_RIGHT_80_CENTER_CHAN)) {
+		temp_bw = DFS_CHWIDTH_165_VAL;
+		tmp_center_freq = RESTRICTED_80P80_CHAN_CENTER_FREQ;
+		}
+	} else if (WLAN_IS_CHAN_MODE_160(ichan)) {
+		temp_bw = DFS_CHWIDTH_160_VAL;
+		tmp_center_freq = ichan->dfs_ch_mhz_freq_seg2;
+	}
+	if (temp_bw > dfs_max_bw_info[index].dfs_max_bw) {
+		dfs_max_bw_info[index].dfs_max_bw = temp_bw;
+		*delimiter = dfs_max_bw_info[index].dfs_pri_ch_freq +
+			     dfs_max_bw_info[index].dfs_max_bw;
+		dfs_max_bw_info[index].dfs_center_ch_freq = tmp_center_freq;
+	}
+}
+
+/* dfs_init_precac_tree_node() - Initialise the preCAC BSTree node with the
+ *                               provided values.
+ * @node:      Precac_tree_node to be filled.
+ * @freq:      IEEE channel freq value.
+ * @bandwidth: Bandwidth of the channel.
+ * @depth:     Depth of the tree. The depth of the tree when the root is 160MHz
+ *             channel is 4, 80MHz is 3, 40MHz is 2 and 20MHz is 1.
+ */
+static inline void
+dfs_init_precac_tree_node_for_freq(struct precac_tree_node *node,
+				   uint16_t freq,
+				   uint8_t bandwidth,
+				   uint8_t depth)
+{
+	node->left_child = NULL;
+	node->right_child = NULL;
+	node->ch_freq = freq;
+	node->ch_ieee = utils_dfs_freq_to_chan(freq);
+	node->n_caced_subchs = 0;
+	node->n_nol_subchs = 0;
+	node->n_valid_subchs = N_SUBCHS_FOR_BANDWIDTH(bandwidth);
+	node->bandwidth = bandwidth;
+	node->depth = depth;
+
+}
+
+/* dfs_descend_precac_tree_for_freq() - Descend into the precac BSTree based on
+ *                             the channel provided. If the channel is less than
+ *                             given node's channel, descend left, else right.
+ * @node:    Precac BSTree node.
+ * @chan_freq: Channel freq whose node is to be found.
+ *
+ * Return: the next precac_tree_node (left child or right child of
+ * current node).
+ */
+
+struct precac_tree_node *
+dfs_descend_precac_tree_for_freq(struct precac_tree_node *node,
+				 uint16_t chan_freq)
+{
+	if (!node)
+		return NULL;
+
+	if (chan_freq < node->ch_freq)
+		return node->left_child;
+	else
+		return node->right_child;
+}
+
+/*dfs_insert_node_into_bstree_for_freq() - Insert a new preCAC BSTree node.
+ * @root:      The preCAC BSTree root pointer.
+ * @chan:      IEEE freq of the new node.
+ * @bandwidth: Bandwidth of the channel.
+ * @depth:     Depth of the tree. The depth of the tree when the root is 160MHz
+ *             channel is 4, 80MHz is 3, 40MHz is 2 and 20MHz is 1.
+ *
+ * Return: EOK if new node is allocated, else return ENOMEM.
+ */
+static QDF_STATUS
+dfs_insert_node_into_bstree_for_freq(struct precac_tree_node **root,
+				     uint16_t chan_freq,
+				     uint8_t bandwidth,
+				     uint8_t depth)
+{
+	struct precac_tree_node *new_node = NULL;
+	struct precac_tree_node *curr_node, *prev_node = NULL;
+	QDF_STATUS status = EOK;
+
+	new_node = qdf_mem_malloc(sizeof(*new_node));
+	if (!new_node)
+		return -ENOMEM;
+	dfs_init_precac_tree_node_for_freq(new_node,
+					   chan_freq,
+					   bandwidth,
+					   depth);
+
+	/* If root node is null, assign the newly allocated node
+	 * to this node and return.
+	 */
+	if (!(*root)) {
+		*root = new_node;
+		return status;
+	}
+
+	curr_node = *root;
+	/* Find the leaf node which will be the new node's parent */
+	while (curr_node) {
+		prev_node = curr_node;
+		curr_node = dfs_descend_precac_tree_for_freq(curr_node,
+							     chan_freq);
+	}
+
+	/* Add to the leaf node */
+	if (chan_freq < prev_node->ch_freq)
+		prev_node->left_child = new_node;
+	else
+		prev_node->right_child = new_node;
+
+	return status;
+}
+
+/* dfs_find_leftmost_leaf_of_precac_tree() - Find the leftmost leaf node of
+ *                                           BSTree rooted by the given node.
+ * @node: PreCAC BSTree node whose leftmost leaf is required.
+ *
+ * Return: Pointer of struct precac_tree_node.
+ */
+static inline struct precac_tree_node *
+dfs_find_leftmost_leaf_of_precac_tree(struct precac_tree_node *node)
+{
+	if (!node)
+		return NULL;
+
+	while (node->left_child)
+		node = node->left_child;
+
+	return node;
+}
+
+/*
+ * dfs_free_precac_tree_nodes() - Free the tree nodes starting from
+ *                                the root node.
+ *                                NOTE: This changes tree structure, hence
+ *                                caller should be in a lock.
+ * @dfs:          Pointer to WLAN DFS structure.
+ * @precac_entry: Precac list entry whose BSTree is to be freed.
+ *
+ * Consider the below Binary tree,
+ *
+ *                         A
+ *                        / \
+ *                       B   C
+ *                        \
+ *                         D
+ *
+ * Steps for freeing this tree,
+ *
+ * 1. Find the leftmost leaf node of the Binary Tree.
+ * 2. Set current node as root node.
+ * 3. If current node has right child, add right child of current node as left
+ *    child of leftmost leaf.
+ * 4. Update the leftmost leaf.
+ * 5. Update current node to left child and remove the node.
+ * 6. Repeat steps 3 to 5 till current node is NULL.
+ *
+ * The above Binary Tree structure during the afore mentioned steps:
+ *
+ *    A            A
+ *   / \          /
+ *  B   C  -->    B     -->    B     -->    B   -->   C   -->  D   -->  .
+ *   \           / \          / \          /         /
+ *    D         C   D        C   D        C         D
+ *                                       /
+ *                                      D
+ *
+ */
+
+static void dfs_free_precac_tree_nodes(struct wlan_dfs *dfs,
+				       struct dfs_precac_entry *precac_entry)
+{
+	struct precac_tree_node *root_node, *left_most_leaf, *prev_root_node;
+
+	root_node = precac_entry->tree_root;
+	if (!root_node) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "tree root is null");
+		return;
+	}
+
+	/* Find leftmost leaf node */
+	left_most_leaf = root_node;
+	left_most_leaf = dfs_find_leftmost_leaf_of_precac_tree(left_most_leaf);
+	if (!left_most_leaf) {
+		/* should've been caught in previous check, assert here */
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Could not find leaf, deletion failed! Asserting");
+		QDF_ASSERT(0);
+		return;
+	}
+
+	while (root_node) {
+		if (root_node->right_child) {
+			/* Add the right subtree as the left child of the
+			 * leftmost leaf
+			 */
+			left_most_leaf->left_child = root_node->right_child;
+			/* Update left most leaf */
+			left_most_leaf = dfs_find_leftmost_leaf_of_precac_tree(
+						left_most_leaf);
+			if (!left_most_leaf) {
+				dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+					"Could not find leaf, deletion failed");
+				QDF_ASSERT(0);
+				return;
+			}
+		}
+		/* Free current node */
+		prev_root_node = root_node;
+		root_node = root_node->left_child;
+		qdf_mem_free(prev_root_node);
+	}
+}
+
+/**
+ * dfs_get_num_cur_subchans_in_node_freq() - Get number of excluded channels
+ *                                           inside the current node.
+ * @dfs:  Pointer to wlan_dfs structure.
+ * @node: Node to be checked.
+ *
+ * Return: uint8_t.
+ * Return the number of excluded (current operating channels in CAC) that are in
+ * the given tree node range.
+ */
+static uint8_t
+dfs_get_num_cur_subchans_in_node_freq(struct wlan_dfs *dfs,
+				      struct precac_tree_node *node)
+{
+	uint16_t exclude_pri_ch_freq, exclude_sec_ch_freq;
+	uint8_t chwidth_val = DFS_CHWIDTH_80_VAL;
+	uint8_t n_exclude_subchs = 0;
+
+	exclude_pri_ch_freq =
+		dfs->dfs_curchan->dfs_ch_mhz_freq_seg1;
+	exclude_sec_ch_freq =
+		dfs->dfs_curchan->dfs_ch_mhz_freq_seg2;
+	if (WLAN_IS_CHAN_MODE_160(dfs->dfs_curchan)) {
+		if (exclude_sec_ch_freq < exclude_pri_ch_freq)
+			exclude_sec_ch_freq -=
+				DFS_160MHZ_SECSEG_CHAN_OFFSET;
+		else
+			exclude_sec_ch_freq +=
+				DFS_160MHZ_SECSEG_CHAN_OFFSET;
+	}
+
+	if (WLAN_IS_CHAN_MODE_20(dfs->dfs_curchan))
+		chwidth_val = DFS_CHWIDTH_20_VAL;
+	else if (WLAN_IS_CHAN_MODE_40(dfs->dfs_curchan))
+		chwidth_val = DFS_CHWIDTH_40_VAL;
+
+	/* Check if the channel is a subset of the tree node and if it's
+	 * currently in CAC period. This is to avoid excluding channels twice,
+	 * one below and one in the already CACed channels exclusion (in the
+	 * caller API).
+	 */
+	if (IS_WITHIN_RANGE(exclude_pri_ch_freq,
+			   node->ch_freq,
+			   (node->bandwidth / 2)) &&
+	   dfs_is_pcac_required_for_freq(node, exclude_pri_ch_freq))
+		n_exclude_subchs += N_SUBCHS_FOR_BANDWIDTH(chwidth_val);
+	if (IS_WITHIN_RANGE(exclude_sec_ch_freq,
+			   node->ch_freq,
+			   (node->bandwidth / 2)) &&
+	   dfs_is_pcac_required_for_freq(node, exclude_sec_ch_freq))
+		n_exclude_subchs += N_SUBCHS_FOR_BANDWIDTH(chwidth_val);
+	return n_exclude_subchs;
+}
+
+/* dfs_is_cac_needed_for_bst_node_for_freq() - For a requested bandwidth, find
+ *                                             if the current preCAC BSTree
+ *                                             node needs CAC.
+ * @dfs:           Pointer to wlan_dfs struct.
+ * @node:          Node to be checked.
+ * @req_bandwidth: bandwidth of channel requested.
+ *
+ * Return: TRUE/FALSE.
+ * Return true if there exists a channel of the requested bandwidth
+ * for the node which is not CAC done, else false.
+ */
+static bool
+dfs_is_cac_needed_for_bst_node_for_freq(struct wlan_dfs *dfs,
+					struct precac_tree_node *node,
+					uint8_t req_bandwidth)
+{
+	uint8_t n_subchs_for_req_bw, n_allowed_subchs, n_excluded_subchs;
+
+	if (!node)
+		return false;
+
+	/* Find the number of subchannels for the requested bandwidth */
+	n_excluded_subchs = dfs_get_num_cur_subchans_in_node_freq(dfs, node);
+	n_subchs_for_req_bw = N_SUBCHS_FOR_BANDWIDTH(req_bandwidth);
+	n_allowed_subchs = node->n_valid_subchs -
+			(node->n_nol_subchs + n_excluded_subchs);
+
+	/* Return false if,
+	 * 1. Number of allowed subchannels (all subchannels other than
+	 *    current operating sub-channels and NOL sub-channels) in the
+	 *    current node is less than the requested number of subchannels.
+	 * 3. If the number CAC done subchannels + NOL subchannels + current
+	 *    operating subchannels in the current node is equal to number of
+	 *    valid subchannels in the node.
+	 * else, return true.
+	 */
+	if ((n_allowed_subchs < n_subchs_for_req_bw) ||
+	    ((node->n_caced_subchs + node->n_nol_subchs + n_excluded_subchs) ==
+	     node->n_valid_subchs))
+		return false;
+
+	return true;
+}
+
+/* dfs_create_precac_tree_for_freq() - Fill precac entry tree (level insertion).
+ * @dfs:       WLAN DFS structure
+ * @ch_freq:   root_node freq.
+ * @root:      Pointer to the node that will be filled and inserted as tree
+ *             root.
+ * @bandwidth: Bandwidth value of the root.
+ */
+static QDF_STATUS
+dfs_create_precac_tree_for_freq(struct wlan_dfs *dfs,
+				uint16_t ch_freq,
+				struct precac_tree_node **root,
+				int bandwidth)
+{
+	int chan_freq, i;
+	QDF_STATUS status = EOK;
+	struct precac_tree_offset_for_different_bw current_mode;
+	uint8_t top_lvl_step;
+	bool is_node_part_of_165_tree = false;
+
+	if (ch_freq == RESTRICTED_80P80_LEFT_80_CENTER_FREQ ||
+	    ch_freq == RESTRICTED_80P80_RIGHT_80_CENTER_FREQ)
+		is_node_part_of_165_tree = true;
+
+	switch (bandwidth) {
+	case DFS_CHWIDTH_160_VAL:
+			current_mode = offset160;
+			break;
+	case DFS_CHWIDTH_80_VAL:
+			current_mode = offset80;
+			break;
+	case DFS_CHWIDTH_40_VAL:
+			current_mode = offset40;
+			break;
+	case DFS_CHWIDTH_20_VAL:
+			current_mode = offset20;
+			break;
+	default:
+			current_mode = default_offset;
+			break;
+	}
+	top_lvl_step = current_mode.initial_and_next_offsets[0][1];
+	for (i = 0; i < current_mode.tree_depth; i++) {
+		/* In offset array,
+		 * column 0 is initial chan offset,
+		 * column 1 is next chan offset.
+		 * Boundary offset is initial offset and next offset
+		 * of root level (since root level can have only 1 node)
+		 */
+		int offset =
+		    current_mode.initial_and_next_offsets[i][START_INDEX];
+		int step = current_mode.initial_and_next_offsets[i][STEP_INDEX];
+		int boundary_offset = offset + top_lvl_step;
+		uint8_t depth = is_node_part_of_165_tree ? i + 1 : i;
+
+		for (; offset < boundary_offset; offset += step) {
+			chan_freq = (int)ch_freq + offset;
+			status =
+			    dfs_insert_node_into_bstree_for_freq(root,
+								 chan_freq,
+								 bandwidth,
+								 depth);
+			if (status)
+				return status;
+		}
+		bandwidth /= 2;
+	}
+
+	return status;
+}
+
+static QDF_STATUS
+dfs_precac_create_165mhz_precac_entry(struct wlan_dfs *dfs,
+				      struct dfs_precac_entry *precac_entry)
+{
+	QDF_STATUS status;
+
+	precac_entry->center_ch_freq =
+		RESTRICTED_80P80_CHAN_CENTER_FREQ;
+	precac_entry->center_ch_ieee =
+		utils_dfs_freq_to_chan(precac_entry->center_ch_freq);
+	precac_entry->bw = DFS_CHWIDTH_160_VAL;
+	/* non_dfs_subch_count will be updated once the channels are marked. */
+	precac_entry->non_dfs_subch_count = 0;
+	precac_entry->dfs = dfs;
+	dfs_insert_node_into_bstree_for_freq(&precac_entry->tree_root,
+					     RESTRICTED_80P80_CHAN_CENTER_FREQ,
+					     DFS_CHWIDTH_160_VAL,
+					     DEPTH_160_ROOT);
+	status =
+		dfs_create_precac_tree_for_freq
+		(dfs,
+					RESTRICTED_80P80_LEFT_80_CENTER_FREQ,
+					&precac_entry->tree_root->left_child,
+					DFS_CHWIDTH_80_VAL);
+	if (!status)
+		status =
+		    dfs_create_precac_tree_for_freq(
+			    dfs,
+			    RESTRICTED_80P80_RIGHT_80_CENTER_FREQ,
+			    &precac_entry->tree_root->right_child,
+			    DFS_CHWIDTH_80_VAL);
+	TAILQ_INSERT_TAIL(
+			&dfs->dfs_precac_list,
+			precac_entry, pe_list);
+	return status;
+}
+
+/* dfs_is_tree_node_marked_as_cac_for_freq() - Check if preCAC BSTree node is
+ * marked as CAC.
+ * @root: Pointer to root node of the preCAC BSTree.
+ * @freq: 20MHz channel to be checked if marked as CAC done already.
+ *
+ * Return: True if already marked, else false.
+ */
+static bool
+dfs_is_tree_node_marked_as_cac_for_freq(struct precac_tree_node *root,
+					uint16_t freq)
+{
+	struct precac_tree_node *curr_node = root;
+
+	while (curr_node) {
+		if (!curr_node->n_caced_subchs)
+			return false;
+		if (curr_node->ch_freq == freq)
+			return curr_node->n_caced_subchs;
+		curr_node = dfs_descend_precac_tree_for_freq(curr_node,
+							     freq);
+	}
+	return false;
+}
+
+/* dfs_unmark_tree_node_as_nol_for_freq() - Unmark the preCAC BSTree node as
+ * NOL.
+ * @dfs:          Pointer to WLAN DFS structure.
+ * @precac_entry: Precac_list entry pointer.
+ * @chan_freq      IEEE channel freq to be marked.
+ *
+ * Note: The input channel is always of 20MHz bandwidth.
+ */
+
+static void
+dfs_unmark_tree_node_as_nol_for_freq(struct wlan_dfs *dfs,
+				     struct dfs_precac_entry *precac_entry,
+				     uint16_t chan_freq)
+{
+	struct precac_tree_node *curr_node;
+
+	if (!precac_entry->tree_root) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Precac tree root pointer is NULL!");
+		return;
+	}
+	curr_node = precac_entry->tree_root;
+	while (curr_node) {
+		if (curr_node->n_nol_subchs)
+			curr_node->n_nol_subchs--;
+		else
+			return;
+		curr_node = dfs_descend_precac_tree_for_freq(curr_node,
+							     chan_freq);
+	}
+}
+
+/**
+ * dfs_update_non_dfs_subchannel_count() - API to update the preCAC entry
+ * with the given non DFS subchannel count.
+ * @dfs:       Pointer to DFS object.
+ * @frequency: Frequency whose corresponding preCAC entry needs to be updated.
+ * @count:     Non DFS subchannel count for the preCAC entry.
+ */
+static void
+dfs_update_non_dfs_subchannel_count(struct wlan_dfs *dfs,
+				    qdf_freq_t frequency,
+				    uint8_t count)
+{
+	struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL;
+
+	PRECAC_LIST_LOCK(dfs);
+	TAILQ_FOREACH_SAFE(precac_entry,
+			   &dfs->dfs_precac_list,
+			   pe_list,
+			   tmp_precac_entry) {
+		if (IS_WITHIN_RANGE_STRICT(frequency,
+					   precac_entry->center_ch_freq,
+					   (precac_entry->bw/2))) {
+			precac_entry->non_dfs_subch_count = count;
+			break;
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+}
+
+static void
+dfs_mark_non_dfs_as_precac_done(struct wlan_dfs *dfs,
+				uint16_t dfs_pri_ch_freq,
+				enum wlan_phymode mode)
+{
+	struct dfs_channel *ichan, lc;
+
+	ichan = &lc;
+	dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj,
+					  dfs_pri_ch_freq,
+					  0,
+					  mode,
+					  &ichan->dfs_ch_freq,
+					  &ichan->dfs_ch_flags,
+					  &ichan->dfs_ch_flagext,
+					  &ichan->dfs_ch_ieee,
+					  &ichan->dfs_ch_vhtop_ch_freq_seg1,
+					  &ichan->dfs_ch_vhtop_ch_freq_seg2,
+					  &ichan->dfs_ch_mhz_freq_seg1,
+					  &ichan->dfs_ch_mhz_freq_seg2);
+	if (!WLAN_IS_CHAN_DFS(ichan)) {
+		PRECAC_LIST_UNLOCK(dfs);
+		dfs_mark_precac_done_for_freq(dfs,
+					      ichan->dfs_ch_mhz_freq_seg1,
+					      0,
+					      CH_WIDTH_80MHZ);
+		dfs_update_non_dfs_subchannel_count(dfs,
+						    ichan->dfs_ch_mhz_freq_seg1,
+						    N_SUBCHANS_FOR_80BW);
+		PRECAC_LIST_LOCK(dfs);
+	} else if (!WLAN_IS_CHAN_DFS_CFREQ2(ichan)) {
+		PRECAC_LIST_UNLOCK(dfs);
+		dfs_mark_precac_done_for_freq(dfs,
+					      ichan->dfs_ch_mhz_freq_seg2,
+					      0,
+					      CH_WIDTH_80MHZ);
+		dfs_update_non_dfs_subchannel_count(dfs,
+						    ichan->dfs_ch_mhz_freq_seg2,
+						    N_SUBCHANS_FOR_80BW);
+		PRECAC_LIST_LOCK(dfs);
+	}
+}
+
+static QDF_STATUS
+dfs_precac_create_precac_entry(struct wlan_dfs *dfs,
+			       struct dfs_precac_entry *precac_entry,
+			       struct dfs_channel_bw *dfs_max_bw_info,
+			       int index)
+{
+	QDF_STATUS status;
+	uint16_t precac_center_freq =
+	    dfs_max_bw_info[index].dfs_center_ch_freq;
+
+	precac_entry->center_ch_freq = precac_center_freq;
+	precac_entry->center_ch_ieee =
+	utils_dfs_freq_to_chan(precac_center_freq);
+	precac_entry->bw = dfs_max_bw_info[index].dfs_max_bw;
+	/* non_dfs_subch_count will be updated once the channels are marked. */
+	precac_entry->non_dfs_subch_count = 0;
+	precac_entry->dfs = dfs;
+	status =
+	    dfs_create_precac_tree_for_freq(dfs,
+					    precac_entry->center_ch_freq,
+					    &precac_entry->tree_root,
+					    precac_entry->bw);
+	if (status)
+		dfs_debug(dfs, WLAN_DEBUG_DFS,
+			  "PreCAC entry for channel %d not created",
+			  precac_entry->center_ch_ieee);
+	else
+	    TAILQ_INSERT_TAIL(&dfs->dfs_precac_list, precac_entry, pe_list);
+
+	return status;
+}
+
+/* dfs_mark_tree_node_as_cac_done_for_freq() - Mark the preCAC BSTree node as
+ * CAC done.
+ * @dfs:          Pointer to WLAN DFS structure.
+ * @precac_entry: Precac_list entry pointer.
+ * @chan_freq:    IEEE channel freq to be marked.
+ *
+ * Note: The input channel is always of 20MHz bandwidth.
+ */
+static void
+dfs_mark_tree_node_as_cac_done_for_freq(struct wlan_dfs *dfs,
+					struct dfs_precac_entry *precac_entry,
+					uint16_t chan_freq)
+{
+	struct precac_tree_node *curr_node;
+
+	if (!precac_entry->tree_root) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Precac tree root pointer is NULL!");
+		return;
+	}
+
+	curr_node = precac_entry->tree_root;
+	/**
+	 * Check if the channel is already marked and return if true.
+	 * This will happen in scenarios like the following:
+	 * preCAC is running on channel 128 in HT20 mode (note: 124 is already
+	 * marked. Now if the mode is switched to HT40, preCAC is restarted
+	 * and the new channel picked for preCAC is 126 HT40. Here, 124
+	 * will be already marked since it was completed in HT20 mode.
+	 * This may happen for any mode switches (20<->40<->80 MHz).
+	 */
+	if (dfs_is_tree_node_marked_as_cac_for_freq(curr_node, chan_freq))
+		return;
+
+	while (curr_node) {
+		/* Update the current node's CACed subchannels count only
+		 * if it's less than maximum subchannels, else return.
+		 */
+		if (curr_node->n_caced_subchs <
+		    N_SUBCHS_FOR_BANDWIDTH(curr_node->bandwidth))
+			curr_node->n_caced_subchs++;
+		curr_node = dfs_descend_precac_tree_for_freq(curr_node,
+							     chan_freq);
+	}
+}
+
+/* dfs_unmark_tree_node_as_cac_done_for_freq() - Unmark the preCAC BSTree
+ *                                       node as CAC done.
+ * @precac_entry: Precac_list entry pointer.
+ * @chan_freq:    IEEE channel freq to be marked.
+ *
+ * Note: The input channel is always of 20MHz bandwidth.
+ */
+static void
+dfs_unmark_tree_node_as_cac_done_for_freq(struct dfs_precac_entry
+					  *precac_entry, uint16_t chan_freq)
+{
+	struct precac_tree_node *curr_node = precac_entry->tree_root;
+
+	while (curr_node) {
+		if (curr_node->n_caced_subchs)
+			curr_node->n_caced_subchs--;
+		else
+			return;
+		curr_node = dfs_descend_precac_tree_for_freq(curr_node,
+							     chan_freq);
+	}
+}
+
+void dfs_deinit_precac_list(struct wlan_dfs *dfs)
+{
+	struct dfs_precac_entry *tmp_precac_entry, *precac_entry;
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"Free the list of VHT80 frequencies from linked list");
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_list))
+		TAILQ_FOREACH_SAFE(precac_entry,
+				   &dfs->dfs_precac_list,
+				   pe_list, tmp_precac_entry) {
+			dfs_free_precac_tree_nodes(dfs, precac_entry);
+			TAILQ_REMOVE(&dfs->dfs_precac_list,
+				     precac_entry, pe_list);
+			qdf_mem_free(precac_entry);
+		}
+	PRECAC_LIST_UNLOCK(dfs);
+
+}
+
+/* dfs_mark_tree_node_as_nol_for_freq() - Mark the preCAC BSTree node as NOL.
+ * @dfs:          Pointer to WLAN DFS structure.
+ * @precac_entry: Precac_list entry pointer.
+ * @freq:         IEEE channel freq to be marked.
+ *
+ * Note: The input channel is always of 20MHz bandwidth.
+ */
+static void
+dfs_mark_tree_node_as_nol_for_freq(struct wlan_dfs *dfs,
+				   struct dfs_precac_entry *pcac,
+				   uint16_t freq)
+{
+	struct precac_tree_node *curr_node;
+
+	if (!pcac->tree_root) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Precac tree root pointer is NULL!");
+		return;
+	}
+	curr_node = pcac->tree_root;
+	while (curr_node) {
+		if (curr_node->n_nol_subchs <
+		    N_SUBCHS_FOR_BANDWIDTH(curr_node->bandwidth)) {
+			curr_node->n_nol_subchs++;
+		} else {
+			dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+				"Radarfound on an already marked NOL channel!");
+			return;
+		}
+		if (freq == curr_node->ch_freq) {
+			if (curr_node->n_caced_subchs) {
+				/* remove cac done status for this node
+				 * and it's parents, since this node
+				 * now requires cac (after NOL expiry)
+				 */
+				dfs_unmark_tree_node_as_cac_done_for_freq(pcac,
+									  freq);
+			}
+		}
+		curr_node = dfs_descend_precac_tree_for_freq(curr_node,
+							     freq);
+	}
+}
+
+/* dfs_fill_max_bw_for_chan() - Finds unique precac tree node in the channel
+ * list and stores the primary channel frequency, maximum bandwidth and the
+ * center frequency. The algorithm is based on the data structure ic_channels
+ * where the channels are organized as 36HT20, 36HT40, 36HT80,... and so on..
+ * @dfs:               WLAN DFS structure
+ * @dfs_max_bw_info:   Structure to store precac tree root channel's
+ * information.
+ * @num_precac_roots:  Number of unique.
+ */
+static void dfs_fill_max_bw_for_chan(struct wlan_dfs *dfs,
+				     struct dfs_channel_bw *dfs_max_bw_info,
+				     int *num_precac_roots)
+{
+	int i;
+	int n_total_chans = 0;
+	int  n_chanseg_found = 0;
+	int prev_ch_freq = 0;
+	int delimiter = 0;
+
+	dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &n_total_chans);
+	for (i = 0; i < n_total_chans; i++) {
+		struct dfs_channel *ichan = NULL, lc;
+		/* The array index of the bandwidth list that needs to be
+		 * updated.
+		 */
+		int index_to_update;
+
+		ichan = &lc;
+		dfs_mlme_get_dfs_channels_for_freq
+			(dfs->dfs_pdev_obj,
+			 &ichan->dfs_ch_freq,
+			 &ichan->dfs_ch_flags,
+			 &ichan->dfs_ch_flagext,
+			 &ichan->dfs_ch_ieee,
+			 &ichan->dfs_ch_vhtop_ch_freq_seg1,
+			 &ichan->dfs_ch_vhtop_ch_freq_seg2,
+			 &ichan->dfs_ch_mhz_freq_seg1,
+			 &ichan->dfs_ch_mhz_freq_seg2,
+			 i);
+		if (!WLAN_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(ichan))
+			continue;
+		if (ichan->dfs_ch_freq == prev_ch_freq) {
+			/* When the primary channels are common for consecutive
+			 * channels, for example 36HT20, 36HT40, 36HT80,...,
+			 * only the center frequecy and the bandwidth have to be
+			 * updated.
+			 */
+			index_to_update = n_chanseg_found - 1;
+			dfs_calculate_bw_for_same_pri_ch(dfs,
+							 dfs_max_bw_info,
+							 index_to_update,
+							 ichan,
+							 &delimiter);
+		} else if (ichan->dfs_ch_freq < delimiter) {
+			continue;
+		} else {
+			prev_ch_freq = ichan->dfs_ch_freq;
+			/* When the primary channels are unique and consecutive
+			 * like 149HT20, 153HT20, 157HT20,..., the new element
+			 * has to be initialized here.
+			 */
+			index_to_update = n_chanseg_found;
+			dfs_calculate_bw_for_same_pri_ch(dfs,
+							 dfs_max_bw_info,
+							 n_chanseg_found,
+							 ichan,
+							 &delimiter);
+			n_chanseg_found++;
+		}
+	}
+	*num_precac_roots = n_chanseg_found;
+	for (i = 0; i < *num_precac_roots; i++)
+		dfs_debug(dfs, WLAN_DEBUG_DFS,
+			  "index = %d pri: %d centr: %d bw: %d",
+			  i,
+			  dfs_max_bw_info[i].dfs_pri_ch_freq,
+			  dfs_max_bw_info[i].dfs_center_ch_freq,
+			  dfs_max_bw_info[i].dfs_max_bw);
+}
+
+/* dfs_find_cac_status_for_chan_for_freq() - Find CAC-done status for the
+ *                                  channel in the precac Binary Search Tree.
+ *				    Return true if CAC done, else false.
+ * @dfs_precac_entry: Precac entry which has the root of the precac BSTree.
+ * @chan_freq:        IEEE channel freq. This is the center of a
+ *                    20/40/80/160/165 MHz channel and the center channel is
+ *                    unique irrespective of the bandwidth
+ *                    (20/40/80/160/165 MHz).
+ */
+static bool
+dfs_find_cac_status_for_chan_for_freq(struct dfs_precac_entry *precac_entry,
+				      uint16_t chan_freq)
+{
+	struct precac_tree_node *node = precac_entry->tree_root;
+	uint8_t n_cur_lvl_subchs = node->n_valid_subchs;
+
+	while (node) {
+		if (node->ch_freq == chan_freq)
+			return (node->n_caced_subchs == n_cur_lvl_subchs) ?
+				true : false;
+
+		n_cur_lvl_subchs /= 2;
+		node = dfs_descend_precac_tree_for_freq(node, chan_freq);
+	}
+
+	return false;
+}
+
+/* dfs_find_ieee_ch_from_precac_tree_for_freq() - from the given preCAC tree,
+ *                                       find a IEEE freq of the given bandwidth
+ *                                       which is valid and needs CAC.
+ * @root:   PreCAC BSTree root pointer.
+ * @req_bw: Bandwidth of channel requested.
+ *
+ * Return: IEEE channel frequency.
+ * Return a valid freq value which needs CAC for the given bandwidth, else
+ * return 0.
+ */
+static uint16_t
+dfs_find_ieee_ch_from_precac_tree_for_freq(struct wlan_dfs *dfs,
+					   struct precac_tree_node *root,
+					   uint8_t req_bw)
+{
+	struct precac_tree_node *curr_node;
+
+	if (!dfs_is_cac_needed_for_bst_node_for_freq(dfs, root, req_bw))
+		return 0;
+
+	curr_node = root;
+	while (curr_node) {
+		if (curr_node->bandwidth == req_bw) {
+			/* find if current node in valid state (req.) */
+			if (dfs_is_cac_needed_for_bst_node_for_freq(dfs,
+								    curr_node,
+								    req_bw))
+				return curr_node->ch_freq;
+			else
+				return 0;
+		}
+
+		/* Find if we need to go to left or right subtree.
+		 * Note: If both are available, go to left.
+		 */
+		if (!dfs_is_cac_needed_for_bst_node_for_freq(
+				dfs,
+				curr_node->left_child,
+				req_bw))
+			curr_node = curr_node->right_child;
+		else
+			curr_node = curr_node->left_child;
+	}
+	/* If requested bandwidth is invalid, return 0 here */
+	return 0;
+}
+
+/**
+ * dfs_find_precac_state_of_node() - Find the preCAC state of the given channel.
+ * @channel: Channel whose preCAC state is to be found.
+ * @precac_entry: PreCAC entry where the channel exists.
+ *
+ * Return, enum value of type precac_chan_state.
+ */
+enum precac_chan_state
+dfs_find_precac_state_of_node(qdf_freq_t channel,
+			      struct dfs_precac_entry *precac_entry)
+{
+	struct precac_tree_node *node = precac_entry->tree_root;
+
+	while (node) {
+		if (node->ch_freq == channel) {
+			if (node->n_nol_subchs)
+				return PRECAC_NOL;
+			if (node->n_caced_subchs ==
+			    N_SUBCHS_FOR_BANDWIDTH(node->bandwidth))
+				return PRECAC_DONE;
+			return PRECAC_REQUIRED;
+		}
+		node = dfs_descend_precac_tree_for_freq(node, channel);
+	}
+	return PRECAC_ERR;
+}
+
+/*
+ * dfs_get_ieeechan_for_precac_for_freq() - Get chan frequency for preCAC.
+ * @dfs: Pointer to wlan_dfs.
+ * @exclude_pri_ch_freq: Primary frequency to be excluded.
+ * @exclude_sec_ch_freq: Secondary freqeuncy to be excluded.
+ * @bandwidth: Bandwidth.
+ */
+uint16_t dfs_get_ieeechan_for_precac_for_freq(struct wlan_dfs *dfs,
+					      uint16_t exclude_pri_ch_freq,
+					      uint16_t exclude_sec_ch_freq,
+					      uint8_t bw)
+{
+	struct dfs_precac_entry *precac_entry;
+	struct precac_tree_node *root = NULL;
+	uint16_t ieee_chan_freq = 0;
+
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+		 "current operating channel(s) to be excluded = [%u] [%u]",
+		 exclude_pri_ch_freq,
+		 exclude_sec_ch_freq);
+
+	/* If interCAC is enabled, prioritize the desired channel first before
+	 * using the normal logic to find a channel for preCAC.
+	 */
+	ieee_chan_freq = dfs_configure_deschan_for_precac(dfs);
+
+	if (ieee_chan_freq)
+		goto exit;
+
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_list)) {
+		TAILQ_FOREACH(precac_entry, &dfs->dfs_precac_list,
+			      pe_list) {
+			root = precac_entry->tree_root;
+			ieee_chan_freq =
+				dfs_find_ieee_ch_from_precac_tree_for_freq(dfs,
+									   root,
+									   bw);
+			if (ieee_chan_freq)
+				break;
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+exit:
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "Channel picked for preCAC = %u",
+		 ieee_chan_freq);
+
+	return ieee_chan_freq;
+}
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+#ifdef CONFIG_CHAN_FREQ_API
+enum precac_chan_state
+dfs_get_precac_chan_state_for_freq(struct wlan_dfs *dfs, uint16_t pcac_freq)
+{
+	struct dfs_channel chan;
+	struct dfs_precac_entry *tmp_precac_entry;
+	struct precac_tree_node *root = NULL;
+	enum precac_chan_state ret = PRECAC_ERR;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (QDF_STATUS_SUCCESS !=
+	    dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj,
+					      pcac_freq, 0,
+					      WLAN_PHYMODE_11AC_VHT80,
+					      &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)) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Intermediate channel not found");
+		return PRECAC_ERR;
+	}
+
+	if (!WLAN_IS_CHAN_DFS(&chan)) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"[%d] Not a DFS channel", pcac_freq);
+		return PRECAC_ERR;
+	}
+
+	PRECAC_LIST_LOCK(dfs);
+	if (dfs_is_precac_timer_running(dfs)) {
+		tmp_precac_entry = TAILQ_FIRST(&dfs->dfs_precac_list);
+		if (tmp_precac_entry && (tmp_precac_entry->vht80_ch_freq ==
+					chan.dfs_ch_mhz_freq_seg1)) {
+			ret = PRECAC_NOW;
+			goto end;
+		}
+	}
+
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_precac_list, pe_list) {
+		if (tmp_precac_entry->vht80_ch_freq ==
+				chan.dfs_ch_mhz_freq_seg1) {
+			root = tmp_precac_entry->tree_root;
+			if (root->n_nol_subchs)
+				ret = PRECAC_NOL;
+			else if (root->n_caced_subchs ==
+					N_SUBCHS_FOR_BANDWIDTH(root->bandwidth))
+				ret = PRECAC_DONE;
+			else
+				ret = PRECAC_REQUIRED;
+			goto end;
+		}
+	}
+end:
+	PRECAC_LIST_UNLOCK(dfs);
+	return ret;
+}
+#endif
+#endif
+
+/*
+ * dfs_init_precac_list() - Initialize preCAC lists.
+ * @dfs: Pointer to wlan_dfs.
+ */
+void dfs_init_precac_list(struct wlan_dfs *dfs)
+{
+	u_int i;
+	uint8_t found;
+	struct dfs_precac_entry *tmp_precac_entry;
+	int nchans = 0;
+	QDF_STATUS status;
+	struct dfs_channel_bw *dfs_max_bw_info;
+	int num_precac_roots;
+
+	/*
+	 * We need to prepare list of uniquee center frequencies of maximum
+	 * possible bandwidths. But at the beginning we do not know how many
+	 * unique frequencies are present. Therefore, we calculate the MAX size
+	 * and allocate a temporary list/array. However we fill the temporary
+	 * array with unique frequencies and copy the unique list of frequencies
+	 * to the final list with exact size.
+	 */
+	dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &nchans);
+	dfs_max_bw_info = qdf_mem_malloc(nchans *
+		sizeof(struct dfs_channel_bw));
+	if (!dfs_max_bw_info) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"memory allocation failed");
+		return;
+	}
+	dfs_fill_max_bw_for_chan(dfs, dfs_max_bw_info, &num_precac_roots);
+
+	TAILQ_INIT(&dfs->dfs_precac_list);
+
+	PRECAC_LIST_LOCK(dfs);
+	for (i = 0; i < num_precac_roots; i++) {
+		uint16_t pri_chan_cfreq = dfs_max_bw_info[i].dfs_center_ch_freq;
+
+		found = 0;
+		TAILQ_FOREACH(tmp_precac_entry,
+			      &dfs->dfs_precac_list,
+			      pe_list) {
+			if (tmp_precac_entry->center_ch_freq ==
+					pri_chan_cfreq) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found && pri_chan_cfreq) {
+			struct dfs_precac_entry *precac_entry;
+
+			precac_entry =
+				qdf_mem_malloc(sizeof(*precac_entry));
+			if (!precac_entry) {
+				dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+					"entry alloc fail for : %d", i);
+				continue;
+			}
+			if (dfs_max_bw_info[i].dfs_max_bw ==
+				DFS_CHWIDTH_165_VAL) {
+				status = dfs_precac_create_165mhz_precac_entry(
+						dfs,
+						precac_entry);
+				if (status) {
+					dfs_debug(dfs,
+						  WLAN_DEBUG_DFS,
+						  "PreCAC entry for channel 146 not created");
+					continue;
+				}
+			    /* The restricted 80p80 or the 165MHz channel might
+			     * have a non DFS part with center frequency 5775.
+			     * Mark the non DFS portion as precac done.
+			     */
+			    dfs_mark_non_dfs_as_precac_done(
+				    dfs,
+				    dfs_max_bw_info[i].dfs_pri_ch_freq,
+				    WLAN_PHYMODE_11AC_VHT80_80);
+			} else {
+			    status =
+				dfs_precac_create_precac_entry(dfs,
+							       precac_entry,
+							       dfs_max_bw_info,
+							       i);
+			if (status)
+				continue;
+			/* Some channels like 36HT160 might have a non DFS
+			 * part. Mark the non DFS portion as precac done.
+			 */
+			dfs_mark_non_dfs_as_precac_done(
+				dfs,
+				dfs_max_bw_info[i].dfs_pri_ch_freq,
+				WLAN_PHYMODE_11AC_VHT160);
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+	qdf_mem_free(dfs_max_bw_info);
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "Print the list of PreCAC ieee chan from linked list");
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_precac_list,
+		      pe_list) {
+	    uint8_t ch_ieee, bw;
+
+	    ch_ieee = utils_dfs_freq_to_chan(tmp_precac_entry->center_ch_freq);
+	    bw = tmp_precac_entry->bw;
+	    dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "ieee=%u bw=%u", ch_ieee, bw);
+	}
+}
+
+/*
+ * dfs_is_precac_done() - Verify if preCAC is done.
+ * @dfs: Pointer to wlan_dfs.
+ * @chan: Pointer to dfs_channel.
+ */
+bool dfs_is_precac_done(struct wlan_dfs *dfs, struct dfs_channel *chan)
+{
+	bool ret_val = 0;
+	uint16_t cfreq;
+
+	if (!WLAN_IS_CHAN_5GHZ(chan)) {
+		dfs_debug(dfs, WLAN_DEBUG_DFS,
+			  "Channel %d not a 5GHz channel",
+			  chan->dfs_ch_ieee);
+		return 0;
+	}
+
+	if (WLAN_IS_CHAN_MODE_160(chan))
+		cfreq = chan->dfs_ch_mhz_freq_seg2;
+	else if (WLAN_IS_CHAN_MODE_165(dfs, chan))
+		cfreq = RESTRICTED_80P80_CHAN_CENTER_FREQ;
+	else
+	    /* Center frequency of 20/40/80MHz is given
+	     * by dfs_ch_mhz_freq_seg1.
+	     */
+		cfreq = chan->dfs_ch_mhz_freq_seg1;
+
+	if (WLAN_IS_CHAN_MODE_20(chan) ||
+	    WLAN_IS_CHAN_MODE_40(chan) ||
+	    WLAN_IS_CHAN_MODE_80(chan) ||
+	    WLAN_IS_CHAN_MODE_160(chan) ||
+	    WLAN_IS_CHAN_MODE_165(dfs, chan)) {
+		ret_val =
+		    dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq(
+				dfs,
+				cfreq);
+	} else if (WLAN_IS_CHAN_MODE_80_80(chan)) {
+		ret_val = dfs_is_precac_done_on_ht8080_chan(dfs, chan);
+	}
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS, "precac_done_status = %d", ret_val);
+	return ret_val;
+}
+
+/*
+ * dfs_is_precac_done_on_ht20_40_80_chan_for_freq() - Find if preCAC is done
+ * for the given frequency.
+ * @dfs: Pointer to wlan_dfs.
+ * @chan_freq: Channel frequency in MHZ.
+ */
+bool
+dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq(struct wlan_dfs *dfs,
+						       uint16_t chan_freq)
+{
+	struct dfs_precac_entry *precac_entry;
+	bool ret_val = 0;
+
+	/*
+	 * A is within B-C and B+C
+	 * (B-C) <= A <= (B+C)
+	 */
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_list))
+		TAILQ_FOREACH(precac_entry,
+			      &dfs->dfs_precac_list,
+			      pe_list) {
+			/* Find if the channel frequency is
+			 * in this precac_list.
+			 */
+			if (IS_WITHIN_RANGE_STRICT(chan_freq,
+					precac_entry->center_ch_freq,
+					(precac_entry->bw/2))) {
+				ret_val = dfs_find_cac_status_for_chan_for_freq(
+						precac_entry, chan_freq);
+				break;
+			}
+		}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS, "ch_freq = %u cac_done = %d",
+		  chan_freq, ret_val);
+
+	return ret_val;
+}
+
+/*
+ * dfs_is_precac_done_on_ht8080_chan - Find if preCAC is done
+ * for the given frequency.
+ * @dfs: Pointer to wlan_dfs.
+ * @chan: Pointer to dfs_channel.
+ */
+bool dfs_is_precac_done_on_ht8080_chan(struct wlan_dfs *dfs,
+				       struct dfs_channel *chan)
+{
+	bool ret_val = 0, primary_found = 0;
+	uint16_t cfreq1, cfreq2 = 0;
+
+	cfreq1 = chan->dfs_ch_mhz_freq_seg1;
+	cfreq2 = chan->dfs_ch_mhz_freq_seg2;
+
+	/* Check if primary is DFS then search */
+	if (WLAN_IS_CHAN_DFS(chan))
+		primary_found =
+			dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq(
+				dfs,
+				cfreq1);
+	else
+		primary_found = 1;
+
+	/* Check if secondary DFS then search */
+	if (WLAN_IS_CHAN_DFS_CFREQ2(chan) && primary_found) {
+		ret_val =
+			dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq(
+				dfs,
+				cfreq2);
+
+	} else {
+		if (primary_found)
+			ret_val = 1;
+	}
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "seg1_freq = %u seg2_freq = %u ret_val = %d",
+		  cfreq1,
+		  cfreq2,
+		  ret_val);
+
+	return ret_val;
+}
+
+/* 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.
+ */
+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_mark_precac_done_for_freq() - Mark a frequency as preCAC done.
+ * @dfs: Pointer to wlan_dfs.
+ * @pri_ch_freq: Primary 80MHZ center frequency.
+ * @sec_ch_freq: Secondary 80MHZ center frequency.
+ * @ch_width: Channel width.
+ */
+void dfs_mark_precac_done_for_freq(struct wlan_dfs *dfs,
+				   uint16_t pri_ch_freq,
+				   uint16_t sec_ch_freq,
+				   enum phy_ch_width ch_width)
+{
+	struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL;
+	uint16_t channels[NUM_CHANNELS_160MHZ];
+	uint8_t i, nchannels = 0;
+
+	if (!pri_ch_freq)
+		return;
+
+	nchannels = dfs_find_subchannels_for_center_freq(pri_ch_freq,
+							 sec_ch_freq,
+							 ch_width,
+							 channels);
+	if (!nchannels)
+		return;
+
+	PRECAC_LIST_LOCK(dfs);
+	if (TAILQ_EMPTY(&dfs->dfs_precac_list)) {
+		PRECAC_LIST_UNLOCK(dfs);
+		return;
+	}
+	for (i = 0; i < nchannels; i++) {
+		TAILQ_FOREACH_SAFE(precac_entry,
+				   &dfs->dfs_precac_list,
+				   pe_list,
+				   tmp_precac_entry) {
+			if (IS_WITHIN_RANGE_STRICT(channels[i],
+					precac_entry->center_ch_freq,
+					(precac_entry->bw/2))) {
+				dfs_mark_tree_node_as_cac_done_for_freq
+					(dfs, precac_entry, channels[i]);
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+}
+
+/*
+ * dfs_mark_precac_nol_for_freq() - Mark a channel as preCAC NOL.
+ * @dfs: Pointer to wlan_dfs.
+ * @is_radar_found_on_secondary_seg: Flag to indicate second segment radar.
+ * @detector_id: Detector ID.
+ * @freq_list: frequency list.
+ * @num_channels: Number of channels.
+ */
+void dfs_mark_precac_nol_for_freq(struct wlan_dfs *dfs,
+				  uint8_t is_radar_found_on_secondary_seg,
+				  uint8_t detector_id,
+				  uint16_t *freq_lst,
+				  uint8_t num_channels)
+{
+	struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL;
+	struct wlan_objmgr_psoc *psoc;
+	uint8_t i;
+	struct dfs_soc_priv_obj *dfs_soc_obj;
+	struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops;
+	struct wlan_objmgr_pdev *pdev;
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "is_radar_found_on_secondary_seg = %u subchannel_marking = %u detector_id = %u",
+		  is_radar_found_on_secondary_seg,
+		  dfs->dfs_use_nol_subchannel_marking,
+		  detector_id);
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "agile detector freq = %u primary_freq = %u secondary_freq = %u",
+		  dfs->dfs_agile_precac_freq_mhz,
+		  dfs->dfs_precac_secondary_freq_mhz,
+		  dfs->dfs_precac_primary_freq_mhz);
+
+	/*
+	 * Even if radar found on primary, we need to move
+	 * the channel from precac-required-list and precac-done-list
+	 * to precac-nol-list.
+	 */
+	PRECAC_LIST_LOCK(dfs);
+	if (TAILQ_EMPTY(&dfs->dfs_precac_list)) {
+		PRECAC_LIST_UNLOCK(dfs);
+		return;
+	}
+	for (i = 0; i < num_channels; i++) {
+		TAILQ_FOREACH_SAFE(precac_entry,
+				   &dfs->dfs_precac_list,
+				   pe_list,
+				   tmp_precac_entry) {
+			if (IS_WITHIN_RANGE_STRICT(freq_lst[i],
+					precac_entry->center_ch_freq,
+					(precac_entry->bw/2))) {
+				dfs_mark_tree_node_as_nol_for_freq(dfs,
+								   precac_entry,
+								   freq_lst[i]);
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj);
+	dfs_soc_obj = dfs->dfs_soc_obj;
+
+	dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc);
+	pdev = dfs->dfs_pdev_obj;
+	/* PreCAC timer is not running, no need to restart preCAC */
+	if (!dfs_soc_obj->dfs_precac_timer_running)
+		return;
+
+	if (dfs_is_legacy_precac_enabled(dfs)) {
+		qdf_timer_sync_cancel(&dfs_soc_obj->dfs_precac_timer);
+		dfs_soc_obj->dfs_precac_timer_running = 0;
+		/*
+		 * If radar is found on primary channel, no need to
+		 * restart VAP's channels since channel change will happen
+		 * after RANDOM channel selection anyway.
+		 */
+		if (is_radar_found_on_secondary_seg) {
+			/*
+			 * Change the channel
+			 * case 1:-  No  VHT80 channel for precac is available
+			 * so bring it back to VHT80.
+			 * case 2:-  pick a new VHT80 channel for precac.
+			 */
+			if (dfs_is_ap_cac_timer_running(dfs)) {
+				dfs->dfs_defer_precac_channel_change = 1;
+				dfs_debug(dfs, WLAN_DEBUG_DFS,
+					  "Primary CAC is running, defer the channel change"
+					  );
+			} else {
+				dfs_mlme_channel_change_by_precac(
+						dfs->dfs_pdev_obj);
+			}
+		}
+	}
+}
+
+#ifdef ATH_SUPPORT_ZERO_CAC_DFS
+enum precac_status_for_chan
+dfs_precac_status_for_channel(struct wlan_dfs *dfs,
+			      struct dfs_channel *deschan)
+{
+	if (!dfs_is_precac_completed_count_non_zero(dfs))
+		return DFS_NO_PRECAC_COMPLETED_CHANS;
+
+	if (dfs_is_precac_done(dfs, deschan))
+		return DFS_PRECAC_COMPLETED_CHAN;
+
+	return DFS_PRECAC_REQUIRED_CHAN;
+}
+#endif
+
+/* dfs_print_node_data() - Print the precac tree node data.
+ * @dfs:  Pointer to WLAN DFS structure.
+ * @node: Precac tree node pointer.
+ *
+ * Sample print for below tree:
+ *
+ *      A                  A(C,N)
+ *     / \                 |
+ *    B   C                |------- B(C,N)
+ *   / \                   |        |
+ *  D   E                  |        |------- D(C,N)
+ *                         |        |
+ *                         |        |------- E(C,N)
+ *                         |
+ *                         |------- E(C,N)
+ *
+ * Where C is number of CACed subchannels, and N is number of NOL subchannels.
+ * For each node, the prefix and previous line prefix to be printed will be
+ * based on the level (and by our logic, bandwidth) of the current node.
+ *
+ */
+/*Retaining IEEE to print node data */
+static void dfs_print_node_data(struct wlan_dfs *dfs,
+				struct precac_tree_node *node)
+{
+	char prefix[MAX_PREFIX_CHAR] = "";
+	char prev_line_prefix[MAX_PREFIX_CHAR] = "";
+	char inv[4] = "inv";
+
+	switch (node->depth) {
+	case DEPTH_160_ROOT:
+		break;
+	case DEPTH_80_ROOT:
+		qdf_str_lcopy(prev_line_prefix, "|", MAX_PREFIX_CHAR);
+		qdf_str_lcopy(prefix, "|------- ", MAX_PREFIX_CHAR);
+		break;
+	case DEPTH_40_ROOT:
+		qdf_str_lcopy(prev_line_prefix, "|        |", MAX_PREFIX_CHAR);
+		qdf_str_lcopy(prefix, "|        |------- ", MAX_PREFIX_CHAR);
+		break;
+	case DEPTH_20_ROOT:
+		qdf_str_lcopy(prev_line_prefix,
+			      "|        |        |",
+			      MAX_PREFIX_CHAR);
+		qdf_str_lcopy(prefix,
+			      "|        |        |------- ",
+			      MAX_PREFIX_CHAR);
+		break;
+	default:
+		return;
+	}
+
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s", prev_line_prefix);
+	/* if current node is not a valid ic channel, print invalid */
+	if (node->n_valid_subchs != N_SUBCHS_FOR_BANDWIDTH(node->bandwidth))
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s%s", prefix, inv);
+	else
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s%u(%u,%u)",
+			 prefix,
+			 node->ch_ieee,
+			 node->n_caced_subchs,
+			 node->n_nol_subchs);
+}
+
+/* dfs_print_precac_tree_nodes() - print the precac tree nodes using
+ *                                 preorder traversal. (Root-Left-Right)
+ * @dfs:          WLAN DFS structure.
+ * @precac_entry: A single entry in the precac list.
+ *
+ * Algorithm used - Morris preorder tree traversal (iterative).
+ *
+ * Description of Algorithm:
+ * Consider the below tree, Preorder sequence (A, B, D, E, C)
+ *
+ *                         A
+ *                        / \
+ *                       B   C
+ *                      / \
+ *                     D   E
+ *
+ * What is inorder predecessor?
+ *
+ *    For a given node, the inorder predecessor of the node is
+ *    the rightmost node of the left subtree.
+ *    For e.g.: In our tree, E is the predecessor of A,
+ *    D is the predecessor of B.
+ *
+ * Steps for Algorithm:
+ * Starting from the root node as the current node,
+ * 1) If there is no left child, print current node data and go to right child.
+ * 2) If the left child exists,
+ *    2.1) Find the inorder predecessor of the current node.
+ *    2.2) If the predecessor's right child is
+ *         2.2.1) NULL, then
+ *                A) Print current node.
+ *                B) Make the predecessor's right child as the current node.
+ *                C) Go to left child.
+ *         2.2.2) Current node, then
+ *                A) Make the predecessor's right child as NULL.
+ *                B) Go to the right child.
+ * 3) Repeat 1 & 2 till current node is NULL.
+ *
+ * The above Binary Tree structure during the afore mentioned steps:
+ * Note: Nodes with '[]' are printed.
+ *
+ *     A         [A]         [A]         [A]        [A]        [A]       [A]
+ *    / \        /|\         /|\         /|\        /|\        / \       / \
+ *   B   C -->  B | C --> [B] | C --> [B] | C --> [B]| C --> [B]  C --> [B] [C]
+ *  / \        / \|       // \|       // \|       / \|       / \        / \
+ * D   E      D   E      D    E      [D]  E     [D]  E     [D] [E]    [D] [E]
+ *
+ */
+static void dfs_print_precac_tree_nodes(struct wlan_dfs *dfs,
+					struct dfs_precac_entry *precac_entry)
+{
+	struct precac_tree_node *root = precac_entry->tree_root;
+	struct precac_tree_node *curr_node, *inorder_predecessor;
+
+	if (!root)
+		return;
+	curr_node = root;
+	while (curr_node) {
+		if (!curr_node->left_child) {
+			dfs_print_node_data(dfs, curr_node);
+			curr_node = curr_node->right_child;
+		} else {
+			/* Find the right most leaf node of the left subtree. */
+			inorder_predecessor = curr_node->left_child;
+			while (inorder_predecessor->right_child &&
+			       inorder_predecessor->right_child != curr_node)
+				inorder_predecessor =
+					inorder_predecessor->right_child;
+
+			/* If the right most child of left subtree already
+			 * is linked to current node. We have traversed
+			 * left subtree. Remove the link and go to right
+			 * subtree
+			 */
+			if (inorder_predecessor->right_child == curr_node) {
+				inorder_predecessor->right_child = NULL;
+				curr_node = curr_node->right_child;
+			} else {
+			/* Print current node data, make current node
+			 * as predecessor's right child, and move to left child.
+			 */
+				dfs_print_node_data(dfs, curr_node);
+				inorder_predecessor->right_child = curr_node;
+				curr_node = curr_node->left_child;
+			}
+		}
+	}
+}
+
+void dfs_print_precaclists(struct wlan_dfs *dfs)
+{
+	struct dfs_precac_entry *tmp_precac_entry;
+
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+
+	PRECAC_LIST_LOCK(dfs);
+
+	/* Print the Pre-CAC required List */
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+		 "Precac status of all nodes in the list:");
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+		 "NOTE: Syntax for each node: <ch_ieee>(<CAC>,<NOL>)");
+	TAILQ_FOREACH(tmp_precac_entry,
+			&dfs->dfs_precac_list,
+			pe_list) {
+		dfs_print_precac_tree_nodes(dfs, tmp_precac_entry);
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+}
+
+void dfs_reinit_precac_lists(struct wlan_dfs *src_dfs,
+			     struct wlan_dfs *dest_dfs,
+			     uint16_t low_5g_freq,
+			     uint16_t high_5g_freq)
+{
+	struct dfs_precac_entry *tmp_precac_entry, *tmp_precac_entry2;
+
+	/* If the destination DFS is not adhering ETSI (or)
+	 * if the source DFS does not have any lists, return (nothing to do).
+	 */
+	if (utils_get_dfsdomain(dest_dfs->dfs_pdev_obj) != DFS_ETSI_DOMAIN ||
+	    TAILQ_EMPTY(&src_dfs->dfs_precac_list))
+		return;
+
+	/* If dest_dfs and src_dfs are same it will cause dead_lock. */
+	if (dest_dfs == src_dfs)
+		return;
+
+	PRECAC_LIST_LOCK(dest_dfs);
+	if (TAILQ_EMPTY(&dest_dfs->dfs_precac_list))
+		TAILQ_INIT(&dest_dfs->dfs_precac_list);
+	PRECAC_LIST_LOCK(src_dfs);
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &src_dfs->dfs_precac_list,
+		      pe_list) {
+		if (low_5g_freq <= tmp_precac_entry->vht80_ch_freq &&
+		    high_5g_freq >= tmp_precac_entry->vht80_ch_freq) {
+			/* If the destination DFS already have the entries for
+			 * some reason, remove them and update with the active
+			 * entry in the source DFS list.
+			 */
+			TAILQ_FOREACH(tmp_precac_entry2,
+				      &dest_dfs->dfs_precac_list,
+				      pe_list) {
+				if (tmp_precac_entry2->vht80_ch_freq ==
+				    tmp_precac_entry->vht80_ch_freq)
+					TAILQ_REMOVE(&dest_dfs->dfs_precac_list,
+						     tmp_precac_entry2,
+						     pe_list);
+			}
+			TAILQ_REMOVE(&src_dfs->dfs_precac_list,
+				     tmp_precac_entry,
+				     pe_list);
+			tmp_precac_entry->dfs = dest_dfs;
+			TAILQ_INSERT_TAIL(&dest_dfs->dfs_precac_list,
+					  tmp_precac_entry,
+					  pe_list);
+		}
+	}
+	PRECAC_LIST_UNLOCK(src_dfs);
+	PRECAC_LIST_UNLOCK(dest_dfs);
+}
+
+void dfs_reset_precac_lists(struct wlan_dfs *dfs)
+{
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+	dfs_reset_precaclists(dfs);
+}
+
+void dfs_reset_precaclists(struct wlan_dfs *dfs)
+{
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "Reset precaclist of VHT80 frequencies");
+	dfs_deinit_precac_list(dfs);
+	dfs_init_precac_list(dfs);
+}
+
+/*
+ * dfs_unmark_precac_nol_for_freq() - Unmark a channel frequency as NOL.
+ * @dfs: Pointer to wlan_dfs.
+ * @chan_freq: Channel frequency in MHZ.
+ */
+void dfs_unmark_precac_nol_for_freq(struct wlan_dfs *dfs, uint16_t chan_freq)
+{
+	struct dfs_precac_entry *pcac_entry = NULL,
+				*tmp_precac_entry = NULL;
+	uint16_t pri_ch_freq = 0, chwidth_80 = DFS_CHWIDTH_80_VAL;
+
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_list)) {
+		TAILQ_FOREACH_SAFE(pcac_entry, &dfs->dfs_precac_list,
+				   pe_list, tmp_precac_entry) {
+			if (IS_WITHIN_RANGE_STRICT(chan_freq,
+					pcac_entry->center_ch_freq,
+					(pcac_entry->bw/2))) {
+				dfs_unmark_tree_node_as_nol_for_freq(dfs,
+								     pcac_entry,
+								     chan_freq);
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	/* If preCAC / agile CAC is not running, restart the timer
+	 * to check if the NOL expired channels can be CACed again.
+	 */
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+		 "NOL expired for chan_freq %u, trying to start preCAC",
+		 chan_freq);
+	if (!dfs->dfs_soc_obj->dfs_precac_timer_running) {
+		if (dfs_is_legacy_precac_enabled(dfs)) {
+			if (dfs_is_ap_cac_timer_running(dfs)) {
+				dfs->dfs_defer_precac_channel_change = 1;
+				dfs_debug(dfs, WLAN_DEBUG_DFS,
+					  "Primary CAC is running, deferred"
+					  );
+			} else if (WLAN_IS_CHAN_11AC_VHT80(dfs->dfs_curchan)) {
+				pri_ch_freq = dfs->dfs_curchan->
+						dfs_ch_mhz_freq_seg1;
+
+				/* Check if there is a new channel to preCAC
+				 * and only then do vdev restart.
+				 */
+				if (!dfs_get_ieeechan_for_precac_for_freq
+				    (dfs, pri_ch_freq, 0, chwidth_80))
+					return;
+				dfs_mlme_channel_change_by_precac(
+						dfs->dfs_pdev_obj);
+			}
+		}
+	}
+}
+
+#ifdef QCA_SUPPORT_ADFS_RCAC
+/**
+ * dfs_unmark_rcac_done() - Unmark the CAC done channels from the RCAC list.
+ * @dfs: Pointer to wlan_dfs object.
+ */
+void dfs_unmark_rcac_done(struct wlan_dfs *dfs)
+{
+	struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL;
+	qdf_freq_t channels[NUM_CHANNELS_160MHZ];
+	uint8_t i, nchannels = 0;
+	qdf_freq_t pri_ch_freq =
+		dfs->dfs_rcac_param.rcac_ch_params.mhz_freq_seg0;
+	qdf_freq_t sec_ch_freq =
+		dfs->dfs_rcac_param.rcac_ch_params.mhz_freq_seg1;
+	enum phy_ch_width ch_width =
+		dfs->dfs_rcac_param.rcac_ch_params.ch_width;
+
+	if (ch_width == CH_WIDTH_160MHZ) {
+		pri_ch_freq = sec_ch_freq;
+		sec_ch_freq = 0;
+	}
+
+	if (!pri_ch_freq)
+		return;
+
+	nchannels = dfs_find_subchannels_for_center_freq(pri_ch_freq,
+							 sec_ch_freq,
+							 ch_width,
+							 channels);
+	if (!nchannels)
+		return;
+
+	PRECAC_LIST_LOCK(dfs);
+	if (TAILQ_EMPTY(&dfs->dfs_precac_list)) {
+		PRECAC_LIST_UNLOCK(dfs);
+		return;
+	}
+	for (i = 0; i < nchannels; i++) {
+		TAILQ_FOREACH_SAFE(precac_entry,
+				   &dfs->dfs_precac_list,
+				   pe_list,
+				   tmp_precac_entry) {
+			if (IS_WITHIN_RANGE(channels[i],
+					    precac_entry->center_ch_freq,
+					    VHT160_FREQ_OFFSET)) {
+				dfs_unmark_tree_node_as_cac_done_for_freq
+					(precac_entry, channels[i]);
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+}
+#endif

File diff suppressed because it is too large
+ 150 - 815
umac/dfs/core/src/misc/dfs_zero_cac.c


Some files were not shown because too many files changed in this diff