[RFC PATCH 04/23] nl80211: Parse Peer Measurement (PMSR) capabilities

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


From: Peddolla Harshavardhan Reddy <peddolla at qti.qualcomm.com>

This commit adds support for parsing the NL80211_ATTR_PEER_MEASUREMENTS
attribute to determine the driver's capabilities for Proximity Ranging
(PR) using Fine Timing Measurement (FTM).

The new functionality includes:
- A handler to parse the main PMSR capabilities attribute.
- A specific parser for FTM capabilities (pmsr_capa_handler) that
  extracts details such as ASAP support, trigger-based options,
  antenna configurations, and supported ranging intervals.
- Updates the driver capability flags (drv->capa) with the parsed
  Proximity Ranging features.

Signed-off-by: Peddolla Harshavardhan Reddy <peddolla at qti.qualcomm.com>
---
 src/common/proximity_ranging.h    |  12 +++
 src/drivers/driver.h              |  12 +++
 src/drivers/driver_nl80211_capa.c | 128 ++++++++++++++++++++++++++++++
 wpa_supplicant/pr_supplicant.c    |   8 ++
 wpa_supplicant/wpa_supplicant.c   |   3 +-
 5 files changed, 162 insertions(+), 1 deletion(-)

diff --git a/src/common/proximity_ranging.h b/src/common/proximity_ranging.h
index 14106d2fb..536d51375 100644
--- a/src/common/proximity_ranging.h
+++ b/src/common/proximity_ranging.h
@@ -358,6 +358,8 @@ struct pr_config {
 
 	u8 edca_format_and_bw;
 
+	u32 edca_min_ranging_interval;
+
 	u8 max_tx_antenna;
 
 	u8 max_rx_antenna;
@@ -368,6 +370,14 @@ struct pr_config {
 
 	bool ntb_rsta_support;
 
+	bool concurrent_ista_rsta;
+
+	u32 pmsr_max_peers;
+
+	u32 pr_max_peer_ista_role;
+
+	u32 pr_max_peer_rsta_role;
+
 	bool secure_he_ltf;
 
 	u8 max_tx_ltf_repetations;
@@ -388,6 +398,8 @@ struct pr_config {
 
 	u8 ntb_format_and_bw;
 
+	u32 ntb_min_ranging_interval;
+
 	struct pr_channels ntb_channels;
 
 	bool support_6ghz;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 268229edd..5f05550be 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2446,6 +2446,11 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS2_802_1X_AUTH		0x0000000800000000ULL
 /** Driver supports MAC address filter for remain-on-channel */
 #define WPA_DRIVER_FLAGS2_ROC_ADDR_FILTER	0x0000001000000000ULL
+/** Driver supports Proximity Ranging (PR) */
+#define WPA_DRIVER_FLAGS2_PR_SUPPORT		0x000000200000000ULL
+/** Driver supports concurrent ISTA and RSTA roles for PR */
+#define WPA_DRIVER_FLAGS2_PR_CONCURRENT_ISTA_RSTA	0x0000004000000000ULL
+
 	u64 flags2;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2575,6 +2580,7 @@ struct wpa_driver_capa {
 	u8 edca_format_and_bw;
 	u8 max_tx_antenna;
 	u8 max_rx_antenna;
+	u32 edca_min_ranging_interval;
 
 	/* NTB based ranging capabilities */
 	u8 ntb_format_and_bw;
@@ -2586,6 +2592,12 @@ struct wpa_driver_capa {
 	u8 max_rx_sts_gt_80;
 	u8 max_tx_sts_le_80;
 	u8 max_tx_sts_gt_80;
+	u32 ntb_min_ranging_interval;
+
+	/* Peer measurement capabilities */
+	u32 pmsr_max_peers;
+	u32 pr_max_peer_ista_role;
+	u32 pr_max_peer_rsta_role;
 
 #ifdef CONFIG_NAN
 /* Driver supports dual band NAN operation */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index b8bfb6613..c9308812d 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -18,6 +18,7 @@
 #include "common/qca-vendor-attr.h"
 #include "common/brcm_vendor.h"
 #include "driver_nl80211.h"
+#include "common/proximity_ranging.h"
 
 
 static int protocol_feature_handler(struct nl_msg *msg, void *arg)
@@ -964,6 +965,128 @@ static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
 }
 
 
+#ifdef CONFIG_PR
+
+static void pmsr_type_ftm_handler(struct wpa_driver_nl80211_data *drv,
+				  struct nlattr *capa)
+{
+	u32 max_rx_sts, max_tx_sts;
+	struct nlattr *tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
+
+	if (nla_parse_nested(tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX, capa, NULL))
+		return;
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_ASAP])
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_FTM_INITIATOR;
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED])
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_INITIATOR;
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_SUPPORT])
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PR_SUPPORT;
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_CONCURRENT_ISTA_RSTA_SUPPORT])
+		drv->capa.flags2 |=
+			WPA_DRIVER_FLAGS2_PR_CONCURRENT_ISTA_RSTA;
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP])
+		drv->capa.max_tx_ltf_repetations =
+			nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP]);
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_LTF_REP])
+		drv->capa.max_rx_ltf_repetations =
+			nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_LTF_REP]);
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_STS]) {
+		max_rx_sts = nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_RX_STS]);
+		drv->capa.max_rx_sts_le_80 = max_rx_sts;
+		drv->capa.max_rx_sts_gt_80 = max_rx_sts;
+	}
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_STS]) {
+		max_tx_sts = nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_STS]);
+		drv->capa.max_tx_sts_le_80 = max_tx_sts;
+		drv->capa.max_tx_sts_gt_80 = max_tx_sts;
+	}
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX])
+		drv->capa.max_tx_ltf_total =
+			nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_TX]);
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX])
+		drv->capa.max_rx_ltf_total =
+			nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX]);
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT] &&
+	    tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT_NTB])
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_NON_TRIGGER_BASED_RESPONDER;
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS])
+		drv->capa.max_tx_antenna =
+			nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS]);
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS])
+		drv->capa.max_rx_antenna =
+			nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS]);
+
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA])
+		drv->capa.edca_min_ranging_interval =
+			nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA]);
+	if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB])
+		drv->capa.ntb_min_ranging_interval =
+			nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB]);
+}
+
+
+static void wiphy_info_pmsr_type_capa(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr *attr)
+{
+	struct nlattr *pos;
+	int rem;
+
+	nla_for_each_nested(pos, attr, rem) {
+		if (nla_type(pos) == NL80211_PMSR_TYPE_FTM)
+			pmsr_type_ftm_handler(drv, pos);
+	}
+}
+
+
+static void wiphy_info_pmsr_capa(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr *tb[])
+{
+	struct nlattr *pmsr_capa[NL80211_PMSR_ATTR_MAX + 1];
+	static struct nla_policy
+	pmsr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
+		[NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_U32 },
+		[NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_NESTED },
+		[NL80211_PMSR_ATTR_PD_MAX_PEER_ISTA_ROLE] = {
+			.type = NLA_U32
+		},
+		[NL80211_PMSR_ATTR_PD_MAX_PEER_RSTA_ROLE] = {
+			.type = NLA_U32
+		},
+	};
+
+	if (nla_parse_nested(pmsr_capa, NL80211_PMSR_ATTR_MAX,
+			     tb[NL80211_ATTR_PEER_MEASUREMENTS],
+			     pmsr_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed To parse PMSR capabilities");
+		return;
+	}
+
+	/* Parse PMSR capablities */
+	if (pmsr_capa[NL80211_PMSR_ATTR_MAX_PEERS])
+		drv->capa.pmsr_max_peers =
+		nla_get_u32(pmsr_capa[NL80211_PMSR_ATTR_MAX_PEERS]);
+	if (pmsr_capa[NL80211_PMSR_ATTR_PD_MAX_PEER_ISTA_ROLE])
+		drv->capa.pr_max_peer_ista_role =
+		nla_get_u32(pmsr_capa[NL80211_PMSR_ATTR_PD_MAX_PEER_ISTA_ROLE]);
+	if (pmsr_capa[NL80211_PMSR_ATTR_PD_MAX_PEER_RSTA_ROLE])
+		drv->capa.pr_max_peer_rsta_role =
+		nla_get_u32(pmsr_capa[NL80211_PMSR_ATTR_PD_MAX_PEER_RSTA_ROLE]);
+
+	if (pmsr_capa[NL80211_PMSR_ATTR_TYPE_CAPA])
+		wiphy_info_pmsr_type_capa(drv,
+					  pmsr_capa[NL80211_PMSR_ATTR_TYPE_CAPA]);
+}
+
+#endif /* CONFIG_PR */
+
 static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1090,6 +1213,11 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 
 	wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]);
 
+#ifdef CONFIG_PR
+	if (tb[NL80211_ATTR_PEER_MEASUREMENTS])
+		wiphy_info_pmsr_capa(drv, tb);
+#endif /* CONFIG_PR */
+
 	if (tb[NL80211_ATTR_VENDOR_DATA]) {
 		struct nlattr *nl;
 		int rem;
diff --git a/wpa_supplicant/pr_supplicant.c b/wpa_supplicant/pr_supplicant.c
index c50d4bd92..a218d78cf 100644
--- a/wpa_supplicant/pr_supplicant.c
+++ b/wpa_supplicant/pr_supplicant.c
@@ -372,6 +372,14 @@ int wpas_pr_init(struct wpa_global *global, struct wpa_supplicant *wpa_s,
 	pr.max_tx_sts_le_80 = capa->max_tx_sts_le_80;
 	pr.max_tx_sts_gt_80 = capa->max_tx_sts_gt_80;
 
+	pr.edca_min_ranging_interval = capa->edca_min_ranging_interval;
+	pr.ntb_min_ranging_interval = capa->ntb_min_ranging_interval;
+	pr.concurrent_ista_rsta = !!(wpa_s->drv_flags2 &
+		WPA_DRIVER_FLAGS2_PR_CONCURRENT_ISTA_RSTA);
+	pr.pmsr_max_peers = capa->pmsr_max_peers;
+	pr.pr_max_peer_ista_role = capa->pr_max_peer_ista_role;
+	pr.pr_max_peer_rsta_role = capa->pr_max_peer_rsta_role;
+
 	pr.support_6ghz = wpas_is_6ghz_supported(wpa_s, true);
 
 	pr.pasn_send_mgmt = wpas_pr_pasn_send_mgmt;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 72079bccf..bd7910ea9 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -8052,7 +8052,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 		return -1;
 	}
 
-	if (wpas_pr_init(wpa_s->global, wpa_s, &capa) < 0)
+	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PR_SUPPORT) &&
+	    wpas_pr_init(wpa_s->global, wpa_s, &capa) < 0)
 		return -1;
 
 	if (wpa_bss_init(wpa_s) < 0)
-- 
2.34.1




More information about the Hostap mailing list