]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: cfg80211: add NTB continuous ranging and FTM request type support
authorPeddolla Harshavardhan Reddy <peddolla.reddy@oss.qualcomm.com>
Mon, 20 Apr 2026 09:08:49 +0000 (14:38 +0530)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 5 May 2026 11:36:30 +0000 (13:36 +0200)
Enable NTB continuous ranging with configurable timing and measurement
parameters as per the Wi-Fi Alliance specification "Proximity Ranging
(PR) Implementation Consideration Draft 1.9 Rev 1, section 5.3". Add
new FTM request attributes for min/max time between measurements,
nominal time (mandatory for NTB), AW duration, and total measurement
count.

Add NL80211_PMSR_PEER_ATTR_REQ_TYPE attribute using the new
nl80211_peer_measurement_ftm_req_type enum to allow userspace to
specify the ranging request type per peer:
  - NL80211_PMSR_FTM_REQ_TYPE_INFRA: STA-to-AP or AP-to-STA ranging
    (default if attribute is absent)
  - NL80211_PMSR_FTM_REQ_TYPE_PD: peer-to-peer ranging

Validate the request type against the device TYPE_CAPS capabilities
advertised via NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS. Reject PD
requests if the device does not advertise PD support.

Reject PD requests that set trigger-based ranging, as TB ranging is
not compatible with peer-to-peer proximity detection.

Add ftms_per_burst limit of 4 for PD NTB ranging requests.

Signed-off-by: Peddolla Harshavardhan Reddy <peddolla.reddy@oss.qualcomm.com>
Link: https://patch.msgid.link/20260420090856.2152905-7-peddolla.reddy@oss.qualcomm.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/nl80211.c
net/wireless/pmsr.c

index 98943a510112ab225d473d89d49a29e7efd332c6..28923539456e61ff8b290ab112b2d9b44f03b510 100644 (file)
@@ -4527,7 +4527,8 @@ struct cfg80211_pmsr_result {
  * @burst_duration: burst duration. If @trigger_based or @non_trigger_based is
  *     set, this is the burst duration in milliseconds, and zero means the
  *     device should pick an appropriate value based on @ftms_per_burst.
- * @ftms_per_burst: number of FTMs per burst
+ * @ftms_per_burst: number of FTMs per burst. If set to 0, the firmware or
+ *     driver can automatically select an appropriate value.
  * @ftmr_retries: number of retries for FTM request
  * @request_lci: request LCI information
  * @request_civicloc: request civic location information
@@ -4544,6 +4545,31 @@ struct cfg80211_pmsr_result {
  * @bss_color: the bss color of the responder. Optional. Set to zero to
  *     indicate the driver should set the BSS color. Only valid if
  *     @non_trigger_based or @trigger_based is set.
+ * @request_type: ranging request type, one of
+ *     &enum nl80211_peer_measurement_ftm_req_type. Defaults to
+ *     %NL80211_PMSR_FTM_REQ_TYPE_INFRA if not specified.
+ * @min_time_between_measurements: minimum time between two consecutive range
+ *     measurements in units of 100 microseconds, for non-trigger based
+ *     ranging. Should be set as short as possible to minimize turnaround
+ *     time, since two-way ranging with delayed LMR requires two measurements.
+ *     Only valid if @non_trigger_based is set.
+ * @max_time_between_measurements: maximum time between two consecutive range
+ *     measurements in units of 10 milliseconds, for non-trigger based
+ *     ranging. Acts as a session timeout; if exceeded, the ranging session
+ *     should be terminated. Only valid if @non_trigger_based is set.
+ * @availability_window: duration of the availability window (AW) in units of
+ *     1 millisecond (0-255 ms). Only valid if @non_trigger_based is set.
+ *     If set to 0, the firmware or driver can automatically select an
+ *     appropriate value.
+ * @nominal_time: Nominal duration between adjacent availability windows
+ *     in units of milli seconds. Only valid if @non_trigger_based is set.
+ *     If set to 0, the firmware or driver can automatically select an
+ *     appropriate value.
+ * @num_measurements: number of Availability Windows (AWs) to schedule
+ *     for non-trigger-based ranging. Each AW may contain multiple FTM
+ *     exchanges as configured by @ftms_per_burst. Only valid if
+ *     @non_trigger_based is set. If set to 0, the firmware or driver
+ *     can automatically select an appropriate value.
  *
  * See also nl80211 for the respective attribute documentation.
  */
@@ -4563,6 +4589,13 @@ struct cfg80211_pmsr_ftm_request_peer {
        u8 ftms_per_burst;
        u8 ftmr_retries;
        u8 bss_color;
+
+       u32 request_type;
+       u32 min_time_between_measurements;
+       u32 max_time_between_measurements;
+       u8 availability_window;
+       u32 nominal_time;
+       u32 num_measurements;
 };
 
 /**
index ea0acce724ca309978a61cb2922b0276112a2b95..6be89e6562769854b4d048481f26441f0f96edd8 100644 (file)
@@ -7991,6 +7991,26 @@ enum nl80211_peer_measurement_resp {
        NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1
 };
 
+/**
+ * enum nl80211_peer_measurement_ftm_req_type - FTM ranging request type,
+ *     used with %NL80211_PMSR_PEER_ATTR_REQ_TYPE
+ *
+ * @NL80211_PMSR_FTM_REQ_TYPE_INFRA: infrastructure ranging, i.e. STA-to-AP
+ * @NL80211_PMSR_FTM_REQ_TYPE_PD: peer-to-peer ranging as defined in the
+ *     Wi-Fi Alliance specification "Proximity Ranging (PR) Implementation
+ *     Consideration Draft 1.9 Rev 1"
+ * @NUM_NL80211_PMSR_FTM_REQ_TYPE: internal
+ * @NL80211_PMSR_FTM_REQ_TYPE_MAX: highest request type value
+ */
+enum nl80211_peer_measurement_ftm_req_type {
+       NL80211_PMSR_FTM_REQ_TYPE_INFRA,
+       NL80211_PMSR_FTM_REQ_TYPE_PD,
+
+       /* keep last */
+       NUM_NL80211_PMSR_FTM_REQ_TYPE,
+       NL80211_PMSR_FTM_REQ_TYPE_MAX = NUM_NL80211_PMSR_FTM_REQ_TYPE - 1
+};
+
 /**
  * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement
  * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid
@@ -8004,6 +8024,9 @@ enum nl80211_peer_measurement_resp {
  * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by
  *     measurement type, with attributes from the
  *     &enum nl80211_peer_measurement_resp inside.
+ * @NL80211_PMSR_PEER_ATTR_REQ_TYPE: u32 attribute specifying the ranging
+ *     request type, using values from &enum nl80211_peer_measurement_ftm_req_type.
+ *     If absent, defaults to %NL80211_PMSR_FTM_REQ_TYPE_INFRA.
  *
  * @NUM_NL80211_PMSR_PEER_ATTRS: internal
  * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number
@@ -8015,6 +8038,7 @@ enum nl80211_peer_measurement_peer_attrs {
        NL80211_PMSR_PEER_ATTR_CHAN,
        NL80211_PMSR_PEER_ATTR_REQ,
        NL80211_PMSR_PEER_ATTR_RESP,
+       NL80211_PMSR_PEER_ATTR_REQ_TYPE,
 
        /* keep last */
        NUM_NL80211_PMSR_PEER_ATTRS,
@@ -8238,9 +8262,11 @@ enum nl80211_peer_measurement_ftm_type_capa {
  *     default 15 i.e. "no preference"). For non-EDCA ranging, this is the
  *     burst duration in milliseconds (optional with default 0, i.e. let the
  *     device decide).
- * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames
- *     requested per burst
+ * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: (Optional) number of successful
+ *     FTM frames requested per burst
  *     (u8, 0-31, optional with default 0 i.e. "no preference")
+ *     If the attribute is absent ("no preference"), the driver or firmware can
+ *     choose a suitable value.
  * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries
  *     (u8, default 3)
  * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag)
@@ -8274,6 +8300,33 @@ enum nl80211_peer_measurement_ftm_type_capa {
  *     Only valid if %NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK is set (so the
  *     RSTA will have the measurement results to report back in the FTM
  *     response).
+ * @NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS: minimum time
+ *     between two consecutive range measurements in units of 100 microseconds,
+ *     for non-trigger based ranging (u32). Should be set as short as possible
+ *     to minimize turnaround time, since two-way ranging with delayed LMR
+ *     requires two measurements. Only valid if
+ *     %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ * @NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS: maximum time
+ *     between two consecutive range measurements in units of 10 milliseconds,
+ *     for non-trigger based ranging (u32). Acts as a session timeout; if
+ *     exceeded, the ranging session should be terminated. Only valid if
+ *     %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ * @NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME: The nominal time field shall be
+ *     set to the nominal duration between adjacent Availability Windows in
+ *     units of milli seconds (u32). Mandatory if
+ *     %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ * @NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION: (Optional) The AW duration field
+ *     shall be set to the duration of the AW in units of 1ms (0-255 ms) (u32).
+ *     Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ *     If the attribute is absent ("no preference"), the driver or firmware
+ *     can choose a suitable value.
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS: (Optional) number of
+ *     Availability Windows (AWs) to schedule for non-trigger-based ranging.
+ *     Each AW may contain multiple FTM exchanges as configured by
+ *     %NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST. Only valid if
+ *     %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ *     If the attribute is absent ("no preference"), the driver or firmware
+ *     can choose a suitable value.
  *
  * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
  * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
@@ -8295,6 +8348,11 @@ enum nl80211_peer_measurement_ftm_req {
        NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK,
        NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
        NL80211_PMSR_FTM_REQ_ATTR_RSTA,
+       NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS,
+       NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS,
+       NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME,
+       NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION,
+       NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS,
 
        /* keep last */
        NUM_NL80211_PMSR_FTM_REQ_ATTR,
index 0a87f4f81cd2b2dac107305cedf42fed6a702933..61ecf8fceb6af4c004708a2d2f51881903a1612e 100644 (file)
@@ -476,6 +476,15 @@ nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
        [NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK] = { .type = NLA_FLAG },
        [NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR] = { .type = NLA_U8 },
        [NL80211_PMSR_FTM_REQ_ATTR_RSTA] = { .type = NLA_FLAG },
+       [NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS] = {
+               .type = NLA_U32
+       },
+       [NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS] = {
+               .type = NLA_U32
+       },
+       [NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION] = NLA_POLICY_MAX(NLA_U32, 255),
+       [NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS] = { .type = NLA_U32 },
 };
 
 static const struct nla_policy
@@ -498,6 +507,8 @@ nl80211_pmsr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
        [NL80211_PMSR_PEER_ATTR_REQ] =
                NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy),
        [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },
+       [NL80211_PMSR_PEER_ATTR_REQ_TYPE] =
+               NLA_POLICY_MAX(NLA_U32, NL80211_PMSR_FTM_REQ_TYPE_MAX),
 };
 
 static const struct nla_policy
index c21f693fac297336929f6c44682060573ca0a985..951ba0b96da26b946118573a160b9cc6dc5cdca2 100644 (file)
@@ -91,11 +91,10 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
                        nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]);
 
        if (capa->ftm.max_ftms_per_burst &&
-           (out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst ||
-            out->ftm.ftms_per_burst == 0)) {
+           out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst) {
                NL_SET_ERR_MSG_ATTR(info->extack,
                                    tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
-                                   "FTM: FTMs per burst must be set lower than the device limit but non-zero");
+                                   "FTM: FTMs per burst must be set lower than the device limit");
                return -EINVAL;
        }
 
@@ -128,6 +127,14 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
                return -EINVAL;
        }
 
+       if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
+           out->ftm.trigger_based) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   ftmreq,
+                                   "FTM: TB ranging is not supported for PD request type");
+               return -EINVAL;
+       }
+
        out->ftm.non_trigger_based =
                !!tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED];
        if (out->ftm.non_trigger_based && !capa->ftm.non_trigger_based) {
@@ -143,6 +150,14 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
                return -EINVAL;
        }
 
+       if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
+           out->ftm.non_trigger_based && out->ftm.ftms_per_burst > 4) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
+                                   "FTM: FTMs per burst must not exceed 4 for PD NTB ranging");
+               return -ERANGE;
+       }
+
        if (out->ftm.ftms_per_burst > 31 && !out->ftm.non_trigger_based &&
            !out->ftm.trigger_based) {
                NL_SET_ERR_MSG_ATTR(info->extack,
@@ -222,6 +237,33 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
                return -EINVAL;
        }
 
+       if (out->ftm.non_trigger_based) {
+               if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
+                   !tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]) {
+                       NL_SET_ERR_MSG(info->extack,
+                                      "FTM: nominal time is required for PD NTB ranging");
+                       return -EINVAL;
+               }
+               out->ftm.nominal_time =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]);
+
+               if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS])
+                       out->ftm.min_time_between_measurements =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]);
+
+               if (tb[NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS])
+                       out->ftm.max_time_between_measurements =
+                       nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]);
+
+               if (tb[NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION])
+                       out->ftm.availability_window =
+                               nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION]);
+
+               if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS])
+                       out->ftm.num_measurements =
+                               nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS]);
+       }
+
        return 0;
 }
 
@@ -249,6 +291,19 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
 
        memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN);
 
+       if (tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE])
+               out->ftm.request_type =
+                       nla_get_u32(tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE]);
+       else
+               out->ftm.request_type = NL80211_PMSR_FTM_REQ_TYPE_INFRA;
+
+       if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
+           !rdev->wiphy.pmsr_capa->ftm.type.pd_support) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE],
+                                   "FTM: PD request type not supported by device");
+               return -EINVAL;
+       }
        /* reuse info->attrs */
        memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1));
        err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX,