[PATCH v3 10/46] nl80211: Parse Peer Measurement (PMSR) capabilities
Kavita Kavita
kavita.kavita at oss.qualcomm.com
Wed May 13 02:59:34 PDT 2026
From: Peddolla Harshavardhan Reddy <peddolla.reddy at oss.qualcomm.com>
Add support for parsing FTM capabilities for Proximity Ranging (PR).
Parse PMSR attributes and store them in nested structs (ista, rsta,
ranging_type). This includes support for:
- ISTA and RSTA role capabilities (NTB, TB, EDCA support)
- Per-role peer limits
- Ranging type support (infrastructure and proximity detection)
- Concurrent ISTA/RSTA support
- 6GHz support
- Proximity Detection preambles and bandwidths
- Minimum ranging intervals for EDCA and NTB
The parsed values are stored in wpa_driver_capa.
Signed-off-by: Peddolla Harshavardhan Reddy <peddolla.reddy at oss.qualcomm.com>
---
src/common/proximity_ranging.h | 14 +++
src/drivers/driver.h | 29 +++++
src/drivers/driver_nl80211_capa.c | 170 ++++++++++++++++++++++++++++++
wpa_supplicant/pr_supplicant.c | 8 ++
wpa_supplicant/wpa_supplicant.c | 6 +-
5 files changed, 226 insertions(+), 1 deletion(-)
diff --git a/src/common/proximity_ranging.h b/src/common/proximity_ranging.h
index 14106d2fb..85fc40770 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,16 @@ 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;
+
+ u8 max_ftms_per_burst;
+
bool secure_he_ltf;
u8 max_tx_ltf_repetations;
@@ -388,6 +400,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 bbab06e7b..32cdbe800 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2660,6 +2660,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;
@@ -2671,6 +2672,34 @@ 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;
+ u8 max_ftms_per_burst;
+ bool concurrent_ista_rsta;
+ bool support_6ghz;
+ u32 pd_preambles;
+ u32 pd_bandwidths;
+
+ struct {
+ bool support_ntb;
+ bool support_tb;
+ bool support_edca;
+ u32 max_peers;
+ } ista;
+
+ struct {
+ bool support_ntb;
+ bool support_tb;
+ bool support_edca;
+ u32 max_peers;
+ } rsta;
+
+ struct {
+ bool infra_support;
+ bool pd_support;
+ } ranging_type;
#ifdef CONFIG_NAN
struct nan_capa nan_capa;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 1799c5395..281f06d22 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)
@@ -1136,6 +1137,170 @@ out:
#endif /* CONFIG_NAN */
+#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;
+
+ /* Parse ISTA capabilities */
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS]) {
+ struct nlattr *ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
+
+ if (!nla_parse_nested(ista_caps, NL80211_PMSR_FTM_CAPA_ATTR_MAX,
+ tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS],
+ NULL)) {
+ drv->capa.ista.support_ntb =
+ !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB];
+ drv->capa.ista.support_tb =
+ !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB];
+ drv->capa.ista.support_edca =
+ !!ista_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA];
+ if (ista_caps[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE])
+ drv->capa.ista.max_peers =
+ nla_get_u32(ista_caps[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE]);
+ }
+ }
+
+ /* Parse ranging type capabilities */
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS]) {
+ struct nlattr *type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX + 1];
+
+ if (!nla_parse_nested(type_caps,
+ NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX,
+ tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS],
+ NULL)) {
+ drv->capa.ranging_type.infra_support =
+ !!type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT];
+ drv->capa.ranging_type.pd_support =
+ !!type_caps[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT];
+ }
+ }
+
+ drv->capa.concurrent_ista_rsta =
+ !!tb[NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT];
+
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TX_LTF_REP])
+ drv->capa.max_tx_ltf_repetations =
+ nla_get_u32(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_u32(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_u32(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_u32(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_u32(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_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX]);
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST])
+ drv->capa.max_ftms_per_burst =
+ nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST]);
+
+ /* Parse RSTA capabilities */
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT] &&
+ tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS]) {
+ struct nlattr *rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
+
+ if (!nla_parse_nested(rsta_caps, NL80211_PMSR_FTM_CAPA_ATTR_MAX,
+ tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS],
+ NULL)) {
+ drv->capa.rsta.support_ntb =
+ !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB];
+ drv->capa.rsta.support_tb =
+ !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB];
+ drv->capa.rsta.support_edca =
+ !!rsta_caps[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA];
+ if (rsta_caps[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE])
+ drv->capa.rsta.max_peers =
+ nla_get_u32(rsta_caps[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE]);
+ }
+ }
+
+ 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]);
+
+ /* Parse additional ranging capabilities */
+ drv->capa.support_6ghz =
+ !!tb[NL80211_PMSR_FTM_CAPA_ATTR_6GHZ_SUPPORT];
+
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES])
+ drv->capa.pd_preambles =
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES]);
+ if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS])
+ drv->capa.pd_bandwidths =
+ nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS]);
+}
+
+
+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 },
+ };
+
+ 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;
+ }
+
+ 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_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];
@@ -1262,6 +1427,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 f47407e5c..0af25f5d4 100644
--- a/wpa_supplicant/pr_supplicant.c
+++ b/wpa_supplicant/pr_supplicant.c
@@ -375,6 +375,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 = capa->concurrent_ista_rsta;
+ pr.pmsr_max_peers = capa->pmsr_max_peers;
+ pr.pr_max_peer_ista_role = capa->ista.max_peers;
+ pr.pr_max_peer_rsta_role = capa->rsta.max_peers;
+ pr.max_ftms_per_burst = capa->max_ftms_per_burst;
+
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 5414eab0f..f54cb02ed 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -8071,8 +8071,12 @@ 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 (!capa.ranging_type.pd_support) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Driver does not support Proximity Ranging - PR disabled");
+ } else if (wpas_pr_init(wpa_s->global, wpa_s, &capa) < 0) {
return -1;
+ }
if (wpa_bss_init(wpa_s) < 0)
return -1;
--
2.34.1
More information about the Hostap
mailing list