Răsfoiți Sursa

qcacmn: Coalesce the frequency ranges of AFC payload

To mark a 20 MHz standard power channel (chan X) as AFC done, we check
if X's range is a subset of a AFC frequency object payload (say Y).
If X is not a subset of Y, chan X is marked as "AFC not done" and is
disabled in the regulatory's AFC channel list.

However, it is possible that channel X's range is split across various
frequency objects of the AFC payload. The current algorithm does not
coalesce the adjacent frequency object payload so that X becomes a
subset. This change aims to coalesce the AFC payload as and when needed.

If the frequency ranges of the AFC payload are sliced and the
PSD power of a 20 MHz channel is split across multiple frequency ranges
in the AFC payload, coalesce the frequency ranges to form a single
range that can fit channel X so that the PSD power can be fetched.
The coalesced PSD power will be minimum PSD power of the frequency
ranges where coalescing is done.
If coalescing is not required, pick the PSD power from AFC payload
directly.

CRs-Fixed: 3387610
Change-Id: Ib358028a6842cb6644eaae2964738d391a5dd763
Priyadarshnee Srinivasan 2 ani în urmă
părinte
comite
62c9261e5f

+ 190 - 9
umac/regulatory/core/src/reg_build_chan_list.c

@@ -4659,6 +4659,183 @@ static void reg_find_high_limit_chan_enum_for_6g(
 	}
 }
 
+/**
+ * reg_find_range_for_chan_idx() - Compute freq range object for the
+ * given chan_enum in the given channel list.
+ * @chan_enum: Channel enum
+ * @afc_chan_list: Pointer to regulatory channel list
+ * @range: Pointer to frequency range to be filled
+ *
+ * Return: None
+ */
+static void
+reg_find_range_for_chan_idx(enum channel_enum chan_enum,
+			    struct regulatory_channel *afc_chan_list,
+			    struct freq_range *range)
+{
+	qdf_freq_t center_freq = afc_chan_list[chan_enum].center_freq;
+	uint16_t min_bw = afc_chan_list[chan_enum].min_bw;
+
+	range->left = center_freq - min_bw / 2;
+	range->right = center_freq + min_bw / 2;
+}
+
+/**
+ * reg_is_range_subset_of_freq_obj() - Return true if the given range
+ * fits into the freq_obj range
+ * @range: Pointer to range
+ * @freq_obj: Pointer to frequency object
+ *
+ * Return: True if the range fits, false otherwise
+ */
+static bool
+reg_is_range_subset_of_freq_obj(struct freq_range *range,
+				struct afc_freq_obj *freq_obj)
+{
+	return (range->left >= freq_obj->low_freq &&
+		range->right <= freq_obj->high_freq);
+}
+
+/**
+ * reg_coalesce_afc_freq_info() - Coalesce the frequency objects of the
+ * AFC payload.
+ *
+ * @power_info: Pointer to afc payload
+ * @in_range: Pointer to the current freq range.
+ * @in_out_freq_index: frequency index, is both an input and output value.
+ * @out_coal_freq_obj: Pointer to coalesced freq range
+ *
+ * If the high freq of n_freq_obj is same as low_freq of
+ * n+1_freq_obj, coalescing can be done. The coalesced object is the
+ * minimum length object that includes the given @in_range. The in_range should
+ * completely fall within the output coalesced frequency object.
+ * "in_out_freq_index" is updated to the new index only if range fits
+ * in the coalesced freq object and it is based on the number of
+ * frequency objects coalesced.
+ *
+ * Return: None
+ */
+static void
+reg_coalesce_afc_freq_info(struct reg_fw_afc_power_event *power_info,
+			   struct freq_range *in_range,
+			   uint8_t *in_out_freq_index,
+			   struct afc_freq_obj *out_coal_freq_obj)
+{
+	struct afc_freq_obj *cur_freq_obj, *n_freq_obj, coal_freq_obj;
+	uint8_t cur_freq_index = *in_out_freq_index;
+	uint8_t nxt_freq_index = cur_freq_index + 1;
+
+	coal_freq_obj = power_info->afc_freq_info[cur_freq_index];
+	*out_coal_freq_obj = coal_freq_obj;
+
+	/* The low edge of the input range must fall within freq object
+	 * else coalescing is meaningless.
+	 * eg: center freq 6135 cannot fit in the range 6123-6129 and hence
+	 * considering this range need not be considered for any tx power
+	 * manipulation.
+	 */
+	if (!IS_WITHIN_RANGE_ASYM(in_range->left, coal_freq_obj.low_freq,
+				  coal_freq_obj.high_freq))
+		return;
+
+	/* Coalesecing is not needed if the input range is already a subset of
+	 * freq obj range of the afc payload.
+	 */
+	if (reg_is_range_subset_of_freq_obj(in_range, &coal_freq_obj))
+		return;
+
+	/* The input range is not within the first freq object.
+	 * Keep coalescing until the input range is found in the
+	 * coalesced object.
+	 */
+	while (nxt_freq_index < power_info->num_freq_objs) {
+		cur_freq_obj = &power_info->afc_freq_info[cur_freq_index];
+		n_freq_obj = &power_info->afc_freq_info[nxt_freq_index];
+
+		if (cur_freq_obj->high_freq == n_freq_obj->low_freq) {
+			coal_freq_obj.high_freq = n_freq_obj->high_freq;
+			coal_freq_obj.max_psd = qdf_min(coal_freq_obj.max_psd,
+							n_freq_obj->max_psd);
+			/* Exit if the coalesced object already
+			 * includes the input range.
+			 */
+			if (reg_is_range_subset_of_freq_obj(in_range,
+							    &coal_freq_obj)) {
+				/* Since the coalesced object includes upto
+				 * nxt_freq_index, update the in_out_freq_index
+				 * so that it is used in the caller to skip
+				 * the last processed index.
+				 */
+				*in_out_freq_index = nxt_freq_index;
+				*out_coal_freq_obj = coal_freq_obj;
+				return;
+			}
+		} else {
+			/* current object and next object not continuous */
+			break;
+		}
+		cur_freq_index++;
+		nxt_freq_index++;
+	}
+
+	reg_debug("Coalesced freq range: low: %u, high: %u, psd: %d\n",
+		  out_coal_freq_obj->low_freq, out_coal_freq_obj->high_freq,
+		  out_coal_freq_obj->max_psd);
+}
+
+/**
+ * reg_find_low_and_high_limit() - Find low_limit and high_limit channel enum
+ * for the given freq range.
+ * @afc_chan_list: Pointer to regulatory_channel
+ * @freq_obj: Pointer to struct afc_freq_obj
+ * @low_limit_enum: Pointer to low limit channel enum
+ * @high_limit_enum: Pointer to high limit channel enum
+ */
+static void
+reg_find_low_and_high_limit(struct regulatory_channel *afc_chan_list,
+			    struct afc_freq_obj *freq_obj,
+			    enum channel_enum *low_limit_enum,
+			    enum channel_enum *high_limit_enum)
+{
+	reg_find_low_limit_chan_enum_for_6g(afc_chan_list,
+					    freq_obj->low_freq,
+					    low_limit_enum);
+	reg_find_high_limit_chan_enum_for_6g(afc_chan_list,
+					     freq_obj->high_freq,
+					     high_limit_enum);
+}
+
+/**
+ * reg_try_coalescing_freq_objs() - Try to coalesce frequency objects.
+ *
+ * @chan_enum: channel enum @afc_chan_list, that may be ignored by the
+ * current frequency object power_info[*cur_freq_index], if coalescing
+ * does not happen.
+ * @afc_chan_list: Pointer to afc channel list
+ * @power_info: Pointer to reg_fw_afc_power_event
+ * @cur_freq_index: Pointer to freq_index of the afc payload
+ * @coal_freq_obj: Pointer to coal_freq_obj
+ *
+ * Try to coalesce adjacent frequency objects of the afc response
+ * if coalescing is needed. After coalescing,  return the coalesced_freq_obj
+ * if it is valid. If invalid, return the current freq object.
+ *
+ * Return: None
+ */
+static void
+reg_try_coalescing_freq_objs(enum channel_enum chan_enum,
+			     struct regulatory_channel *afc_chan_list,
+			     struct reg_fw_afc_power_event *power_info,
+			     uint8_t *cur_freq_index,
+			     struct afc_freq_obj *coal_freq_obj)
+{
+	struct freq_range range;
+
+	reg_find_range_for_chan_idx(chan_enum, afc_chan_list, &range);
+	reg_coalesce_afc_freq_info(power_info, &range, cur_freq_index,
+				   coal_freq_obj);
+}
+
 /**
  * reg_fill_max_psd_in_afc_chan_list() - Fill max_psd in the afc master chan
  * list
@@ -4676,6 +4853,8 @@ static QDF_STATUS reg_fill_max_psd_in_afc_chan_list(
 	uint8_t i;
 	struct regulatory_channel *sp_chan_list;
 	struct regulatory_channel *cfi_chan_list;
+	enum channel_enum last_enum = reg_convert_enum_to_6g_idx(MIN_6GHZ_CHANNEL);
+	struct afc_freq_obj coal_freq_obj = {};
 
 	if (!power_info) {
 		reg_err("power_info is NULL");
@@ -4702,16 +4881,18 @@ static QDF_STATUS reg_fill_max_psd_in_afc_chan_list(
 				  power_info);
 
 	for (i = 0; i < power_info->num_freq_objs; i++) {
-		struct afc_freq_obj *freq_obj = &power_info->afc_freq_info[i];
 		enum channel_enum low_limit_enum, high_limit_enum;
 		uint8_t j;
 
-		reg_find_low_limit_chan_enum_for_6g(afc_chan_list,
-						    freq_obj->low_freq,
-						    &low_limit_enum);
-		reg_find_high_limit_chan_enum_for_6g(afc_chan_list,
-						     freq_obj->high_freq,
-						     &high_limit_enum);
+		/* Counter variable 'i' may be incremented in the following
+		 * function. The following function guarantees that  'i'
+		 * shall never be greater than the number of frequency objects.
+		 */
+		reg_try_coalescing_freq_objs(last_enum, afc_chan_list,
+					     power_info, &i, &coal_freq_obj);
+		reg_find_low_and_high_limit(afc_chan_list, &coal_freq_obj,
+					    &low_limit_enum, &high_limit_enum);
+
 		for (j = low_limit_enum; j <= high_limit_enum; j++) {
 			if ((sp_chan_list[j].state == CHANNEL_STATE_ENABLE) &&
 			    (cfi_chan_list[j].state == CHANNEL_STATE_ENABLE)) {
@@ -4723,13 +4904,13 @@ static QDF_STATUS reg_fill_max_psd_in_afc_chan_list(
 				 * target sends the PSD in the units of
 				 * 0.01 dbm/MHz.
 				 */
-				afc_chan_list[j].psd_eirp =
-							freq_obj->max_psd / 100;
+				afc_chan_list[j].psd_eirp = coal_freq_obj.max_psd / 100;
 				afc_chan_list[j].psd_flag = true;
 				afc_chan_list[j].tx_power =
 						cfi_chan_list[j].tx_power;
 			}
 		}
+		last_enum = j;
 	}
 
 	qdf_mem_free(cfi_chan_list);

+ 4 - 0
umac/regulatory/core/src/reg_build_chan_list.h

@@ -48,6 +48,10 @@
 #define HALF_IEEE_CH_SEP  2
 #define IEEE_20MHZ_CH_SEP 4
 
+/* Check if the freq lies within low_freq and high_freq (both inclusive) */
+#define IS_WITHIN_RANGE_ASYM(_freq, _low_freq, _high_freq)\
+	(((_freq) >= (_low_freq) && (_freq) <= (_high_freq)))
+
 #include "reg_priv_objs.h"
 /**
  * reg_reset_reg_rules() - provides the reg domain rules info