[RFC PATCH 19/23] nl80211: Add peer measurement support for proximity ranging

Peddolla Harshavardhan Reddy peddolla.reddy at oss.qualcomm.com
Tue Mar 31 22:48:57 PDT 2026


Introduce support for triggering FTM-based proximity ranging
via the NL80211 peer measurement interface after PASN auth.

Add nl80211_start_peer_measurement() to build and send
NL80211_CMD_PEER_MEASUREMENT_START with nested attributes for
timeout handling, proximity detection, preamble selection, and
FTM parameter structure. Channel configuration includes proper
center frequency calculation and mapping for EDCA and NTB.

Signed-off-by: Peddolla Harshavardhan Reddy <peddolla.reddy at oss.qualcomm.com>
---
 src/common/proximity_ranging.h |   7 +
 src/drivers/driver.h           |  20 +++
 src/drivers/driver_nl80211.c   | 302 +++++++++++++++++++++++++++++++++
 wpa_supplicant/driver_i.h      |  16 ++
 wpa_supplicant/pr_supplicant.c | 213 ++++++++++++++++++++++-
 5 files changed, 557 insertions(+), 1 deletion(-)

diff --git a/src/common/proximity_ranging.h b/src/common/proximity_ranging.h
index cd12aaa50..6f597410f 100644
--- a/src/common/proximity_ranging.h
+++ b/src/common/proximity_ranging.h
@@ -273,6 +273,7 @@ struct pr_pasn_ranging_params {
 	u8 pr_pasn_status;
 	u8 auth_mode;
 	int freq;
+	u32 ranging_timeout;
 	u8 src_addr[ETH_ALEN];
 	enum pr_pasn_role pasn_role;
 
@@ -353,6 +354,12 @@ struct pr_pasn_ranging_params {
 	u32 continuous_ranging_session_time;
 
 	int forced_pr_freq;
+	u8 ranging_op_class;
+	u16 channel_width; /* channel width in MHz (20/40/80/160/320) */
+	u8 format_bw;
+	u32 center_freq1;
+	u32 center_freq2;
+	u64 cookie;
 };
 
 struct pr_dev_ik {
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 348634fad..c250bdaa9 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -30,6 +30,7 @@
 
 struct nan_subscribe_params;
 struct nan_publish_params;
+struct pr_pasn_ranging_params;
 
 #define HOSTAPD_CHAN_DISABLED 0x00000001
 #define HOSTAPD_CHAN_NO_IR 0x00000002
@@ -5660,6 +5661,25 @@ struct wpa_driver_ops {
 	struct hostapd_multi_hw_info *
 	(*get_multi_hw_info)(void *priv, unsigned int *num_multi_hws);
 
+#ifdef CONFIG_PR
+	/**
+	 * start_peer_measurement - Start peer measurement (FTM ranging)
+	 * @priv: Private driver interface data
+	 * @peer_addr: Peer MAC address
+	 * @freq: Operating frequency in MHz
+	 * @channel: Operating channel number
+	 * @bw: Channel bandwidth in MHz
+	 * @params: Ranging parameters (EDCA/NTB specific)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function triggers peer measurement (FTM ranging) after
+	 * successful PASN authentication for proximity ranging.
+	 */
+	int (*start_peer_measurement)(void *priv, const u8 *peer_addr,
+				      int freq, u8 channel, int bw,
+				      struct pr_pasn_ranging_params *params);
+#endif /* CONFIG_PR */
+
 #ifdef CONFIG_NAN
 	/**
 	 * nan_start - Start NAN operation
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 2543bc187..370701aa5 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -41,6 +41,7 @@
 #include "radiotap_iter.h"
 #include "rfkill.h"
 #include "driver_nl80211.h"
+#include "common/proximity_ranging.h"
 
 
 #ifndef NETLINK_CAP_ACK
@@ -9110,6 +9111,306 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
 					    0, NULL, 0, 0, link_id);
 }
 
+#ifdef CONFIG_PR
+
+static u8 get_pr_preamble(u8 ranging_type, u8 format_bw)
+{
+	/* Determine preamble based on ranging type and format_bw */
+	if (ranging_type & PR_EDCA_BASED_RANGING) {
+		/* EDCA format_bw maps to different preambles */
+		switch (format_bw) {
+		case EDCA_FORMAT_AND_BW_HT40:
+			return NL80211_PREAMBLE_HT;
+		case EDCA_FORMAT_AND_BW_VHT20:
+		case EDCA_FORMAT_AND_BW_VHT40:
+		case EDCA_FORMAT_AND_BW_VHT80:
+		case EDCA_FORMAT_AND_BW_VHT80P80:
+		case EDCA_FORMAT_AND_BW_VHT160_DUAL_LO:
+		case EDCA_FORMAT_AND_BW_VHT160_SINGLE_LO:
+			return NL80211_PREAMBLE_VHT;
+		default:
+			return NL80211_PREAMBLE_VHT;
+		}
+	} else if (ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) {
+		/* NTB format_bw all use HE preamble */
+		return NL80211_PREAMBLE_HE;
+	}
+
+	/* Default fallback */
+	return NL80211_PREAMBLE_VHT;
+}
+
+
+/**
+ * nl80211_start_peer_measurement - Start peer measurement (FTM ranging)
+ * @priv: Private driver interface data
+ * @peer_addr: Peer MAC address
+ * @freq: Operating frequency in MHz
+ * @channel: Operating channel number
+ * @bw: Channel bandwidth in MHz
+ * @params: Ranging parameters (EDCA/NTB specific)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function constructs and sends NL80211_CMD_PEER_MEASUREMENT_START
+ * command to trigger FTM ranging after successful PASN authentication.
+ */
+static int nl80211_start_peer_measurement(void *priv, const u8 *peer_addr,
+					  int freq, u8 channel, int bw,
+					  struct pr_pasn_ranging_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct i802_bss *pd_bss;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *pmsr_attr, *peers_attr, *peer_attr, *chan_attr, *req_attr, *ftm_attr;
+	struct nlattr *data_attr;
+	int ret = -1, center_freq1 = 0, center_freq2 = 0;
+	struct nl80211_ack_ext_arg ack_arg;
+	enum nl80211_chan_width width;
+	u32 preamble;
+	u64 cookie;
+
+	if (!peer_addr || !params) {
+		wpa_printf(MSG_ERROR, "nl80211: Invalid parameters for peer measurement");
+		return -1;
+	}
+
+	pd_bss = nl80211_get_pd_bss_by_addr(drv, params->src_addr);
+	if (pd_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Peer measurement routed via PD wdev addr=" MACSTR,
+			   MAC2STR(pd_bss->addr));
+		bss = pd_bss;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Start peer measurement for " MACSTR " freq=%d ch=%u bw=%d",
+		   MAC2STR(peer_addr), freq, channel, bw);
+
+	/* Use center frequencies from params (calculated in wpas_pr_trigger_ranging) */
+	center_freq1 = params->center_freq1;
+	center_freq2 = params->center_freq2;
+
+	/* channel_width is in MHz */
+	switch (params->channel_width) {
+	case 20:
+		width = NL80211_CHAN_WIDTH_20;
+		break;
+	case 40:
+		width = NL80211_CHAN_WIDTH_40;
+		break;
+	case 80:
+		width = NL80211_CHAN_WIDTH_80;
+		break;
+	case 160:
+		width = NL80211_CHAN_WIDTH_160;
+		break;
+	case 320:
+		width = NL80211_CHAN_WIDTH_320;
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "nl80211: Unsupported channel width %u MHz",
+			   params->channel_width);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Using center_freq1=%u, center_freq2=%u, width=%d",
+		   center_freq1, center_freq2, width);
+	/*
+	 * PD interfaces are non-netdev (ifindex=0); use nl80211_cmd_msg()
+	 * which emits NL80211_ATTR_WDEV instead of NL80211_ATTR_IFINDEX.
+	 */
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_PEER_MEASUREMENT_START);
+	if (!msg)
+		return -1;
+
+	/* Add timeout if specified */
+	if (params->ranging_timeout > 0) {
+		if (nla_put_u32(msg, NL80211_ATTR_TIMEOUT, params->ranging_timeout))
+			goto fail;
+		wpa_printf(MSG_DEBUG, "nl80211: Added timeout %u ms", params->ranging_timeout);
+	}
+
+	/* Add peer measurements attribute */
+	pmsr_attr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
+	if (!pmsr_attr)
+		goto fail;
+
+	/* Add peers array */
+	peers_attr = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS);
+	if (!peers_attr)
+		goto fail;
+
+	/* Add single peer */
+	peer_attr = nla_nest_start(msg, 0);
+	if (!peer_attr)
+		goto fail;
+
+	/* Peer MAC address */
+	if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, peer_addr))
+		goto fail;
+
+	/* Add proximity detection request flag */
+	if (nla_put_flag(msg, NL80211_PMSR_PEER_ATTR_PD_REQUEST))
+		goto fail;
+
+	/* Channel information */
+	chan_attr = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_CHAN);
+	if (!chan_attr)
+		goto fail;
+
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+	    nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, width) ||
+	    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, center_freq1))
+		goto fail;
+
+	if (center_freq2 && nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, center_freq2))
+		goto fail;
+
+	nla_nest_end(msg, chan_attr);
+
+
+	/* Request attributes */
+	req_attr = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_REQ);
+	if (!req_attr)
+		goto fail;
+
+	/* Add DATA attribute as required by NL80211 specification */
+	data_attr = nla_nest_start(msg, NL80211_PMSR_REQ_ATTR_DATA);
+
+	if (!data_attr)
+		goto fail;
+
+	/* FTM request */
+	ftm_attr = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM);
+	if (!ftm_attr)
+		goto fail;
+
+	preamble = get_pr_preamble(params->ranging_type, params->format_bw);
+	if (nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, preamble))
+		goto fail;
+
+	/* Map ranging parameters to NL80211 attributes based on protocol type */
+	if (params->ranging_role == PR_ISTA_SUPPORT &&
+	    (params->ranging_type & PR_EDCA_BASED_RANGING)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Adding EDCA ranging parameters");
+
+		if (nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_ASAP) ||
+		    nla_put_u16(msg, NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD,
+				params->burst_period) ||
+		    nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP,
+			       params->num_bursts_exp) ||
+		    nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
+			       params->ftms_per_burst) ||
+		    nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES,
+			       params->ftmr_retries) ||
+		    nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION,
+			       params->burst_duration))
+			goto fail;
+	}
+
+	if (params->ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING | PR_NTB_OPEN_BASED_RANGING)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Adding NTB ranging parameters");
+
+		/* Set non-trigger based flag */
+		if (nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED))
+			goto fail;
+
+		if (params->ranging_role == PR_ISTA_SUPPORT &&
+		    (nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS,
+				params->min_time_between_measurements) ||
+		    nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS,
+				params->max_time_between_measurements) ||
+		    nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION,
+				params->availability_window) ||
+		    nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME,
+				params->nominal_time) ||
+		    nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_MEAS_PER_AW,
+				params->measurements_per_aw)))
+			goto fail;
+	}
+
+	/* Location requests */
+	if (params->request_lci &&
+	    nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI))
+		goto fail;
+
+	if (params->request_civicloc &&
+	    nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC))
+		goto fail;
+
+	/* RSTA mode if responder role */
+	if (params->ranging_role == PR_RSTA_SUPPORT &&
+	    nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_RSTA))
+		goto fail;
+
+	/*
+	 * LMR feedback: negotiate LMR feedback for NTB ranging so that
+	 * the RSTA can report its measurement results back to the ISTA.
+	 * Only valid when NON_TRIGGER_BASED is set.
+	 */
+	if ((params->ranging_type & (PR_NTB_SECURE_LTF_BASED_RANGING |
+				     PR_NTB_OPEN_BASED_RANGING)) &&
+	    nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK))
+		goto fail;
+
+	/*
+	 * PD ingress/egress thresholds: only valid when
+	 * NL80211_PMSR_PEER_ATTR_PD_REQUEST is set (always the case here).
+	 * Thresholds are in millimeters.
+	 */
+	if (params->ingress_threshold &&
+	    nla_put_u64(msg, NL80211_PMSR_FTM_REQ_ATTR_INGRESS,
+			params->ingress_threshold))
+		goto fail;
+
+	if (params->egress_threshold &&
+	    nla_put_u64(msg, NL80211_PMSR_FTM_REQ_ATTR_EGRESS,
+			params->egress_threshold))
+		goto fail;
+
+	/*
+	 * PD suppress results: suppress ranging results for PD requests.
+	 */
+	if (params->pr_suppress_results &&
+	    nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS))
+		goto fail;
+
+	nla_nest_end(msg, ftm_attr);
+	nla_nest_end(msg, data_attr);
+	nla_nest_end(msg, req_attr);
+	nla_nest_end(msg, peer_attr);
+	nla_nest_end(msg, peers_attr);
+	nla_nest_end(msg, pmsr_attr);
+
+	cookie = 0;
+	os_memset(&ack_arg, 0, sizeof(struct nl80211_ack_ext_arg));
+	ack_arg.ext_data = &cookie;
+	ret = send_and_recv(drv, drv->global->nl, msg, NULL, NULL,
+			    ack_handler_cookie, &ack_arg, NULL);
+
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Peer measurement start failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Peer measurement started successfully addr=" MACSTR
+			   " cookie=%llu", MAC2STR(peer_addr),
+			   (unsigned long long) cookie);
+		params->cookie = cookie;
+	}
+
+	return ret;
+
+fail:
+	wpa_printf(MSG_ERROR, "nl80211: Failed to build peer measurement message");
+	nlmsg_free(msg);
+	return -1;
+}
+
+#endif /* CONFIG_PR */
 
 static void dump_ifidx(struct wpa_driver_nl80211_data *drv)
 {
@@ -15883,5 +16184,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.pd_start = nl80211_pd_start,
 	.pd_stop = nl80211_pd_stop,
 	.get_pd_addr = nl80211_get_pd_addr,
+	.start_peer_measurement = nl80211_start_peer_measurement,
 #endif /* CONFIG_PR */
 };
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index f2eba74dc..3d1988fd3 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -758,6 +758,22 @@ wpa_drv_pd_stop(struct wpa_supplicant *wpa_s)
 
 #endif /* CONFIG_PR */
 
+#ifdef CONFIG_PR
+
+static inline int
+wpa_drv_start_peer_measurement(struct wpa_supplicant *wpa_s, const u8 *peer,
+			       int freq, u8 channel, int bw,
+			       struct pr_pasn_ranging_params *params)
+{
+	if (!wpa_s->driver->start_peer_measurement)
+		return -1;
+	return wpa_s->driver->start_peer_measurement(wpa_s->drv_priv, peer,
+						      freq, channel, bw,
+						      params);
+}
+
+#endif /* CONFIG_PR */
+
 static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
 				     int vendor_id, int subcmd, const u8 *data,
 				     size_t data_len,
diff --git a/wpa_supplicant/pr_supplicant.c b/wpa_supplicant/pr_supplicant.c
index 2504a2030..bc52c8389 100644
--- a/wpa_supplicant/pr_supplicant.c
+++ b/wpa_supplicant/pr_supplicant.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/proximity_ranging.h"
 #include "p2p/p2p.h"
 #include "wpa_supplicant_i.h"
@@ -135,6 +136,107 @@ wpas_pr_ntb_is_valid_op_class(u32 bw_bitmap, u32 preamble_bitmap,
 }
 
 
+/**
+ * wpas_pr_op_class_to_chan_params - Derive channel parameters from operating
+ *     class and channel number
+ * @op_class: IEEE 802.11 operating class
+ * @op_channel: Primary channel number
+ * @center_freq1: Output - center frequency segment 0 (MHz)
+ * @center_freq2: Output - center frequency segment 1 (MHz), 0 if not used
+ * @width: Output - channel bandwidth
+ * Returns: 0 on success, -1 on failure
+ *
+ * Derives center_freq1 and bandwidth from the operating class and primary
+ * channel using existing hostap helpers. 2 GHz 40 MHz op_classes are handled
+ * explicitly because 2.4 GHz channels are 5 MHz apart and the generic
+ * offset-based formula does not apply. 80+80 MHz op_classes (130, 135) are
+ * rejected because center_freq2 cannot be determined from the primary channel
+ * alone.
+ */
+static int wpas_pr_op_class_to_chan_params(u8 op_class, u8 op_channel,
+					   u32 *center_freq1,
+					   u32 *center_freq2,
+					   u16 *channel_width)
+{
+	int control_freq, bw, offset = 0;
+
+	if (!center_freq1 || !center_freq2 || !channel_width)
+		return -1;
+
+	*center_freq1 = 0;
+	*center_freq2 = 0;
+	*channel_width = 0;
+
+	/* 80+80 MHz: center_freq2 is unknown from primary channel alone */
+	if (op_class == 130 || op_class == 135) {
+		wpa_printf(MSG_DEBUG,
+			   "PR: op_class %u (80+80 MHz) not supported, "
+			   "center_freq2 cannot be determined", op_class);
+		return -1;
+	}
+
+	control_freq = ieee80211_chan_to_freq(NULL, op_class, op_channel);
+	if (control_freq < 0) {
+		wpa_printf(MSG_DEBUG, "PR: Invalid op_class=%u channel=%u",
+			   op_class, op_channel);
+		return -1;
+	}
+
+	bw = op_class_to_bandwidth(op_class);
+
+	/*
+	 * 2 GHz 40 MHz: channels are 5 MHz apart so the generic offset
+	 * formula does not apply. Handle upper (op_class 83) and lower
+	 * (op_class 84) secondary channel directions explicitly.
+	 */
+	if (op_class == 83) {
+		*center_freq1 = control_freq + 10;
+		*channel_width = 40;
+		return 0;
+	}
+	if (op_class == 84) {
+		*center_freq1 = control_freq - 10;
+		*channel_width = 40;
+		return 0;
+	}
+
+	if (bw == 20) {
+		*center_freq1 = control_freq;
+		*channel_width = 20;
+		return 0;
+	}
+
+	/*
+	 * For 5 GHz and 6 GHz wider bandwidths, compute the primary channel's
+	 * position (in 20 MHz steps) within its band segment, then derive
+	 * center_freq1 using the formula:
+	 *   center_freq1 = control_freq + (bw/2 - 10) - (offset & (bw/20 - 1)) * 20
+	 */
+	if (control_freq >= 5955)
+		offset = (control_freq - 5955) / 20;
+	else if (control_freq >= 5745)
+		offset = (control_freq - 5745) / 20;
+	else if (control_freq >= 5180)
+		offset = (control_freq - 5180) / 20;
+
+	/* Distance from the primary channel to the center of the BW block */
+	int half_bw = bw / 2 - 10;
+
+	/* Position of the primary channel within its BW block (in 20 MHz steps) */
+	int block_pos = offset & (bw / 20 - 1);
+
+	/* Center = primary channel + half-BW offset - position within block */
+	*center_freq1 = control_freq + half_bw - block_pos * 20;
+	*channel_width = bw;
+
+	wpa_printf(MSG_DEBUG,
+		   "PR: op_class=%u ch=%u -> freq=%d cf1=%u bw=%d",
+		   op_class, op_channel, control_freq, *center_freq1, bw);
+
+	return 0;
+}
+
+
 static void
 wpas_pr_setup_edca_channels(struct wpa_supplicant *wpa_s,
 			    struct pr_channels *chan,
@@ -275,6 +377,111 @@ static void wpas_pr_pasn_result(void *ctx, u8 role, u8 protocol_type,
 }
 
 
+/**
+ * wpas_pr_trigger_ranging - Trigger ranging measurement after PASN auth
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @peer_addr: Peer MAC address
+ * @freq: Operating frequency in MHz
+ * @op_class: Operating class
+ * @op_channel: Operating channel number
+ * @bw: Channel bandwidth enum value
+ * @format_bw: Format and bandwidth value
+ * @protocol_type: Ranging protocol type (EDCA/NTB)
+ *
+ * This function triggers the actual ranging measurement after successful
+ * PASN authentication by constructing and sending the NL80211 peer
+ * measurement start command.
+ */
+static int wpas_pr_trigger_ranging(struct wpa_supplicant *wpa_s,
+				   const u8 *peer_addr, int freq, u8 op_class,
+				   u8 op_channel, u8 format_bw,
+				   u8 protocol_type)
+{
+	struct pr_data *pr = wpa_s->global->pr;
+	struct pr_pasn_ranging_params *params;
+	u16 channel_width = 0;
+	u32 center_freq1 = 0, center_freq2 = 0;
+	int ret;
+
+	if (!pr || !pr->pr_pasn_params) {
+		wpa_printf(MSG_DEBUG,
+			   "PR: No ranging params available for " MACSTR,
+			   MAC2STR(peer_addr));
+		return -1;
+	}
+
+	params = pr->pr_pasn_params;
+
+	wpa_printf(MSG_DEBUG,
+		   "PR: Triggering ranging for " MACSTR " freq=%d ch=%u",
+		   MAC2STR(peer_addr), freq, op_channel);
+
+	/* Derive center frequencies and channel width (MHz) from op_class */
+	ret = wpas_pr_op_class_to_chan_params(op_class, op_channel,
+					      &center_freq1, &center_freq2,
+					      &channel_width);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   "PR: Failed to derive channel params for "
+			   "op_class=%u ch=%u", op_class, op_channel);
+		goto fail;
+	}
+
+	/* Populate ranging params */
+	params->ranging_op_class = op_class;
+	params->channel_width = channel_width;
+	params->format_bw = format_bw;
+	params->center_freq1 = center_freq1;
+	params->center_freq2 = center_freq2;
+
+	/* Validate format_bw is within enum limits before setting preamble */
+	if (protocol_type & PR_EDCA_BASED_RANGING) {
+		if (format_bw < EDCA_FORMAT_AND_BW_VHT20 ||
+		    format_bw >= EDCA_FORMAT_AND_BW_MAX) {
+			wpa_printf(MSG_ERROR,
+				   "PR: Invalid EDCA format_bw %u (valid range: %u-%u)",
+				   format_bw, EDCA_FORMAT_AND_BW_VHT20,
+				   EDCA_FORMAT_AND_BW_MAX - 1);
+			goto fail;
+		}
+	} else if (protocol_type & (PR_NTB_SECURE_LTF_BASED_RANGING |
+				    PR_NTB_OPEN_BASED_RANGING)) {
+		if (format_bw >= NTB_FORMAT_AND_BW_MAX) {
+			wpa_printf(MSG_ERROR,
+				   "PR: Invalid NTB format_bw %u (valid range: 0-%u)",
+				   format_bw, NTB_FORMAT_AND_BW_MAX - 1);
+			goto fail;
+		}
+	} else {
+		wpa_printf(MSG_ERROR, "PR: Unknown protocol_type %u",
+			   protocol_type);
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "PR: Ranging params - op_class=%u, ch_width=%u, format_bw=%u, cf1=%u, cf2=%u",
+		   params->ranging_op_class, params->channel_width,
+		   params->format_bw, params->center_freq1, params->center_freq2);
+
+	/* Call driver operation to start peer measurement */
+	if (wpa_drv_start_peer_measurement(wpa_s, peer_addr, freq, op_channel,
+					   channel_width, params) < 0) {
+		wpa_printf(MSG_ERROR, "PR: Failed to start peer measurement");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "PR: Successfully triggered ranging measurement");
+
+	return 0;
+
+fail:
+	/* Clean up on failure */
+	os_free(pr->pr_pasn_params);
+	pr->pr_pasn_params = NULL;
+	return -1;
+}
+
+
 static void wpas_pr_ranging_params(void *ctx, const u8 *dev_addr,
 				   const u8 *peer_addr, u8 ranging_role,
 				   u8 protocol_type, u8 op_class, u8 op_channel,
@@ -283,7 +490,7 @@ static void wpas_pr_ranging_params(void *ctx, const u8 *dev_addr,
 	struct wpa_supplicant *wpa_s = ctx;
 	int bw, format_bw, freq;
 
-	bw = oper_class_bw_to_int(get_oper_class(NULL, op_class));
+	bw = op_class_to_bandwidth(op_class);
 	format_bw = self_format_bw < peer_format_bw ?
 		self_format_bw : peer_format_bw;
 	freq = ieee80211_chan_to_freq(NULL, op_class, op_channel);
@@ -291,6 +498,10 @@ static void wpas_pr_ranging_params(void *ctx, const u8 *dev_addr,
 	wpas_notify_pr_ranging_params(wpa_s, dev_addr, peer_addr, ranging_role,
 				      protocol_type, freq, op_channel, bw,
 				      format_bw);
+
+	/* Trigger ranging measurement after successful PASN authentication */
+	wpas_pr_trigger_ranging(wpa_s, peer_addr, freq, op_class, op_channel,
+				format_bw, protocol_type);
 }
 
 
-- 
2.34.1




More information about the Hostap mailing list