[PATCH 11/16] PR: Use preferred frequency for channel selection in PASN ranging

Peddolla Harshavardhan Reddy peddolla.reddy at oss.qualcomm.com
Sun Jul 12 23:29:59 PDT 2026


Currently the channel for proximity ranging is always selected
automatically from the common channel set using the best available
option. This gives no control to the caller over which channel is
used for ranging.

Add a preferred_freq field to pr_pasn_ranging_params. When set,
the frequency is resolved to a channel and the op_class with the
highest bandwidth containing that channel is selected from the
common channel set. If the preferred frequency is not present in
the common channel set, automatic channel selection is used as
a fallback.

Signed-off-by: Peddolla Harshavardhan Reddy <peddolla.reddy at oss.qualcomm.com>
---
 src/common/proximity_ranging.c | 111 ++++++++++++++++++++++++++++++++-
 src/common/proximity_ranging.h |   1 +
 2 files changed, 109 insertions(+), 3 deletions(-)

diff --git a/src/common/proximity_ranging.c b/src/common/proximity_ranging.c
index 7786dcd6e..40abf9809 100644
--- a/src/common/proximity_ranging.c
+++ b/src/common/proximity_ranging.c
@@ -609,6 +609,71 @@ static void pr_channels_intersect(const struct pr_channels *a,
 	}
 }
 
+
+static bool pr_is_channel_in_list(const struct pr_channels *chan,
+				  u8 op_class, u8 channel)
+{
+	size_t i, j;
+
+	for (i = 0; i < chan->op_classes; i++) {
+		if (chan->op_class[i].op_class != op_class)
+			continue;
+		for (j = 0; j < chan->op_class[i].channels; j++) {
+			if (chan->op_class[i].channel[j] == channel)
+				return true;
+		}
+	}
+	return false;
+}
+
+
+static bool pr_get_preferred_channel(int preferred_freq,
+				     const struct pr_channels *common_chan,
+				     u8 *op_class, u8 *op_channel)
+{
+	u8 base_op_class = 0, channel = 0;
+	enum hostapd_hw_mode hw_mode;
+	const struct oper_class_map *map;
+	u8 best_op_class = 0;
+	int best_bw = 0;
+	size_t i, j;
+
+	if (!preferred_freq || !common_chan || !op_class || !op_channel)
+		return false;
+
+	/* Resolve freq to a base channel number via the 20 MHz op_class */
+	hw_mode = ieee80211_freq_to_channel_ext(preferred_freq, 0, 0,
+						&base_op_class, &channel);
+	if (hw_mode == NUM_HOSTAPD_MODES)
+		return false;
+
+	/* Among all op_classes in common_chan that contain this channel,
+	 * pick the one with the highest bandwidth.
+	 */
+	for (i = 0; i < common_chan->op_classes; i++) {
+		for (j = 0; j < common_chan->op_class[i].channels; j++) {
+			if (common_chan->op_class[i].channel[j] != channel)
+				continue;
+			map = get_oper_class(NULL,
+					     common_chan->op_class[i].op_class);
+			if (!map || map->mode != hw_mode)
+				break;
+			if (oper_class_bw_to_int(map) > best_bw) {
+				best_bw = oper_class_bw_to_int(map);
+				best_op_class = common_chan->op_class[i].op_class;
+			}
+			break;
+		}
+	}
+
+	if (!best_op_class)
+		return false;
+
+	*op_class = best_op_class;
+	*op_channel = channel;
+	return true;
+}
+
 #endif /* CONFIG_PASN */
 
 
@@ -1406,6 +1471,7 @@ static u8 pr_pasn_get_best_op_mode(struct pr_data *pr, u8 peer_supp_roles,
 	int status = PR_NEGOTIATION_FAIL;
 	u8 op_class = 0, op_channel = 0;
 	bool own_ista_support = false, own_rsta_support = false;
+	bool preferred_found;
 
 	if (op_mode->protocol_type &
 	    (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) {
@@ -1507,11 +1573,24 @@ static u8 pr_pasn_get_best_op_mode(struct pr_data *pr, u8 peer_supp_roles,
 	res_op_mode->protocol_type = ranging_type;
 	os_memcpy(res_op_mode->country, pr->cfg->country, 3);
 
-	if (res_op_mode->role == PR_RSTA_SUPPORT) {
+	preferred_found = pr->pr_pasn_params &&
+		pr->pr_pasn_params->preferred_freq &&
+		pr_get_preferred_channel(pr->pr_pasn_params->preferred_freq,
+					 &common_chan, &op_class, &op_channel);
+
+	if (!preferred_found && res_op_mode->role == PR_RSTA_SUPPORT) {
 		pr_copy_channels(&res_op_mode->channels, &common_chan,
 				 pr->cfg->support_6ghz);
 	} else {
-		pr_choose_best_channel(&common_chan, &op_class, &op_channel);
+		if (preferred_found) {
+			wpa_printf(MSG_DEBUG,
+				   "PR: Using preferred_freq %d: op_class=%u, channel=%u",
+				   pr->pr_pasn_params->preferred_freq,
+				   op_class, op_channel);
+		} else {
+			pr_choose_best_channel(&common_chan, &op_class,
+					       &op_channel);
+		}
 		if (!op_class || !op_channel) {
 			wpa_printf(MSG_DEBUG,
 				   "PR: Couldn't choose a common channel for ranging in ISTA role");
@@ -1611,7 +1690,16 @@ static u8 pr_pasn_get_final_op_mode(struct pr_data *pr, u8 supp_roles,
 		return PR_NEGOTIATION_FAIL;
 	}
 
-	pr_choose_best_channel(&common_chan, &op_class, &op_channel);
+	if (pr->pr_pasn_params && pr->pr_pasn_params->preferred_freq &&
+	    pr_get_preferred_channel(pr->pr_pasn_params->preferred_freq,
+				     &common_chan, &op_class, &op_channel)) {
+		wpa_printf(MSG_DEBUG,
+			   "PR: Using preferred_freq %d: op_class=%u, channel=%u",
+			   pr->pr_pasn_params->preferred_freq, op_class,
+			   op_channel);
+	} else {
+		pr_choose_best_channel(&common_chan, &op_class, &op_channel);
+	}
 	if (!op_class || !op_channel) {
 		wpa_printf(MSG_INFO,
 			   "PR: Couldn't choose a common channel for ranging");
@@ -1644,7 +1732,10 @@ static int pr_prepare_pasn_pr_elem(struct pr_data *pr, struct wpabuf *extra_ies,
 	struct operation_mode op_mode;
 	struct pr_channels op_channels;
 	u8 forced_op_class = 0, forced_op_channel = 0;
+	u8 pref_op_class = 0, pref_op_channel = 0;
 	enum hostapd_hw_mode hw_mode;
+	enum hostapd_hw_mode pref_hw_mode;
+	int pref_freq = 0;
 
 	buf = wpabuf_alloc(1000);
 	if (!buf)
@@ -1671,6 +1762,20 @@ static int pr_prepare_pasn_pr_elem(struct pr_data *pr, struct wpabuf *extra_ies,
 		return -1;
 	}
 
+	if (pr->pr_pasn_params && pr->pr_pasn_params->preferred_freq &&
+	    !forced_pr_freq) {
+		pref_freq = pr->pr_pasn_params->preferred_freq;
+		pref_hw_mode = ieee80211_freq_to_channel_ext(pref_freq, 0, 0,
+							     &pref_op_class,
+							     &pref_op_channel);
+		if (pref_hw_mode == NUM_HOSTAPD_MODES ||
+		    !pr_is_channel_in_list(&op_channels, pref_op_class,
+					   pref_op_channel))
+			wpa_printf(MSG_DEBUG,
+				   "PR: preferred_freq %d is not present in device channel list, ignoring",
+				   pref_freq);
+	}
+
 	os_memset(&op_mode, 0, sizeof(struct operation_mode));
 	op_mode.role = ranging_role;
 	op_mode.protocol_type = ranging_type;
diff --git a/src/common/proximity_ranging.h b/src/common/proximity_ranging.h
index a9b917520..d992464c7 100644
--- a/src/common/proximity_ranging.h
+++ b/src/common/proximity_ranging.h
@@ -352,6 +352,7 @@ struct pr_pasn_ranging_params {
 	u32 continuous_ranging_session_time;
 
 	int forced_pr_freq;
+	int preferred_freq;
 	u8 ranging_op_class;
 	u16 channel_width; /* channel width in MHz (20/40/80/160/320) */
 	u8 format_bw;
-- 
2.34.1




More information about the Hostap mailing list