[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,
+ ¢er_freq1, ¢er_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