瀏覽代碼

qcacmn: PreCAC auto channel switch support

Changes to switch to the preferred preCAC channel after the
preCAC is done.
User can configure non-DFS channel as an intermediate
channel to reduce AP bringup time, and configure the DFS
channel as usual. PreCAC Logic will use the intermediate
channel as the home channel and will prioritize the DFS channel
to start preCAC first and switch automatically after the preCAC
timeout.
Command to configure intermediate non-DFS channel
iwpriv wifiX interCACChan <non-DFS channel num>
Assumption:
Intermediate channel should be configured before configuring
the DFS channel as operating channel.

Change-Id: I1dc00fa395aec53bfe5a6361095cd7d77ced503c
CRs-Fixed: 2272085
Shreedhar Parande 6 年之前
父節點
當前提交
62f2bb9136

+ 19 - 0
umac/dfs/core/src/dfs.h

@@ -898,6 +898,9 @@ struct dfs_event_log {
  * @dfs_precac_primary_freq:         Primary freq.
  * @dfs_precac_timer_running:        Precac timer running.
  * @dfs_defer_precac_channel_change: Defer precac channel change.
+ * @dfs_precac_preferred_chan:       Channel to change after precac.
+ * @dfs_precac_inter_chan:           Intermediate non-DFS channel used while
+ *                                   doing precac.
  * @dfs_pre_cac_timeout_channel_change: Channel change due to precac timeout.
  * @wlan_dfs_task_timer:             Dfs wait timer.
  * @dur_multiplier:                  Duration multiplier.
@@ -1017,6 +1020,10 @@ struct wlan_dfs {
 	uint8_t        dfs_precac_primary_freq;
 	uint8_t        dfs_precac_timer_running;
 	uint8_t        dfs_defer_precac_channel_change;
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+	uint8_t        dfs_precac_preferred_chan;
+	uint8_t        dfs_precac_inter_chan;
+#endif
 	uint8_t        dfs_pre_cac_timeout_channel_change:1;
 	os_timer_t     wlan_dfs_task_timer;
 	int            dur_multiplier;
@@ -2045,6 +2052,18 @@ void dfs_stacac_stop(struct wlan_dfs *dfs);
 void dfs_find_precac_secondary_vht80_chan(struct wlan_dfs *dfs,
 		struct dfs_channel *chan);
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * dfs_precac_csa() - Automatically switch the channel to the DFS channel
+ *			on which PreCAC was completed without finding a RADAR.
+ *			Use CSA with TBTT_COUNT to switch the channel.
+ * @dfs: Pointer to dfs handler.
+ *
+ * Return: Void
+ */
+void dfs_precac_csa(struct wlan_dfs *dfs);
+#endif
+
 /**
  * dfs_phyerr_param_copy() - Function to copy src buf to dest buf.
  * @dst: dest buf.

+ 119 - 4
umac/dfs/core/src/dfs_zero_cac.h

@@ -49,6 +49,22 @@ struct dfs_precac_entry {
 	struct wlan_dfs      *dfs;
 };
 
+/**
+ * enum precac_chan_state - Enum for PreCAC state of a channel.
+ * @PRECAC_ERR:            Invalid preCAC state.
+ * @PRECAC_REQUIRED:       preCAC need to be done on the channel.
+ * @PRECAC_NOW:            preCAC is running on the channel.
+ * @PRECAC_DONE:           preCAC is done and channel is clear.
+ * @PRECAC_NOL:            preCAC is done and radar is detected.
+ */
+enum precac_chan_state {
+	PRECAC_ERR      = -1,
+	PRECAC_REQUIRED,
+	PRECAC_NOW,
+	PRECAC_DONE,
+	PRECAC_NOL,
+};
+
 /**
  * dfs_zero_cac_timer_init() - Initialize zero-cac timers
  * @dfs: Pointer to DFS structure.
@@ -164,16 +180,42 @@ static inline void dfs_zero_cac_reset(struct wlan_dfs *dfs)
 /**
  * dfs_is_precac_done() - Is precac done.
  * @dfs: Pointer to wlan_dfs structure.
+ * @chan: Pointer to dfs_channel for which preCAC done is checked.
+ *
+ * Return:
+ * * True:  If precac is done on channel.
+ * * False: If precac is not done on channel.
  */
 #if defined(WLAN_DFS_PARTIAL_OFFLOAD)
-bool dfs_is_precac_done(struct wlan_dfs *dfs);
+bool dfs_is_precac_done(struct wlan_dfs *dfs, struct dfs_channel *chan);
 #else
-static inline bool dfs_is_precac_done(struct wlan_dfs *dfs)
+static inline bool dfs_is_precac_done(struct wlan_dfs *dfs,
+				      struct dfs_channel *chan)
 {
 	return false;
 }
 #endif
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * dfs_decide_precac_preferred_chan() - Choose operating channel among
+ *                                      configured DFS channel and
+ *                                      intermediate channel based on
+ *                                      precac status of configured
+ *                                      DFS channel.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @pref_chan: Congigired DFS channel.
+ *
+ * Return: void.
+ */
+void dfs_decide_precac_preferred_chan(struct wlan_dfs *dfs, uint8_t *pref_chan);
+#else
+static inline void dfs_decide_precac_preferred_chan(struct wlan_dfs *dfs,
+						    uint8_t *pref_chan)
+{
+}
+#endif
+
 /**
  * dfs_get_freq_from_precac_required_list() - Get VHT80 freq from
  *                                            precac_required_list.
@@ -301,6 +343,67 @@ static inline uint32_t dfs_get_precac_enable(struct wlan_dfs *dfs)
 }
 #endif
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * dfs_set_precac_intermediate_chan() - Set intermediate chan to be used while
+ *                                      doing precac.
+ * @dfs: Pointer to wlan_dfs structure.
+ * @value: input value for dfs_precac_enable flag.
+ *
+ * Return:
+ * * 0       - Successfully set intermediate channel.
+ * * -EINVAL - Invalid channel.
+ */
+int32_t dfs_set_precac_intermediate_chan(struct wlan_dfs *dfs,
+					 uint32_t value);
+#else
+static inline int32_t dfs_set_precac_intermediate_chan(struct wlan_dfs *dfs,
+						       uint32_t value)
+{
+	return 0;
+}
+#endif
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * dfs_get_precac_intermediate_chan() - Get configured precac
+ *					intermediate channel.
+ * @dfs: Pointer to wlan_dfs structure.
+ *
+ * Return: Configured intermediate channel number.
+ */
+uint32_t dfs_get_precac_intermediate_chan(struct wlan_dfs *dfs);
+#else
+static inline uint32_t dfs_get_intermediate_chan(struct wlan_dfs *dfs)
+{
+	return 0;
+}
+#endif
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * dfs_get_precac_chan_state() - Get precac status of a given channel.
+ * @dfs:         Pointer to wlan_dfs structure.
+ * @precac_chan: Channel number for which precac state need to be checked.
+ *
+ * Return:
+ * * PRECAC_REQUIRED: Precac has not done on precac_chan.
+ * * PRECAC_NOW     : Precac is running on precac_chan.
+ * * PRECAC_DONE    : precac_chan is in precac done list.
+ * * PRECAC_NOL     : precac_chan is in precac NOL list.
+ * * PRECAC_ERR     : Invalid precac state.
+ */
+enum precac_chan_state
+dfs_get_precac_chan_state(struct wlan_dfs *dfs, uint8_t precac_chan);
+#else
+static inline enum precac_chan_state
+dfs_get_precac_chan_state(struct wlan_dfs *dfs,
+			  uint8_t precac_chan)
+{
+	return PRECAC_REQUIRED;
+}
+#endif
+
 /**
  * dfs_zero_cac_reset() - Reset Zero cac DFS variables.
  * @dfs: Pointer to wlan_dfs structure.
@@ -311,15 +414,27 @@ void dfs_zero_cac_reset(struct wlan_dfs *dfs);
  * dfs_is_ht20_40_80_chan_in_precac_done_list() - Is precac done on a
  *                                                VHT20/40/80 channel.
  *@dfs: Pointer to wlan_dfs structure.
+ *@chan: Pointer to dfs_channel for which preCAC done is checked.
+ *
+ * Return:
+ * * True:  If channel is present in precac-done list.
+ * * False: If channel is not present in precac-done list.
  */
-bool dfs_is_ht20_40_80_chan_in_precac_done_list(struct wlan_dfs *dfs);
+bool dfs_is_ht20_40_80_chan_in_precac_done_list(struct wlan_dfs *dfs,
+						struct dfs_channel *chan);
 
 /**
  * dfs_is_ht80_80_chan_in_precac_done_list() - Is precac done on a VHT80+80
  *                                             channel.
  *@dfs: Pointer to wlan_dfs structure.
+ *@chan: Pointer to dfs_channel for which preCAC done is checked.
+ *
+ * Return:
+ * * True:  If channel is present in precac-done list.
+ * * False: If channel is not present in precac-done list.
  */
-bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs);
+bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs,
+					     struct dfs_channel *chan);
 
 /**
  * dfs_mark_precac_dfs() - Mark the precac channel as radar.

+ 342 - 51
umac/dfs/core/src/misc/dfs_zero_cac.c

@@ -146,7 +146,8 @@ int dfs_get_override_precac_timeout(struct wlan_dfs *dfs, int *precac_timeout)
 #define IS_WITHIN_RANGE(_A, _B, _C)  \
 	(((_A) >= ((_B)-(_C))) && ((_A) <= ((_B)+(_C))))
 
-bool dfs_is_ht20_40_80_chan_in_precac_done_list(struct wlan_dfs *dfs)
+bool dfs_is_ht20_40_80_chan_in_precac_done_list(struct wlan_dfs *dfs,
+						struct dfs_channel *chan)
 {
 	struct dfs_precac_entry *precac_entry;
 	bool ret_val = 0;
@@ -161,9 +162,9 @@ bool dfs_is_ht20_40_80_chan_in_precac_done_list(struct wlan_dfs *dfs)
 				&dfs->dfs_precac_done_list,
 				pe_list) {
 			/* Find if the VHT80 freq1 is in Pre-CAC done list */
-			if (IS_WITHIN_RANGE(dfs->dfs_curchan->dfs_ch_ieee,
-						precac_entry->vht80_freq,
-						VHT80_OFFSET)) {
+			if (IS_WITHIN_RANGE(chan->dfs_ch_ieee,
+					    precac_entry->vht80_freq,
+					    VHT80_OFFSET)) {
 				ret_val = 1;
 				break;
 			}
@@ -176,7 +177,8 @@ bool dfs_is_ht20_40_80_chan_in_precac_done_list(struct wlan_dfs *dfs)
 	return ret_val;
 }
 
-bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs)
+bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs,
+					     struct dfs_channel *chan)
 {
 	struct dfs_precac_entry *precac_entry;
 	bool ret_val = 0;
@@ -185,11 +187,11 @@ bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs)
 	if (!TAILQ_EMPTY(&dfs->dfs_precac_done_list)) {
 		bool primary_found = 0;
 		/* Check if primary is DFS then search */
-		if (WLAN_IS_CHAN_DFS(dfs->dfs_curchan)) {
+		if (WLAN_IS_CHAN_DFS(chan)) {
 			TAILQ_FOREACH(precac_entry,
 					&dfs->dfs_precac_done_list,
 					pe_list) {
-				if (dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg1
+				if (chan->dfs_ch_vhtop_ch_freq_seg1
 						== precac_entry->vht80_freq) {
 					primary_found = 1;
 					break;
@@ -200,12 +202,12 @@ bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs)
 		}
 
 		/* Check if secondary DFS then search */
-		if (WLAN_IS_CHAN_DFS_CFREQ2(dfs->dfs_curchan) &&
-				primary_found) {
+		if (WLAN_IS_CHAN_DFS_CFREQ2(chan) &&
+		    primary_found) {
 			TAILQ_FOREACH(precac_entry,
 					&dfs->dfs_precac_done_list,
 					pe_list) {
-				if (dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2
+				if (chan->dfs_ch_vhtop_ch_freq_seg2
 						== precac_entry->vht80_freq) {
 					/* Now secondary also found */
 					ret_val = 1;
@@ -221,24 +223,24 @@ bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs)
 
 	dfs_debug(dfs, WLAN_DEBUG_DFS,
 		"freq_seg1 = %u freq_seq2 = %u ret_val = %d",
-		dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg1,
-		dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2,
+		chan->dfs_ch_vhtop_ch_freq_seg1,
+		chan->dfs_ch_vhtop_ch_freq_seg2,
 		ret_val);
 
 	return ret_val;
 }
 
-bool dfs_is_precac_done(struct wlan_dfs *dfs)
+bool dfs_is_precac_done(struct wlan_dfs *dfs, struct dfs_channel *chan)
 {
 	bool ret_val = 0;
 
-	if (WLAN_IS_CHAN_11AC_VHT20(dfs->dfs_curchan) ||
-			WLAN_IS_CHAN_11AC_VHT40(dfs->dfs_curchan) ||
-			WLAN_IS_CHAN_11AC_VHT80(dfs->dfs_curchan)) {
-		ret_val = dfs_is_ht20_40_80_chan_in_precac_done_list(dfs);
-	} else if (WLAN_IS_CHAN_11AC_VHT80_80(dfs->dfs_curchan) ||
-			WLAN_IS_CHAN_11AC_VHT160(dfs->dfs_curchan)) {
-		ret_val = dfs_is_ht80_80_chan_in_precac_done_list(dfs);
+	if (WLAN_IS_CHAN_11AC_VHT20(chan) ||
+	    WLAN_IS_CHAN_11AC_VHT40(chan) ||
+	    WLAN_IS_CHAN_11AC_VHT80(chan)) {
+		ret_val = dfs_is_ht20_40_80_chan_in_precac_done_list(dfs, chan);
+	} else if (WLAN_IS_CHAN_11AC_VHT80_80(chan) ||
+		   WLAN_IS_CHAN_11AC_VHT160(chan)) {
+		ret_val = dfs_is_ht80_80_chan_in_precac_done_list(dfs, chan);
 	}
 
 	dfs_debug(dfs, WLAN_DEBUG_DFS, "ret_val = %d", ret_val);
@@ -384,6 +386,95 @@ void dfs_find_precac_secondary_vht80_chan(struct wlan_dfs *dfs,
 			&(chan->dfs_ch_vhtop_ch_freq_seg2));
 }
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+void dfs_precac_csa(struct wlan_dfs *dfs)
+{
+	uint32_t ieeechan = dfs->dfs_precac_preferred_chan;
+
+	/*
+	 * Save current chan into intermediate chan, so that
+	 * next time a DFS channel needs preCAC, there is no channel switch
+	 * until preCAC finishes.
+	 */
+	dfs->dfs_precac_inter_chan = dfs->dfs_precac_preferred_chan;
+
+	/* reset once the channel is used */
+	dfs->dfs_precac_preferred_chan = 0;
+
+	if (NULL != global_dfs_to_mlme.mlme_precac_chan_change_csa)
+		global_dfs_to_mlme.mlme_precac_chan_change_csa(
+						dfs->dfs_pdev_obj,
+						ieeechan);
+}
+#endif
+
+#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 change the channel from
+ * intermediate channel to home channel and continue precac on other channels.
+ *
+ * Return: Void.
+ */
+static void dfs_precac_check_home_chan_change(struct wlan_dfs *dfs)
+{
+	struct dfs_channel chan;
+	bool is_home_chan_changed = false;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (dfs->dfs_precac_preferred_chan) {
+		if (QDF_STATUS_SUCCESS !=
+		    dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+						dfs->dfs_precac_preferred_chan,
+						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)) {
+			dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+				"Invalid preferred channel %d",
+				dfs->dfs_precac_preferred_chan);
+			return;
+		}
+		is_home_chan_changed = dfs_is_precac_done(dfs, &chan);
+	}
+	/*
+	 * Change home channel using CSA
+	 * and continue to next preCAC channel.
+	 */
+	if (is_home_chan_changed) {
+		dfs_precac_csa(dfs);
+	} else {
+		/*
+		 * Use same home channel, only change preCAC channel.
+		 */
+
+		/*
+		 * TODO xxx : Need to lock the channel change.
+		 */
+		dfs_mlme_channel_change_by_precac(dfs->dfs_pdev_obj);
+	}
+}
+#else
+static inline void dfs_precac_check_home_chan_change(struct wlan_dfs *dfs)
+{
+	/*
+	 * Use same home channel, only change preCAC channel.
+	 */
+
+	/*
+	 * TODO xxx : Need to lock the channel change.
+	 */
+	dfs_mlme_channel_change_by_precac(dfs->dfs_pdev_obj);
+}
+#endif
+
 /**
  * dfs_precac_timeout() - Precac timeout.
  *
@@ -428,10 +519,10 @@ static os_timer_func(dfs_precac_timeout)
 		"Pre-cac expired, Precac Secondary chan %u curr time %d",
 		dfs->dfs_precac_secondary_freq,
 		(qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000));
-	/* Do vdev restart so that we can change the secondary VHT80 channel. */
-
-	/* TODO xxx : Need to lock the channel change */
-	dfs_mlme_channel_change_by_precac(dfs->dfs_pdev_obj);
+	/*
+	 * Do vdev restart so that we can change the secondary VHT80 channel.
+	 */
+	dfs_precac_check_home_chan_change(dfs);
 }
 
 void dfs_zero_cac_timer_init(struct wlan_dfs *dfs)
@@ -769,6 +860,151 @@ void dfs_reset_precac_lists(struct wlan_dfs *dfs)
 	dfs_reset_precaclists(dfs);
 }
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+void dfs_set_precac_preferred_channel(struct wlan_dfs *dfs,
+				      struct dfs_channel *chan)
+{
+	bool found = false;
+	struct dfs_precac_entry *precac_entry;
+
+	dfs->dfs_precac_preferred_chan = chan->dfs_ch_ieee;
+	if (dfs_is_precac_timer_running(dfs) &&
+	    (dfs->dfs_precac_secondary_freq == chan->dfs_ch_freq)) {
+		return;
+	}
+
+	/* Remove and insert into head, so that the user configured channel
+	 * is picked first for preCAC.
+	 */
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_required_list)) {
+		TAILQ_FOREACH(precac_entry,
+			      &dfs->dfs_precac_required_list, pe_list) {
+			if (precac_entry->vht80_freq ==
+			    chan->dfs_ch_vhtop_ch_freq_seg1) {
+				found = true;
+				TAILQ_REMOVE(&dfs->dfs_precac_required_list,
+					     precac_entry, pe_list);
+				TAILQ_INSERT_HEAD(&dfs->dfs_precac_required_list,
+						  precac_entry, pe_list);
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	if (!found) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"frequency %d not found in precac list",
+			chan->dfs_ch_vhtop_ch_freq_seg1);
+		return;
+	}
+}
+
+void dfs_decide_precac_preferred_chan(struct wlan_dfs *dfs, uint8_t *pref_chan)
+{
+	struct dfs_channel chan;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (QDF_STATUS_SUCCESS !=
+	    dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+					*pref_chan, 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))
+		return;
+	if (!dfs->dfs_precac_inter_chan)
+		return;
+
+	/*
+	 * If precac is done on this channel use it, else use a intermediate
+	 * non-DFS channel and trigger a precac on this channel.
+	 */
+	if (WLAN_IS_CHAN_DFS(&chan) && !dfs_is_precac_done(dfs, &chan)) {
+		dfs_set_precac_preferred_channel(dfs, &chan);
+		*pref_chan = dfs->dfs_precac_inter_chan;
+
+	} else {
+		dfs->dfs_precac_inter_chan = chan.dfs_ch_ieee;
+	}
+}
+
+enum precac_chan_state
+dfs_get_precac_chan_state(struct wlan_dfs *dfs, uint8_t precac_chan)
+{
+	struct dfs_channel chan;
+	struct dfs_precac_entry *tmp_precac_entry;
+	enum precac_chan_state ret = PRECAC_ERR;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (QDF_STATUS_SUCCESS !=
+	    dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+					precac_chan, 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)) {
+		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", precac_chan);
+		return PRECAC_ERR;
+	}
+
+	PRECAC_LIST_LOCK(dfs);
+	if (dfs_is_precac_timer_running(dfs)) {
+		tmp_precac_entry = TAILQ_FIRST(&dfs->dfs_precac_required_list);
+		if (tmp_precac_entry && (tmp_precac_entry->vht80_freq ==
+			chan.dfs_ch_vhtop_ch_freq_seg1)) {
+			ret = PRECAC_NOW;
+			goto end;
+		}
+	}
+
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_precac_required_list, pe_list) {
+		if (tmp_precac_entry->vht80_freq ==
+		    chan.dfs_ch_vhtop_ch_freq_seg1) {
+			ret = PRECAC_REQUIRED;
+			goto end;
+		}
+	}
+
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_precac_done_list, pe_list) {
+		if (tmp_precac_entry->vht80_freq ==
+		    chan.dfs_ch_vhtop_ch_freq_seg1) {
+			ret = PRECAC_DONE;
+			goto end;
+		}
+	}
+
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_precac_nol_list, pe_list) {
+		if (tmp_precac_entry->vht80_freq ==
+		    chan.dfs_ch_vhtop_ch_freq_seg1) {
+			ret = PRECAC_NOL;
+			goto end;
+		}
+	}
+
+end:
+	PRECAC_LIST_UNLOCK(dfs);
+	return ret;
+}
+#endif
+
 void dfs_find_vht80_chan_for_precac(struct wlan_dfs *dfs,
 		uint32_t chan_mode,
 		uint8_t ch_freq_seg1,
@@ -778,29 +1014,7 @@ void dfs_find_vht80_chan_for_precac(struct wlan_dfs *dfs,
 		bool *dfs_set_cfreq2,
 		bool *set_agile)
 {
-	struct wlan_objmgr_psoc *psoc;
-	struct wlan_lmac_if_target_tx_ops *tx_ops;
-	uint32_t target_type;
-
-	psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj);
-	if (!psoc) {
-		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "psoc is NULL");
-		return;
-	}
-
-	tx_ops = &(psoc->soc_cb.tx_ops.target_tx_ops);
-	target_type = lmac_get_target_type(dfs->dfs_pdev_obj);
-
 	if (chan_mode == WLAN_PHYMODE_11AC_VHT80) {
-		/*
-		 * If
-		 * 1) The chip is CASCADE
-		 * 2) The user phy_mode is VHT80 and
-		 * 3) The user has enabled Pre-CAC and
-		 * 4) The regdomain the ETSI
-		 * then find a center frequency for the secondary VHT80 and
-		 * Change the mode to VHT80_80 or VHT160
-		 */
 		uint8_t ieee_freq;
 
 		dfs_debug(dfs, WLAN_DEBUG_DFS,
@@ -808,10 +1022,12 @@ void dfs_find_vht80_chan_for_precac(struct wlan_dfs *dfs,
 			dfs->dfs_precac_secondary_freq,
 			dfs->dfs_precac_timer_running);
 
-		if (dfs->dfs_precac_enable &&
-				tx_ops->tgt_is_tgt_type_qca9984(target_type) &&
-				(utils_get_dfsdomain(dfs->dfs_pdev_obj) ==
-				 DFS_ETSI_DOMAIN)) {
+		/*
+		 * If Pre-CAC is enabled then find a center frequency for
+		 * the secondary VHT80 and Change the mode to
+		 * VHT80_80 or VHT160.
+		 */
+		if (dfs->dfs_precac_enable) {
 			/*
 			 * If precac timer is running then do not change the
 			 * secondary channel use the old secondary VHT80
@@ -903,6 +1119,8 @@ void dfs_find_vht80_chan_for_precac(struct wlan_dfs *dfs,
 				/* Start the pre_cac_timer */
 				dfs_start_precac_timer(dfs,
 						dfs->dfs_precac_secondary_freq);
+			} else {
+				dfs->dfs_precac_secondary_freq = 0;
 			} /* End of if(ieee_freq) */
 		} /* End of if(dfs->dfs_precac_enable) */
 	}
@@ -910,10 +1128,83 @@ void dfs_find_vht80_chan_for_precac(struct wlan_dfs *dfs,
 
 void dfs_set_precac_enable(struct wlan_dfs *dfs, uint32_t value)
 {
-	dfs->dfs_precac_enable = value;
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_lmac_if_target_tx_ops *tx_ops;
+	uint32_t target_type;
+
+	psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj);
+	if (!psoc) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "psoc is NULL");
+		dfs->dfs_precac_enable = 0;
+		return;
+	}
+
+	tx_ops = &psoc->soc_cb.tx_ops.target_tx_ops;
+	target_type = lmac_get_target_type(dfs->dfs_pdev_obj);
+
+	/*
+	 * If
+	 * 1) The chip is CASCADE,
+	 * 3) The user has enabled Pre-CAC and
+	 * 4) The regdomain the ETSI,
+	 * then enable preCAC.
+	 */
+	if ((1 == value) && tx_ops->tgt_is_tgt_type_qca9984(target_type) &&
+	    (utils_get_dfsdomain(dfs->dfs_pdev_obj) == DFS_ETSI_DOMAIN)) {
+		dfs->dfs_precac_enable = value;
+	} else {
+		dfs->dfs_precac_enable = 0;
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "preCAC disabled");
+	}
+	if (dfs_is_precac_timer_running(dfs)) {
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			 "Precac flag changed. Cancel the precac timer");
+		dfs_cancel_precac_timer(dfs);
+	}
 }
 
 uint32_t dfs_get_precac_enable(struct wlan_dfs *dfs)
 {
 	return dfs->dfs_precac_enable;
 }
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+int32_t dfs_set_precac_intermediate_chan(struct wlan_dfs *dfs, uint32_t value)
+{
+	struct dfs_channel chan;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (QDF_STATUS_SUCCESS !=
+	    dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+					value, 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)) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Intermediate channel not found");
+		return -EINVAL;
+	}
+
+	/*Intermediate channel should be non-DFS channel*/
+	if (!WLAN_IS_CHAN_DFS(&chan)) {
+		dfs->dfs_precac_inter_chan = value;
+	} else {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "intermediate channel %s",
+			(chan.dfs_ch_ieee == value) ?
+			"should not be DFS channel" : "is invalid");
+		dfs->dfs_precac_inter_chan = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+uint32_t dfs_get_precac_intermediate_chan(struct wlan_dfs *dfs)
+{
+	return dfs->dfs_precac_inter_chan;
+}
+#endif

+ 22 - 17
umac/dfs/dispatcher/inc/wlan_dfs_mlme_api.h

@@ -135,17 +135,22 @@ int dfs_mlme_ieee2mhz(struct wlan_objmgr_pdev *pdev,
  * @dfs_ch_vhtop_ch_freq_seg1:  Channel Center frequency.
  * @dfs_ch_vhtop_ch_freq_seg2:  Channel Center frequency applicable for 80+80MHz
  *                          mode of operation.
+ *
+ * Return:
+ * * QDF_STATUS_SUCCESS  : Channel found.
+ * * QDF_STATUS_E_FAILURE: Channel not found.
  */
-void dfs_mlme_find_dot11_channel(struct wlan_objmgr_pdev *pdev,
-		uint8_t ieee,
-		uint8_t des_cfreq2,
-		int mode,
-		uint16_t *dfs_ch_freq,
-		uint64_t *dfs_ch_flags,
-		uint16_t *dfs_ch_flagext,
-		uint8_t *dfs_ch_ieee,
-		uint8_t *dfs_ch_vhtop_ch_freq_seg1,
-		uint8_t *dfs_ch_vhtop_ch_freq_seg2);
+QDF_STATUS
+dfs_mlme_find_dot11_channel(struct wlan_objmgr_pdev *pdev,
+			    uint8_t ieee,
+			    uint8_t des_cfreq2,
+			    int mode,
+			    uint16_t *dfs_ch_freq,
+			    uint64_t *dfs_ch_flags,
+			    uint16_t *dfs_ch_flagext,
+			    uint8_t *dfs_ch_ieee,
+			    uint8_t *dfs_ch_vhtop_ch_freq_seg1,
+			    uint8_t *dfs_ch_vhtop_ch_freq_seg2);
 
 /**
  * dfs_mlme_get_dfs_ch_channels() - Get channel from channel list.
@@ -160,13 +165,13 @@ void dfs_mlme_find_dot11_channel(struct wlan_objmgr_pdev *pdev,
  * @index: Index into channel list.
  */
 void dfs_mlme_get_dfs_ch_channels(struct wlan_objmgr_pdev *pdev,
-		uint16_t *dfs_ch_freq,
-		uint64_t *dfs_ch_flags,
-		uint16_t *dfs_ch_flagext,
-		uint8_t *dfs_ch_ieee,
-		uint8_t *dfs_ch_vhtop_ch_freq_seg1,
-		uint8_t *dfs_ch_vhtop_ch_freq_seg2,
-		int index);
+				  uint16_t *dfs_ch_freq,
+				  uint64_t *dfs_ch_flags,
+				  uint16_t *dfs_ch_flagext,
+				  uint8_t *dfs_ch_ieee,
+				  uint8_t *dfs_ch_vhtop_ch_freq_seg1,
+				  uint8_t *dfs_ch_vhtop_ch_freq_seg2,
+				  int index);
 
 /**
  * dfs_mlme_dfs_ch_flags_ext() - Get extension channel flags.

+ 53 - 1
umac/dfs/dispatcher/inc/wlan_dfs_ucfg_api.h

@@ -48,6 +48,8 @@
  * @mlme_get_dfs_ch_channels:          Get the channel list.
  * @mlme_dfs_ch_flags_ext:             Gets channel extension flag.
  * @mlme_channel_change_by_precac:     Channel change triggered by PreCAC.
+ * @mlme_precac_chan_change_csa:       Channel change triggered by PrCAC using
+ *                                     Channel Switch Announcement.
  * @mlme_nol_timeout_notification:     NOL timeout notification.
  * @mlme_clist_update:                 Updates the channel list.
  * @mlme_get_cac_timeout:              Gets the CAC timeout.
@@ -119,6 +121,10 @@ struct dfs_to_mlme {
 			uint16_t *flag_ext);
 	QDF_STATUS (*mlme_channel_change_by_precac)(
 			struct wlan_objmgr_pdev *pdev);
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+	QDF_STATUS (*mlme_precac_chan_change_csa)(struct wlan_objmgr_pdev *pdev,
+						  uint8_t ch_ieee);
+#endif
 	QDF_STATUS (*mlme_nol_timeout_notification)(
 			struct wlan_objmgr_pdev *pdev);
 	QDF_STATUS (*mlme_clist_update)(struct wlan_objmgr_pdev *pdev,
@@ -227,7 +233,7 @@ QDF_STATUS ucfg_dfs_override_precac_timeout(struct wlan_objmgr_pdev *pdev,
  * This function called from outside of dfs component.
  */
 QDF_STATUS ucfg_dfs_set_precac_enable(struct wlan_objmgr_pdev *pdev,
-		uint32_t value);
+				      uint32_t value);
 
 /**
  * ucfg_dfs_get_precac_enable() - Get precac enable flag.
@@ -239,6 +245,52 @@ QDF_STATUS ucfg_dfs_set_precac_enable(struct wlan_objmgr_pdev *pdev,
  */
 QDF_STATUS ucfg_dfs_get_precac_enable(struct wlan_objmgr_pdev *pdev, int *buff);
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * ucfg_dfs_set_precac_intermediate_chan() - Set intermediate channel
+ *                                           for preCAC.
+ * @pdev: Pointer to DFS pdev object.
+ * @value: Channel number of intermediate channel
+ *
+ * Wrapper function for dfs_set_precac_intermediate_chan().
+ * This function is called from outside of dfs component.
+ *
+ * Return:
+ * * QDF_STATUS_SUCCESS  : Successfully set intermediate channel.
+ * * QDF_STATUS_E_FAILURE: Failed to set intermediate channel.
+ */
+QDF_STATUS ucfg_dfs_set_precac_intermediate_chan(struct wlan_objmgr_pdev *pdev,
+						 uint32_t value);
+
+/**
+ * ucfg_dfs_get_precac_intermediate_chan() - Get intermediate channel
+ *						for preCAC.
+ * @pdev: Pointer to DFS pdev object.
+ * @buff: Pointer to Channel number of intermediate channel.
+ *
+ * Wrapper function for dfs_get_precac_intermediate_chan().
+ * This function is called from outside of dfs component.
+ *
+ * Return: Configured intermediate precac channel.
+ */
+QDF_STATUS ucfg_dfs_get_precac_intermediate_chan(struct wlan_objmgr_pdev *pdev,
+						 int *buff);
+
+/**
+ * ucfg_dfs_get_precac_chan_state() - Get precac status for the given channel.
+ * @pdev: Pointer to DFS pdev object.
+ * @precac_chan: Channel number for which precac state needs to be determined.
+ *
+ * Wrapper function for dfs_get_precac_chan_state().
+ * This function called from outside of dfs component.
+ *
+ * Return: Precac state of the given channel.
+ */
+enum precac_chan_state
+ucfg_dfs_get_precac_chan_state(struct wlan_objmgr_pdev *pdev,
+			       uint8_t precac_chan);
+#endif
+
 #ifdef QCA_MCL_DFS_SUPPORT
 /**
  * ucfg_dfs_update_config() - Update DFS user config.

+ 30 - 0
umac/dfs/dispatcher/inc/wlan_dfs_utils_api.h

@@ -136,6 +136,19 @@ static inline QDF_STATUS utils_dfs_reset_etsi_precaclists(
  */
 QDF_STATUS utils_dfs_cancel_precac_timer(struct wlan_objmgr_pdev *pdev);
 
+/**
+ * utils_dfs_start_precac_timer() - Start the precac timer.
+ * @pdev: Pointer to DFS pdev object.
+ *
+ * Wrapper function for dfs_start_precac_timer(). This function called from
+ * outside of dfs component.
+ *
+ * Return:
+ * * QDF_STATUS_E_FAILURE: Failed to start timer.
+ * * QDF_STATUS_SUCCESS: Timer started successfully.
+ */
+QDF_STATUS utils_dfs_start_precac_timer(struct wlan_objmgr_pdev *pdev);
+
 /**
  * utils_dfs_is_precac_done() - Is precac done.
  * @pdev: Pointer to DFS pdev object.
@@ -146,6 +159,23 @@ QDF_STATUS utils_dfs_cancel_precac_timer(struct wlan_objmgr_pdev *pdev);
 QDF_STATUS utils_dfs_is_precac_done(struct wlan_objmgr_pdev *pdev,
 		bool *is_precac_done);
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * utils_dfs_precac_decide_pref_chan() - Choose preferred channel
+ * @pdev: Pointer to DFS pdev object.
+ * @ch_ieee: Pointer to channel number
+
+ * Wrapper function for dfs_decide_precac_preferred_chan(). This
+ * function called from outside of dfs component.
+ *
+ * Return:
+ * * QDF_STATUS_E_FAILURE: Failed to decide preferred channel.
+ * * QDF_STATUS_SUCCESS: Set preferred channel successfully.
+ */
+QDF_STATUS utils_dfs_precac_decide_pref_chan(struct wlan_objmgr_pdev *pdev,
+					     uint8_t *ch_ieee);
+#endif
+
 /**
  * utils_dfs_is_esti_precac_done() - Is ETSI precac done.
  * @pdev: Pointer to DFS pdev object.

+ 22 - 0
umac/dfs/dispatcher/src/wlan_dfs_init_deinit_api.c

@@ -47,6 +47,23 @@ struct wlan_dfs *wlan_pdev_get_dfs_obj(struct wlan_objmgr_pdev *pdev)
 }
 
 #ifndef QCA_MCL_DFS_SUPPORT
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+static inline void
+register_dfs_precac_auto_chan_callbacks(struct dfs_to_mlme *mlme_callback)
+{
+	if (!mlme_callback)
+		return;
+
+	mlme_callback->mlme_precac_chan_change_csa =
+		mlme_dfs_precac_chan_change_csa;
+}
+#else
+static inline void
+register_dfs_precac_auto_chan_callbacks(struct dfs_to_mlme *mlme_callback)
+{
+}
+#endif
+
 void register_dfs_callbacks(void)
 {
 	struct dfs_to_mlme *tmp_dfs_to_mlme = &global_dfs_to_mlme;
@@ -85,6 +102,11 @@ void register_dfs_callbacks(void)
 		mlme_dfs_restart_vaps_with_non_dfs_chan;
 	tmp_dfs_to_mlme->mlme_check_allowed_prim_chanlist =
 		mlme_dfs_check_allowed_prim_chanlist;
+
+	/*
+	 * Register precac auto channel switch feature related callbacks
+	 */
+	register_dfs_precac_auto_chan_callbacks(tmp_dfs_to_mlme);
 }
 #else
 void register_dfs_callbacks(void)

+ 22 - 20
umac/dfs/dispatcher/src/wlan_dfs_mlme_api.c

@@ -188,28 +188,30 @@ int dfs_mlme_ieee2mhz(struct wlan_objmgr_pdev *pdev, int ieee, uint64_t flag)
 	return freq;
 }
 
-void dfs_mlme_find_dot11_channel(struct wlan_objmgr_pdev *pdev,
-		uint8_t ieee,
-		uint8_t des_cfreq2,
-		int mode,
-		uint16_t *dfs_ch_freq,
-		uint64_t *dfs_ch_flags,
-		uint16_t *dfs_ch_flagext,
-		uint8_t *dfs_ch_ieee,
-		uint8_t *dfs_ch_vhtop_ch_freq_seg1,
-		uint8_t *dfs_ch_vhtop_ch_freq_seg2)
+QDF_STATUS
+dfs_mlme_find_dot11_channel(struct wlan_objmgr_pdev *pdev,
+			    uint8_t ieee,
+			    uint8_t des_cfreq2,
+			    int mode,
+			    uint16_t *dfs_ch_freq,
+			    uint64_t *dfs_ch_flags,
+			    uint16_t *dfs_ch_flagext,
+			    uint8_t *dfs_ch_ieee,
+			    uint8_t *dfs_ch_vhtop_ch_freq_seg1,
+			    uint8_t *dfs_ch_vhtop_ch_freq_seg2)
 {
 	if (global_dfs_to_mlme.mlme_find_dot11_channel != NULL)
-		global_dfs_to_mlme.mlme_find_dot11_channel(pdev,
-				ieee,
-				des_cfreq2,
-				mode,
-				dfs_ch_freq,
-				dfs_ch_flags,
-				dfs_ch_flagext,
-				dfs_ch_ieee,
-				dfs_ch_vhtop_ch_freq_seg1,
-				dfs_ch_vhtop_ch_freq_seg2);
+		return global_dfs_to_mlme.mlme_find_dot11_channel(pdev,
+								  ieee,
+								  des_cfreq2,
+								  mode,
+								  dfs_ch_freq,
+								  dfs_ch_flags,
+								  dfs_ch_flagext,
+								  dfs_ch_ieee,
+								  dfs_ch_vhtop_ch_freq_seg1,
+								  dfs_ch_vhtop_ch_freq_seg2);
+	return QDF_STATUS_E_FAILURE;
 }
 
 void dfs_mlme_get_dfs_ch_channels(struct wlan_objmgr_pdev *pdev,

+ 56 - 0
umac/dfs/dispatcher/src/wlan_dfs_ucfg_api.c

@@ -154,6 +154,62 @@ QDF_STATUS ucfg_dfs_get_precac_enable(struct wlan_objmgr_pdev *pdev,
 }
 qdf_export_symbol(ucfg_dfs_get_precac_enable);
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+QDF_STATUS ucfg_dfs_set_precac_intermediate_chan(struct wlan_objmgr_pdev *pdev,
+						 uint32_t value)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = global_dfs_to_mlme.pdev_get_comp_private_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "null dfs");
+		return  QDF_STATUS_E_FAILURE;
+	}
+
+	dfs_set_precac_intermediate_chan(dfs, value);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS ucfg_dfs_get_precac_intermediate_chan(struct wlan_objmgr_pdev *pdev,
+						 int *buff)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = global_dfs_to_mlme.pdev_get_comp_private_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "null dfs");
+		return  QDF_STATUS_E_FAILURE;
+	}
+
+	*buff = dfs_get_precac_intermediate_chan(dfs);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+enum precac_chan_state
+ucfg_dfs_get_precac_chan_state(struct wlan_objmgr_pdev *pdev,
+			       uint8_t precac_chan)
+{
+	struct wlan_dfs *dfs;
+	enum precac_chan_state retval = PRECAC_ERR;
+
+	dfs = global_dfs_to_mlme.pdev_get_comp_private_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "null dfs");
+		return PRECAC_ERR;
+	}
+
+	retval = dfs_get_precac_chan_state(dfs, precac_chan);
+	if (PRECAC_ERR == retval) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Could not find precac channel state");
+	}
+
+	return retval;
+}
+#endif
+
 #ifdef QCA_MCL_DFS_SUPPORT
 QDF_STATUS ucfg_dfs_update_config(struct wlan_objmgr_psoc *psoc,
 		struct dfs_user_config *req)

+ 31 - 1
umac/dfs/dispatcher/src/wlan_dfs_utils_api.c

@@ -118,6 +118,36 @@ QDF_STATUS utils_dfs_cancel_precac_timer(struct wlan_objmgr_pdev *pdev)
 }
 qdf_export_symbol(utils_dfs_cancel_precac_timer);
 
+QDF_STATUS utils_dfs_start_precac_timer(struct wlan_objmgr_pdev *pdev)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = global_dfs_to_mlme.pdev_get_comp_private_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "NULL dfs");
+		return  QDF_STATUS_E_FAILURE;
+	}
+	dfs_start_precac_timer(dfs, dfs->dfs_precac_secondary_freq);
+	return QDF_STATUS_SUCCESS;
+}
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+QDF_STATUS utils_dfs_precac_decide_pref_chan(struct wlan_objmgr_pdev *pdev,
+					     uint8_t *ch_ieee)
+{
+	struct wlan_dfs *dfs;
+
+	dfs = global_dfs_to_mlme.pdev_get_comp_private_obj(pdev);
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "NULL dfs");
+		return  QDF_STATUS_E_FAILURE;
+	}
+	dfs_decide_precac_preferred_chan(dfs, ch_ieee);
+
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
 QDF_STATUS utils_dfs_is_precac_done(struct wlan_objmgr_pdev *pdev,
 		bool *is_precac_done)
 {
@@ -127,7 +157,7 @@ QDF_STATUS utils_dfs_is_precac_done(struct wlan_objmgr_pdev *pdev,
 	if (!dfs)
 		return  QDF_STATUS_E_FAILURE;
 
-	*is_precac_done = dfs_is_precac_done(dfs);
+	*is_precac_done = dfs_is_precac_done(dfs, dfs->dfs_curchan);
 
 	return QDF_STATUS_SUCCESS;
 }

+ 17 - 0
umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h

@@ -1037,6 +1037,12 @@ struct wlan_lmac_if_nan_rx_ops {
  * @dfs_override_precac_timeout:      Override the default precac timeout.
  * @dfs_set_precac_enable:            Set precac enable flag.
  * @dfs_get_precac_enable:            Get precac enable flag.
+ * @dfs_set_precac_intermediate_chan: Set intermediate channel for precac.
+ * @dfs_get_precac_intermediate_chan: Get intermediate channel for precac.
+ * @dfs_precac_preferred_chan:        Configure preferred channel during
+ *                                    precac.
+ * dfs_get_precac_chan_state:         Get precac status for given channel.
+ * dfs_start_precac_timer:            Start precac timer.
  * @dfs_get_override_precac_timeout:  Get precac timeout.
  * @dfs_set_current_channel:          Set DFS current channel.
  * @dfs_process_radar_ind:            Process radar found indication.
@@ -1086,6 +1092,7 @@ struct wlan_lmac_if_dfs_rx_ops {
 					      uint32_t *phy_mode,
 					      bool *dfs_set_cfreq2,
 					      bool *set_agile);
+	QDF_STATUS (*dfs_start_precac_timer)(struct wlan_objmgr_pdev *pdev);
 	QDF_STATUS (*dfs_cancel_precac_timer)(struct wlan_objmgr_pdev *pdev);
 	QDF_STATUS (*dfs_override_precac_timeout)(
 			struct wlan_objmgr_pdev *pdev,
@@ -1094,6 +1101,16 @@ struct wlan_lmac_if_dfs_rx_ops {
 			uint32_t value);
 	QDF_STATUS (*dfs_get_precac_enable)(struct wlan_objmgr_pdev *pdev,
 			int *buff);
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+	QDF_STATUS (*dfs_set_precac_intermediate_chan)(struct wlan_objmgr_pdev *pdev,
+						       uint32_t value);
+	QDF_STATUS (*dfs_get_precac_intermediate_chan)(struct wlan_objmgr_pdev *pdev,
+						       int *buff);
+	QDF_STATUS (*dfs_decide_precac_preferred_chan)(struct wlan_objmgr_pdev *pdev,
+						       uint8_t *pref_chan);
+	enum precac_chan_state (*dfs_get_precac_chan_state)(struct wlan_objmgr_pdev *pdev,
+							    uint8_t precac_chan);
+#endif
 	QDF_STATUS (*dfs_get_override_precac_timeout)(
 			struct wlan_objmgr_pdev *pdev,
 			int *precac_timeout);

+ 22 - 0
umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c

@@ -301,6 +301,25 @@ static void wlan_lmac_if_umac_rx_ops_register_p2p(
 #endif
 
 #ifdef DFS_COMPONENT_ENABLE
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+static inline void
+register_precac_auto_chan_rx_ops(struct wlan_lmac_if_dfs_rx_ops *rx_ops)
+{
+	if (!rx_ops)
+		return;
+	rx_ops->dfs_set_precac_intermediate_chan =
+		ucfg_dfs_set_precac_intermediate_chan;
+	rx_ops->dfs_get_precac_intermediate_chan =
+		ucfg_dfs_get_precac_intermediate_chan;
+	rx_ops->dfs_get_precac_chan_state = ucfg_dfs_get_precac_chan_state;
+}
+#else
+static inline void
+register_precac_auto_chan_rx_ops(struct wlan_lmac_if_dfs_rx_ops *rx_ops)
+{
+}
+#endif
+
 static QDF_STATUS
 wlan_lmac_if_umac_dfs_rx_ops_register(struct wlan_lmac_if_rx_ops *rx_ops)
 {
@@ -318,6 +337,7 @@ wlan_lmac_if_umac_dfs_rx_ops_register(struct wlan_lmac_if_rx_ops *rx_ops)
 		tgt_dfs_is_precac_timer_running;
 	dfs_rx_ops->dfs_find_vht80_chan_for_precac =
 		tgt_dfs_find_vht80_chan_for_precac;
+	dfs_rx_ops->dfs_start_precac_timer = utils_dfs_start_precac_timer;
 	dfs_rx_ops->dfs_cancel_precac_timer = utils_dfs_cancel_precac_timer;
 	dfs_rx_ops->dfs_override_precac_timeout =
 		ucfg_dfs_override_precac_timeout;
@@ -341,6 +361,8 @@ wlan_lmac_if_umac_dfs_rx_ops_register(struct wlan_lmac_if_rx_ops *rx_ops)
 	dfs_rx_ops->dfs_reset_spoof_test =
 		tgt_dfs_reset_spoof_test;
 
+	register_precac_auto_chan_rx_ops(dfs_rx_ops);
+
 	return QDF_STATUS_SUCCESS;
 }
 #else