]> exis.tech > repos - linux.git/blobdiff - net/wireless/pmsr.c
Merge tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groec...
[linux.git] / net / wireless / pmsr.c
index d6cd0de64d1f844f683ab39036501893cab9ee61..34c3625f7fd5e51d34bb4c6ad6f9b0651ad231c6 100644 (file)
@@ -17,11 +17,19 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
        u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */
 
        /* validate existing data */
-       if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) {
+       if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_INFRA &&
+           !(capa->ftm.bandwidths & BIT(out->chandef.width))) {
                NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth");
                return -EINVAL;
        }
 
+       if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
+           !(capa->ftm.pd_bandwidths & BIT(out->chandef.width))) {
+               NL_SET_ERR_MSG(info->extack,
+                              "FTM: unsupported bandwidth for PD request");
+               return -EINVAL;
+       }
+
        /* no validation needed - was already done via nested policy */
        nla_parse_nested_deprecated(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq,
                                    NULL, NULL);
@@ -44,13 +52,22 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
                }
        }
 
-       if (!(capa->ftm.preambles & BIT(preamble))) {
+       if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_INFRA &&
+           !(capa->ftm.preambles & BIT(preamble))) {
                NL_SET_ERR_MSG_ATTR(info->extack,
                                    tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
                                    "FTM: invalid preamble");
                return -EINVAL;
        }
 
+       if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
+           !(capa->ftm.pd_preambles & BIT(preamble))) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
+                                   "FTM: invalid preamble for PD request");
+               return -EINVAL;
+       }
+
        out->ftm.preamble = preamble;
 
        out->ftm.burst_period = 0;
@@ -91,11 +108,10 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
                        nla_get_u8(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;
        }
 
@@ -109,6 +125,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
                NL_SET_ERR_MSG_ATTR(info->extack,
                                    tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI],
                                    "FTM: LCI request not supported");
+               return -EOPNOTSUPP;
        }
 
        out->ftm.request_civicloc =
@@ -117,6 +134,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
                NL_SET_ERR_MSG_ATTR(info->extack,
                                    tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC],
                            "FTM: civic location request not supported");
+               return -EOPNOTSUPP;
        }
 
        out->ftm.trigger_based =
@@ -128,6 +146,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 +169,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,
@@ -188,20 +222,86 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
        }
 
        out->ftm.rsta = !!tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA];
-       if (out->ftm.rsta && !capa->ftm.support_rsta) {
+       if (out->ftm.rsta && out->ftm.non_trigger_based &&
+           !capa->ftm.rsta.support_ntb) {
                NL_SET_ERR_MSG_ATTR(info->extack,
                                    tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
-                                   "FTM: RSTA not supported by device");
+                                   "FTM: NTB RSTA not supported by device");
                return -EOPNOTSUPP;
        }
 
-       if (out->ftm.rsta && !out->ftm.lmr_feedback) {
+       if (out->ftm.rsta && out->ftm.trigger_based &&
+           !capa->ftm.rsta.support_tb) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
+                                   "FTM: TB RSTA not supported by device");
+               return -EOPNOTSUPP;
+       }
+
+       if (out->ftm.rsta && !out->ftm.non_trigger_based &&
+           !out->ftm.trigger_based &&
+           !capa->ftm.rsta.support_edca) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
+                                   "FTM: EDCA RSTA not supported by device");
+               return -EOPNOTSUPP;
+       }
+
+       if (out->ftm.rsta &&
+           (out->ftm.non_trigger_based || out->ftm.trigger_based) &&
+           !out->ftm.lmr_feedback) {
                NL_SET_ERR_MSG_ATTR(info->extack,
                                    tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
                                    "FTM: RSTA set without LMR feedback");
                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]);
+       }
+
+       if (tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS])
+               out->ftm.ingress_distance =
+                       nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS]);
+
+       if (tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS])
+               out->ftm.egress_distance =
+                       nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS]);
+
+       out->ftm.pd_suppress_range_results =
+               nla_get_flag(tb[NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS]);
+
+       if (out->ftm.request_type != NL80211_PMSR_FTM_REQ_TYPE_PD &&
+           out->ftm.pd_suppress_range_results) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   tb[NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS],
+                                   "FTM: suppress range result flag only valid for PD requests");
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -212,6 +312,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
 {
        struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
        struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
+       bool have_measurement_type = false;
        struct nlattr *treq;
        int err, rem;
 
@@ -229,6 +330,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,
@@ -238,7 +352,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
                return err;
 
        err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
-                                   &out->chandef);
+                                   &out->chandef, false);
        if (err)
                return err;
 
@@ -265,6 +379,14 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
        }
 
        nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) {
+               if (have_measurement_type) {
+                       NL_SET_ERR_MSG_ATTR(info->extack, treq,
+                                           "multiple measurement types in request data");
+                       return -EINVAL;
+               }
+
+               have_measurement_type = true;
+
                switch (nla_type(treq)) {
                case NL80211_PMSR_TYPE_FTM:
                        err = pmsr_parse_ftm(rdev, treq, out, info);
@@ -274,10 +396,16 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
                                            "unsupported measurement type");
                        err = -EINVAL;
                }
+               if (err)
+                       return err;
        }
 
-       if (err)
-               return err;
+       if (!have_measurement_type) {
+               NL_SET_ERR_MSG_ATTR(info->extack,
+                                   req[NL80211_PMSR_REQ_ATTR_DATA],
+                                   "missing measurement type in request data");
+               return -EINVAL;
+       }
 
        return 0;
 }
@@ -286,12 +414,15 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS];
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       int count, rem, err, idx, peer_count;
        struct wireless_dev *wdev = info->user_ptr[1];
+       const struct cfg80211_pmsr_capabilities *capa;
        struct cfg80211_pmsr_request *req;
        struct nlattr *peers, *peer;
-       int count, rem, err, idx;
 
-       if (!rdev->wiphy.pmsr_capa)
+       capa = rdev->wiphy.pmsr_capa;
+
+       if (!capa)
                return -EOPNOTSUPP;
 
        if (!reqattr)
@@ -306,13 +437,18 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
        nla_for_each_nested(peer, peers, rem) {
                count++;
 
-               if (count > rdev->wiphy.pmsr_capa->max_peers) {
+               if (count > capa->max_peers) {
                        NL_SET_ERR_MSG_ATTR(info->extack, peer,
                                            "Too many peers used");
                        return -EINVAL;
                }
        }
 
+       if (!count) {
+               NL_SET_ERR_MSG_ATTR(info->extack, peers, "No peers specified");
+               return -EINVAL;
+       }
+
        req = kzalloc_flex(*req, peers, count);
        if (!req)
                return -ENOMEM;
@@ -322,7 +458,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
                req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]);
 
        if (info->attrs[NL80211_ATTR_MAC]) {
-               if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) {
+               if (!capa->randomize_mac_addr) {
                        NL_SET_ERR_MSG_ATTR(info->extack,
                                            info->attrs[NL80211_ATTR_MAC],
                                            "device cannot randomize MAC address");
@@ -347,6 +483,41 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
                        goto out_err;
                idx++;
        }
+
+       /* Validate per-role peer limits if advertised */
+       if (capa->ftm.ista.max_peers) {
+               peer_count = 0;
+
+               for (idx = 0; idx < req->n_peers; idx++) {
+                       if (!req->peers[idx].ftm.rsta) {
+                               peer_count++;
+
+                               if (peer_count > capa->ftm.ista.max_peers) {
+                                       NL_SET_ERR_MSG(info->extack,
+                                                      "Too many ISTA peers for device limit");
+                                       err = -EINVAL;
+                                       goto out_err;
+                               }
+                       }
+               }
+       }
+
+       if (capa->ftm.rsta.max_peers) {
+               peer_count = 0;
+
+               for (idx = 0; idx < req->n_peers; idx++) {
+                       if (req->peers[idx].ftm.rsta) {
+                               peer_count++;
+
+                               if (peer_count > capa->ftm.rsta.max_peers) {
+                                       NL_SET_ERR_MSG(info->extack,
+                                                      "Too many RSTA peers for device limit");
+                                       err = -EINVAL;
+                                       goto out_err;
+                               }
+                       }
+               }
+       }
        req->cookie = cfg80211_assign_cookie(rdev);
        req->nl_portid = info->snd_portid;
 
@@ -487,6 +658,21 @@ static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg,
        PUTOPT_U64(DIST_AVG, dist_avg);
        PUTOPT_U64(DIST_VARIANCE, dist_variance);
        PUTOPT_U64(DIST_SPREAD, dist_spread);
+       PUTOPT(u32, TX_LTF_REPETITION_COUNT, tx_ltf_repetition_count);
+       PUTOPT(u32, RX_LTF_REPETITION_COUNT, rx_ltf_repetition_count);
+       PUTOPT(u32, MAX_TIME_BETWEEN_MEASUREMENTS,
+              max_time_between_measurements);
+       PUTOPT(u32, MIN_TIME_BETWEEN_MEASUREMENTS,
+              min_time_between_measurements);
+       PUTOPT(u8, NUM_TX_SPATIAL_STREAMS, num_tx_spatial_streams);
+       PUTOPT(u8, NUM_RX_SPATIAL_STREAMS, num_rx_spatial_streams);
+       PUTOPT(u32, NOMINAL_TIME, nominal_time);
+       PUTOPT(u8, AVAILABILITY_WINDOW, availability_window);
+       PUTOPT(u32, CHANNEL_WIDTH, chan_width);
+       PUTOPT(u32, PREAMBLE, preamble);
+       if (res->ftm.is_delayed_lmr &&
+           nla_put_flag(msg, NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR))
+               goto error;
        if (res->ftm.lci && res->ftm.lci_len &&
            nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI,
                    res->ftm.lci_len, res->ftm.lci))
@@ -643,13 +829,11 @@ static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
        }
 }
 
-void cfg80211_pmsr_free_wk(struct work_struct *work)
+void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work)
 {
        struct wireless_dev *wdev = container_of(work, struct wireless_dev,
                                                 pmsr_free_wk);
 
-       guard(wiphy)(wdev->wiphy);
-
        cfg80211_pmsr_process_abort(wdev);
 }
 
@@ -665,7 +849,7 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
        }
        spin_unlock_bh(&wdev->pmsr_lock);
 
-       cancel_work_sync(&wdev->pmsr_free_wk);
+       wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk);
        if (found)
                cfg80211_pmsr_process_abort(wdev);
 
@@ -680,7 +864,7 @@ void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
        list_for_each_entry(req, &wdev->pmsr_list, list) {
                if (req->nl_portid == portid) {
                        req->nl_portid = 0;
-                       schedule_work(&wdev->pmsr_free_wk);
+                       wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk);
                }
        }
        spin_unlock_bh(&wdev->pmsr_lock);