]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211_hwsim: Add support for extended FTM ranging
authorKavita Kavita <kavita.kavita@oss.qualcomm.com>
Mon, 20 Apr 2026 09:08:56 +0000 (14:38 +0530)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 5 May 2026 12:48:56 +0000 (14:48 +0200)
Introduce support for continuous ranging and advanced timing
parameters in the FTM request, response, and capability paths.
This enables more flexible ranging scenarios with improved control
over measurement timing and session management.

Co-developed-by: Peddolla Harshavardhan Reddy <peddolla.reddy@oss.qualcomm.com>
Signed-off-by: Peddolla Harshavardhan Reddy <peddolla.reddy@oss.qualcomm.com>
Signed-off-by: Kavita Kavita <kavita.kavita@oss.qualcomm.com>
Link: https://patch.msgid.link/20260420090856.2152905-14-peddolla.reddy@oss.qualcomm.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/virtual/mac80211_hwsim_main.c

index e510357c14110bc9ca656821ce5009635db2c2a5..3a0c4366dfdb9d6e135971666ee5887da5fab69b 100644 (file)
@@ -746,6 +746,17 @@ hwsim_ftm_result_policy[NL80211_PMSR_FTM_RESP_ATTR_MAX + 1] = {
        [NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD] = { .type = NLA_U64 },
        [NL80211_PMSR_FTM_RESP_ATTR_LCI] = { .type = NLA_STRING },
        [NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC] = { .type = NLA_STRING },
+       [NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS] = { .type = NLA_U8 },
+       [NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS] = { .type = NLA_U8 },
+       [NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR] = { .type = NLA_FLAG },
 };
 
 static const struct nla_policy
@@ -779,6 +790,19 @@ hwsim_pmsr_peers_result_policy[NL80211_PMSR_ATTR_MAX + 1] = {
        [NL80211_PMSR_ATTR_PEERS] = NLA_POLICY_NESTED_ARRAY(hwsim_pmsr_peer_result_policy),
 };
 
+static const struct nla_policy
+hwsim_ftm_role_capa_policy[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1] = {
+       [NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB] = { .type = NLA_FLAG },
+       [NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB] = { .type = NLA_FLAG },
+       [NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA] = { .type = NLA_FLAG },
+};
+
+static const struct nla_policy
+hwsim_ftm_type_capa_policy[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX + 1] = {
+       [NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT] = { .type = NLA_FLAG },
+       [NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT] = { .type = NLA_FLAG },
+};
+
 static const struct nla_policy
 hwsim_ftm_capa_policy[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1] = {
        [NL80211_PMSR_FTM_CAPA_ATTR_ASAP] = { .type = NLA_FLAG },
@@ -791,6 +815,19 @@ hwsim_ftm_capa_policy[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1] = {
        [NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST] = NLA_POLICY_MAX(NLA_U8, 31),
        [NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED] = { .type = NLA_FLAG },
        [NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED] = { .type = NLA_FLAG },
+       [NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS] = { .type = NLA_U8 },
+       [NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS] = { .type = NLA_U8 },
+       [NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS] =
+               NLA_POLICY_NESTED(hwsim_ftm_role_capa_policy),
+       [NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS] =
+               NLA_POLICY_NESTED(hwsim_ftm_role_capa_policy),
+       [NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS] =
+               NLA_POLICY_NESTED(hwsim_ftm_type_capa_policy),
+       [NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT] = { .type = NLA_FLAG },
 };
 
 static const struct nla_policy
@@ -3527,6 +3564,47 @@ static int mac80211_hwsim_send_pmsr_ftm_request_peer(struct sk_buff *msg,
        if (nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR, request->bss_color))
                return -ENOBUFS;
 
+       if (request->min_time_between_measurements &&
+           nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS,
+                       request->min_time_between_measurements))
+               return -ENOBUFS;
+
+       if (request->max_time_between_measurements &&
+           nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS,
+                       request->max_time_between_measurements))
+               return -ENOBUFS;
+
+       if (request->availability_window &&
+           nla_put_u8(msg, NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION,
+                      request->availability_window))
+               return -ENOBUFS;
+
+       if (request->nominal_time &&
+           nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME,
+                       request->nominal_time))
+               return -ENOBUFS;
+
+       if (request->num_measurements &&
+           nla_put_u32(msg, NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS,
+                       request->num_measurements))
+               return -ENOBUFS;
+
+       if (request->ingress_distance &&
+           nla_put_u64_64bit(msg, NL80211_PMSR_FTM_REQ_ATTR_INGRESS,
+                             request->ingress_distance,
+                             NL80211_PMSR_FTM_REQ_ATTR_PAD))
+               return -ENOBUFS;
+
+       if (request->egress_distance &&
+           nla_put_u64_64bit(msg, NL80211_PMSR_FTM_REQ_ATTR_EGRESS,
+                             request->egress_distance,
+                             NL80211_PMSR_FTM_REQ_ATTR_PAD))
+               return -ENOBUFS;
+
+       if (request->pd_suppress_range_results &&
+           nla_put_flag(msg, NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS))
+               return -ENOBUFS;
+
        nla_nest_end(msg, ftm);
 
        return 0;
@@ -3896,6 +3974,69 @@ static int mac80211_hwsim_parse_ftm_result(struct nlattr *ftm,
                result->civicloc_len = nla_len(tb[NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC]);
        }
 
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT]) {
+               result->tx_ltf_repetition_count_valid = 1;
+               result->tx_ltf_repetition_count =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_TX_LTF_REPETITION_COUNT]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT]) {
+               result->rx_ltf_repetition_count_valid = 1;
+               result->rx_ltf_repetition_count =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_RX_LTF_REPETITION_COUNT]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]) {
+               result->max_time_between_measurements_valid = 1;
+               result->max_time_between_measurements =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]) {
+               result->min_time_between_measurements_valid = 1;
+               result->min_time_between_measurements =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS]) {
+               result->num_tx_spatial_streams_valid = 1;
+               result->num_tx_spatial_streams =
+                       nla_get_u8(tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_TX_SPATIAL_STREAMS]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS]) {
+               result->num_rx_spatial_streams_valid = 1;
+               result->num_rx_spatial_streams =
+                       nla_get_u8(tb[NL80211_PMSR_FTM_RESP_ATTR_NUM_RX_SPATIAL_STREAMS]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME]) {
+               result->nominal_time_valid = 1;
+               result->nominal_time =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_NOMINAL_TIME]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW]) {
+               result->availability_window_valid = 1;
+               result->availability_window =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_AVAILABILITY_WINDOW]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH]) {
+               result->chan_width_valid = 1;
+               result->chan_width =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_CHANNEL_WIDTH]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE]) {
+               result->preamble_valid = 1;
+               result->preamble =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_RESP_ATTR_PREAMBLE]);
+       }
+
+       result->is_delayed_lmr =
+               nla_get_flag(tb[NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR]);
+
        return 0;
 }
 
@@ -6280,6 +6421,82 @@ static int parse_ftm_capa(const struct nlattr *ftm_capa, struct cfg80211_pmsr_ca
        out->ftm.trigger_based = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED];
        out->ftm.non_trigger_based = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED];
 
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS])
+               out->ftm.max_no_of_tx_antennas =
+                       nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS]);
+
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS])
+               out->ftm.max_no_of_rx_antennas =
+                       nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS]);
+
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA])
+               out->ftm.min_allowed_ranging_interval_edca =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA]);
+
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB])
+               out->ftm.min_allowed_ranging_interval_ntb =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB]);
+
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES])
+               out->ftm.pd_preambles =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES]);
+
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS])
+               out->ftm.pd_bandwidths =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS]);
+
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS]) {
+               struct nlattr *ista_tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
+
+               if (!nla_parse_nested(ista_tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX,
+                                     tb[NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS],
+                                     hwsim_ftm_role_capa_policy, NULL)) {
+                       out->ftm.ista.support_ntb =
+                               !!ista_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB];
+                       out->ftm.ista.support_tb =
+                               !!ista_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB];
+                       out->ftm.ista.support_edca =
+                               !!ista_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA];
+                       if (ista_tb[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE])
+                               out->ftm.ista.max_peers =
+                                       nla_get_u32(ista_tb[NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE]);
+               }
+       }
+
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS]) {
+               struct nlattr *rsta_tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
+
+               if (!nla_parse_nested(rsta_tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX,
+                                     tb[NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS],
+                                     hwsim_ftm_role_capa_policy, NULL)) {
+                       out->ftm.rsta.support_ntb =
+                               !!rsta_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB];
+                       out->ftm.rsta.support_tb =
+                               !!rsta_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB];
+                       out->ftm.rsta.support_edca =
+                               !!rsta_tb[NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA];
+                       if (rsta_tb[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE])
+                               out->ftm.rsta.max_peers =
+                                       nla_get_u32(rsta_tb[NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE]);
+               }
+       }
+
+       if (tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS]) {
+               struct nlattr *type_tb[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX + 1];
+
+               if (!nla_parse_nested(type_tb, NL80211_PMSR_FTM_TYPE_CAPA_ATTR_MAX,
+                                     tb[NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS],
+                                     hwsim_ftm_type_capa_policy, NULL)) {
+                       out->ftm.type.infra_support =
+                               !!type_tb[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT];
+                       out->ftm.type.pd_support =
+                               !!type_tb[NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT];
+               }
+       }
+
+       out->ftm.concurrent_ista_rsta_support =
+               !!tb[NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT];
+
        return 0;
 }