瀏覽代碼

qca-wifi: Modify the autochannel switch design

In autochannel switch feature, a user configured primary channel
is prioritized for preCAC and after preCAC completion, the channel
is used as the primary operating channel of the device.

To acheive this, the existing preCAC lists were reordered since
every entry of the list was of a fixed 80MHz bandwidth and the
channels to be prioritized were also provided as 80MHz. i.e the list
entry to be prioritized will be made as the list HEAD.

This logic was only possible because the preCAC lists were channels
of 80MHz bandwidth. After the agile 160MHz changes, the preCAC lists
were now of variable bandwidth, from 20-160MHz. Reordering a 160MHz
channel will not allow the expected 80MHz channel to run first.

To fix this issue in autochannel switch algorithm, instead of
reordering the lists,
1. Remember the user desired channels to be prioritized in
   dfs structure.
2. Before picking a channel for preCAC, see if the autoswitch desired
channel is configured
3. If yes, check the preCAC state of those channels.
4. If the preCAC state is "PRECAC_REQUIRED", use this channel to run
   preCAC.

CRs-Fixed: 2723206
Change-Id: Icd6d01adfae6dfa63d0733c5dae6d6494a660190
Vignesh Mohan 5 年之前
父節點
當前提交
21c6741f48
共有 1 個文件被更改,包括 146 次插入117 次删除
  1. 146 117
      umac/dfs/core/src/misc/dfs_zero_cac.c

+ 146 - 117
umac/dfs/core/src/misc/dfs_zero_cac.c

@@ -2189,7 +2189,7 @@ void dfs_precac_csa(struct wlan_dfs *dfs)
 	 * next time a DFS channel needs preCAC, there is no channel switch
 	 * until preCAC finishes.
 	 */
-	dfs->dfs_precac_inter_chan_freq = dfs->dfs_autoswitch_des_chan_freq;
+	dfs->dfs_precac_inter_chan_freq = dfs->dfs_autoswitch_chan->dfs_ch_freq;
 	dfs_debug(dfs, WLAN_DEBUG_DFS,
 		  "Use %d as intermediate channel for further channel changes",
 		  dfs->dfs_precac_inter_chan_freq);
@@ -2197,9 +2197,10 @@ void dfs_precac_csa(struct wlan_dfs *dfs)
 	if (global_dfs_to_mlme.mlme_precac_chan_change_csa_for_freq)
 		global_dfs_to_mlme.mlme_precac_chan_change_csa_for_freq
 			(dfs->dfs_pdev_obj,
-			 dfs->dfs_autoswitch_des_chan_freq,
+			 dfs->dfs_autoswitch_chan->dfs_ch_freq,
 			 dfs->dfs_autoswitch_des_mode);
-	dfs->dfs_autoswitch_des_chan_freq = 0;
+	qdf_mem_free(dfs->dfs_autoswitch_chan);
+	dfs->dfs_autoswitch_chan = NULL;
 }
 #else
 #ifdef CONFIG_CHAN_NUM_API
@@ -2239,33 +2240,9 @@ void dfs_precac_csa(struct wlan_dfs *dfs)
 #ifdef CONFIG_CHAN_FREQ_API
 static bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs)
 {
-	struct dfs_channel chan;
+	struct dfs_channel *chan = dfs->dfs_autoswitch_chan;
 
-	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
-	if (QDF_STATUS_SUCCESS !=
-	    dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj,
-					      dfs->dfs_autoswitch_des_chan_freq,
-					      0, dfs->dfs_autoswitch_des_mode,
-					      &chan.dfs_ch_freq,
-					      &chan.dfs_ch_flags,
-					      &chan.dfs_ch_flagext,
-					      &chan.dfs_ch_ieee,
-					      &chan.dfs_ch_vhtop_ch_freq_seg1,
-					      &chan.dfs_ch_vhtop_ch_freq_seg2,
-					      &chan.dfs_ch_mhz_freq_seg1,
-					      &chan.dfs_ch_mhz_freq_seg2)) {
-		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
-			"Channel %d not found for mode %d",
-			dfs->dfs_autoswitch_des_chan_freq,
-			dfs->dfs_autoswitch_des_mode);
-		return false;
-	}
-	/*
-	 * If desired channel is in precac done list,
-	 * Change channel to desired channel using CSA.
-	 */
-	if (dfs->dfs_autoswitch_des_chan_freq &&
-	    dfs_is_precac_done(dfs, &chan)) {
+	if (chan && dfs_is_precac_done(dfs, chan)) {
 		dfs_precac_csa(dfs);
 		dfs->dfs_soc_obj->precac_state_started = false;
 		return true;
@@ -3813,6 +3790,106 @@ uint8_t dfs_get_ieeechan_for_precac(struct wlan_dfs *dfs,
 }
 #endif
 
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+#ifdef CONFIG_CHAN_FREQ_API
+/**
+ * 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.
+ */
+static 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_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.
+ */
+static qdf_freq_t
+dfs_configure_deschan_for_precac(struct wlan_dfs *dfs)
+{
+	struct dfs_channel *deschan = dfs->dfs_autoswitch_chan;
+	qdf_freq_t channels[2];
+	uint8_t i, nchannels;
+	struct dfs_precac_entry *tmp_precac_entry;
+	enum precac_chan_state precac_state = PRECAC_ERR;
+
+	if (!deschan)
+		return 0;
+
+	if (dfs->dfs_precac_chwidth == CH_WIDTH_160MHZ &&
+	    WLAN_IS_CHAN_MODE_160(dfs->dfs_autoswitch_chan)) {
+		channels[0] = deschan->dfs_ch_mhz_freq_seg2;
+		channels[1] = 0;
+		nchannels = 1;
+	} else {
+		/* The InterCAC feature is enabled only for 80MHz or 160MHz.
+		 * Hence split preferred channel into two 80MHz channels.
+		 */
+		channels[0] = deschan->dfs_ch_mhz_freq_seg1;
+		channels[1] = deschan->dfs_ch_mhz_freq_seg2;
+		if (WLAN_IS_CHAN_MODE_160(dfs->dfs_autoswitch_chan)) {
+			if (deschan->dfs_ch_freq >
+			    deschan->dfs_ch_mhz_freq_seg2)
+				channels[1] -= DFS_160MHZ_SECSEG_CHAN_OFFSET;
+			else
+				channels[1] += DFS_160MHZ_SECSEG_CHAN_OFFSET;
+		}
+		nchannels = 2;
+	}
+
+	for (i = 0; i < nchannels; i++) {
+		PRECAC_LIST_LOCK(dfs);
+		TAILQ_FOREACH(tmp_precac_entry,
+			      &dfs->dfs_precac_list,
+			      pe_list) {
+			if (IS_WITHIN_RANGE(channels[i],
+					    tmp_precac_entry->center_ch_freq,
+					    tmp_precac_entry->bw)) {
+				precac_state = dfs_find_precac_state_of_node(
+						channels[i],
+						tmp_precac_entry);
+				if (precac_state == PRECAC_REQUIRED) {
+					PRECAC_LIST_UNLOCK(dfs);
+					return channels[i];
+				}
+			}
+		}
+		PRECAC_LIST_UNLOCK(dfs);
+	}
+
+	return 0;
+}
+#endif
+#else
+static inline qdf_freq_t
+dfs_configure_deschan_for_precac(struct wlan_dfs *dfs)
+{
+	return 0;
+}
+#endif
+
 /*
  * dfs_get_ieeechan_for_precac_for_freq() - Get chan frequency for preCAC.
  * @dfs: Pointer to wlan_dfs.
@@ -3835,6 +3912,13 @@ uint16_t dfs_get_ieeechan_for_precac_for_freq(struct wlan_dfs *dfs,
 		 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,
@@ -3849,6 +3933,8 @@ uint16_t dfs_get_ieeechan_for_precac_for_freq(struct wlan_dfs *dfs,
 		}
 	}
 	PRECAC_LIST_UNLOCK(dfs);
+
+exit:
 	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "Channel picked for preCAC = %u",
 		 ieee_chan_freq);
 
@@ -4448,73 +4534,6 @@ void dfs_reset_precac_lists(struct wlan_dfs *dfs)
  * @mode: Wireless mode of channel.
  */
 #ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
-#ifdef CONFIG_CHAN_FREQ_API
-void dfs_set_precac_preferred_channel(struct wlan_dfs *dfs,
-				      struct dfs_channel *chan, uint8_t mode)
-{
-	bool found = false;
-	uint16_t freq_160_sec_mhz = 0;
-	struct dfs_precac_entry *precac_entry;
-
-	if (dfs_is_precac_timer_running(dfs) &&
-	    WLAN_IS_CHAN_MODE_80(chan) &&
-	    (dfs->dfs_precac_secondary_freq_mhz == 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 (WLAN_IS_CHAN_DFS(chan) &&
-	    !TAILQ_EMPTY(&dfs->dfs_precac_list)) {
-		TAILQ_FOREACH(precac_entry,
-			      &dfs->dfs_precac_list, pe_list) {
-			if (precac_entry->vht80_ch_freq ==
-			    chan->dfs_ch_mhz_freq_seg1) {
-				found = true;
-				TAILQ_REMOVE(&dfs->dfs_precac_list,
-					     precac_entry, pe_list);
-				TAILQ_INSERT_HEAD(&dfs->dfs_precac_list,
-						  precac_entry, pe_list);
-				break;
-			}
-		}
-	}
-
-	if (WLAN_IS_CHAN_MODE_160(chan) && WLAN_IS_CHAN_DFS(chan) &&
-	    !TAILQ_EMPTY(&dfs->dfs_precac_list)) {
-		if (chan->dfs_ch_freq < chan->dfs_ch_mhz_freq_seg2)
-			freq_160_sec_mhz = chan->dfs_ch_mhz_freq_seg1 +
-				VHT160_FREQ_DIFF;
-		else
-			freq_160_sec_mhz = chan->dfs_ch_mhz_freq_seg1 -
-				VHT160_FREQ_DIFF;
-
-		found = false;
-		TAILQ_FOREACH(precac_entry,
-			      &dfs->dfs_precac_list, pe_list) {
-			if (precac_entry->vht80_ch_freq ==
-			    freq_160_sec_mhz) {
-				found = true;
-				TAILQ_REMOVE(&dfs->dfs_precac_list,
-					     precac_entry, pe_list);
-				TAILQ_INSERT_HEAD(&dfs->dfs_precac_list,
-						  precac_entry, pe_list);
-				break;
-			}
-		}
-	}
-
-	PRECAC_LIST_UNLOCK(dfs);
-
-	if (!found) {
-		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
-			"frequency not found in precac list");
-		return;
-	}
-}
-#else
 #ifdef CONFIG_CHAN_NUM_API
 void dfs_set_precac_preferred_channel(struct wlan_dfs *dfs,
 				      struct dfs_channel *chan, uint8_t mode)
@@ -4582,7 +4601,6 @@ void dfs_set_precac_preferred_channel(struct wlan_dfs *dfs,
 	}
 }
 #endif
-#endif
 
 #ifdef CONFIG_CHAN_NUM_API
 bool
@@ -4638,46 +4656,56 @@ dfs_decide_precac_preferred_chan_for_freq(struct wlan_dfs *dfs,
 					  uint16_t *pref_chan_freq,
 					  enum wlan_phymode mode)
 {
-	struct dfs_channel chan;
+	struct dfs_channel *chan;
+
+	chan = qdf_mem_malloc(sizeof(struct dfs_channel));
+
+	if (!chan) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "malloc failed");
+		return false;
+	}
 
-	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
 	if (QDF_STATUS_SUCCESS !=
 	    dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj,
 					      *pref_chan_freq, 0,
 					      mode,
-					      &chan.dfs_ch_freq,
-					      &chan.dfs_ch_flags,
-					      &chan.dfs_ch_flagext,
-					      &chan.dfs_ch_ieee,
-					      &chan.dfs_ch_vhtop_ch_freq_seg1,
-					      &chan.dfs_ch_vhtop_ch_freq_seg2,
-					      &chan.dfs_ch_mhz_freq_seg1,
-					      &chan.dfs_ch_mhz_freq_seg2))
-		return false;
+					      &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))
+		goto exit;
 	if (!dfs->dfs_precac_inter_chan_freq)
-		return false;
+		goto exit;
 
 	/*
 	 * 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) ||
-	     (WLAN_IS_CHAN_MODE_160(&chan) &&
-	      WLAN_IS_CHAN_DFS_CFREQ2(&chan))) &&
-	    !dfs_is_precac_done(dfs, &chan)) {
-		dfs_set_precac_preferred_channel(dfs, &chan, mode);
-		dfs->dfs_autoswitch_des_chan_freq = *pref_chan_freq;
+	if ((WLAN_IS_CHAN_DFS(chan) ||
+	     (WLAN_IS_CHAN_MODE_160(chan) &&
+	      WLAN_IS_CHAN_DFS_CFREQ2(chan))) &&
+	    !dfs_is_precac_done(dfs, chan)) {
 		dfs->dfs_autoswitch_des_mode = mode;
 		*pref_chan_freq = dfs->dfs_precac_inter_chan_freq;
 		dfs_debug(dfs, WLAN_DEBUG_DFS,
 			  "des_chan=%d, des_mode=%d. Current operating channel=%d",
-			  dfs->dfs_autoswitch_des_chan_freq,
+			  chan->dfs_ch_freq,
 			  dfs->dfs_autoswitch_des_mode,
 			  *pref_chan_freq);
+		dfs->dfs_autoswitch_chan = chan;
 		return true;
 	}
 
-	dfs->dfs_precac_inter_chan_freq = chan.dfs_ch_freq;
+	/* Since preCAC is completed on the user configured preferred channel,
+	 * make this channel the future intermediate channel.
+	 */
+	dfs->dfs_precac_inter_chan_freq = chan->dfs_ch_freq;
+exit:
+	qdf_mem_free(chan);
 	return false;
 }
 #endif
@@ -4907,6 +4935,7 @@ void dfs_get_ieeechan_for_agilecac(struct wlan_dfs *dfs,
 						     dfs->dfs_precac_chwidth);
 
 	dfs->dfs_soc_obj->ocac_status = OCAC_RESET;
+
 	ieee_chan = dfs_get_ieeechan_for_precac(dfs,
 						pri_ch_ieee,
 						sec_ch_ieee,